zoukankan      html  css  js  c++  java
  • UOJ Rounds

    UOJ Test Round #1

      T1:数字比大小的本质是按(长度,字典序)比大小。

      T2首先发现单调性,二分答案,用堆模拟,$O(nlog^2 n)$。

        第二个log已经没有什么可优化的了,但是第一个可以做到线性。

        我们先将特殊题的p就当作是-1跑一边,设这个题的出现时间是tx,完成所需时间为sx,记录下每个题在[tx,T]上的出现时间。把所有题按优先级排序,可以发现如果找到了前i个满足出现时间之和为sx,那么这些时间区间正好可以被特殊题区间覆盖,找到这个i就确定了优先级,最后再模拟一遍即可。

        有个很容易忽视的问题,就是一定要保证得到的i是能让px尽量小的,这样才可以在总时间相同的情况下使特殊题最后做完,从而保证特殊题的完成时间就是T而不是T之前。

        当然按上述做法把优先级从低到高排序没有任何问题,但是如果从高到低排序最后相减就会出错了。

        为了这个问题调了一整个下午。

     1 #include<map>
     2 #include<cstdio>
     3 #include<queue>
     4 #include<algorithm>
     5 #define rep(i,l,r) for (int i=l; i<=r; i++)
     6 typedef long long ll;
     7 using namespace std;
     8 
     9 const int N=300010;
    10 const ll inf=1000000000000000000ll;
    11 int n,pp,pos,s[N],sm[N];
    12 ll mx,S,T,Ed[N];
    13 struct P{ int t,s,p,id; }a[N];
    14 priority_queue<P>Q;
    15 map<ll,bool>mp;
    16 
    17 bool operator <(const P &a,const P &b){ return a.p<b.p; }
    18 bool cmp(const P &a,const P &b){ return (a.t==b.t) ? a.p>b.p : a.t<b.t; }
    19 bool cmp1(int x,int y){ return a[x].p<a[y].p; }
    20 void F(int id,ll l,ll r){
    21     l=max(l,S); r=min(r,T);
    22     if (r>l) sm[id]+=r-l;
    23 }
    24 
    25 int main(){
    26     scanf("%d",&n); mp[0]=1;
    27     rep(i,1,n){
    28         scanf("%d%d%d",&a[i].t,&a[i].s,&a[i].p);
    29         if (a[i].p==-1) S=a[i].t,pp=a[i].s;
    30         mp[a[i].p]=1; a[i].id=i;
    31     }
    32     scanf("%lld",&T);
    33     sort(a+1,a+n+1,cmp);
    34     rep(i,1,n) s[i]=i;
    35     sort(s+1,s+n+1,cmp1);
    36     for (int i=1,j=1; i<=n; i=j){
    37         for (; a[j].t==a[i].t; j++) Q.push(a[j]);
    38         ll tim=a[i].t,ed=a[j].t;
    39         if (ed==0) ed=inf;
    40         while (!Q.empty()){
    41             P x=Q.top(); Q.pop();
    42             if (tim+x.s<=ed){
    43                 F(x.id,tim,tim+x.s); tim+=x.s; mx=max(mx,tim);
    44                 if (tim==ed) break;
    45             }else { x.s-=ed-tim; F(x.id,tim,ed); mx=max(mx,ed); Q.push(x); break; }
    46         }
    47     }
    48     ll res=0,ans=1;
    49     while (mp.count(ans)) ans++;
    50     rep(i,1,n){
    51         res+=sm[a[s[i]].id];
    52         if (res==pp){
    53             //printf("%lld %lld %d %lld
    ",S,res,pp,T);
    54             //printf("%d %d %d
    ",a[s[i]].p,a[s[i+1]].p,a[s[i+2]].p);
    55             ans=(~a[s[i]].p) ? a[s[i]].p : a[s[i-1]].p;
    56             while (mp.count(ans)) ans++;
    57             break;
    58         }
    59     }
    60     rep(i,1,n) if (a[i].p==-1) { a[i].p=ans; break; }
    61     while (!Q.empty()) Q.pop();
    62     for (int i=1,j=1; i<=n; i=j){
    63         for (; a[j].t==a[i].t; j++) Q.push(a[j]);
    64         ll tim=a[i].t,ed=a[j].t;
    65         if (ed==0) ed=inf;
    66         while (!Q.empty()){
    67             P x=Q.top(); Q.pop();
    68             if (tim+x.s<=ed) Ed[x.id]=tim+x.s,tim+=x.s;
    69                 else { x.s-=ed-tim; Q.push(x); tim=ed; break; }
    70         }
    71     }
    72     printf("%lld
    ",ans);
    73     rep(i,1,n) printf("%lld ",Ed[i]);
    74     return 0;
    75 }
    UTR#2 T2

      T3:毒瘤类中心。

    UOJ Easy Round #1

      T1:均值不等式或极大极小值定理直接出解。注意:

    精度问题

    有人可能会写:

    ans_min = (long long)sqrt((double)g * l);

    这样会被卡精度,因为double大概只有15位10进制有效数字。只能得到60分。

    解决方法是:

    ans_min = (long long)sqrt(l / g) * g;

    当然有人可能直接long double保平安了……

      T2:Trie树上放些指针就好了。

      T3:实际上就是一个可持久化并查集,然后整个状态空间形成了一棵树,用树上倍增即可。

        考虑更简便的做法。

        首先如果只有Add操作,MST的形态是不会变的。

        如果没有Return操作。Delete直接用可撤销并查集即可(不要路径压缩,因为代价是均摊而不是严格)。

        有了Return之后的难点就在于前两种操作可以保证的均摊复杂度分析失效,我们就这一点处理:

          考虑一个Return操作,如果前面是Add,那这就是一个Delete 1。

          考虑一个Delete,可不可以做到,如果后面的操作不是Return,我们就“真删”,否则“假删”呢?

          思考如何“假删”,就是回答前k'条边形成的生成树的权值和,这个用维护一个数组即可。

    UOJ Round #1

      T1:最小化sum{ a[i]%x+a[i]/x },变形成 sum{ a[i]-a[i]/x *(x-1) }。

        我们枚举x,问题就变成了对每个x求sum{ a[i]/x },这个设为z[x]。

        从a[]的角度思考,考虑每个a[i]对数组z的影响,由于a[i]/x的值只有$O(sqrt{a_i})$个,总复杂度就可以做到根号级别了。

        从x的角度思考,枚举a[i]/x的所有值t,查询满足$txleq a_i < (t+1)x$的i的个数,然后给z[x]加上t和个数的积,查询开个桶用前缀和完成。

        这样根据调和级数就可以做到$O(Xlog X)$了,感觉很巧妙。

        还有一种角度,就是每次加一个后缀和:http://uoj.ac/submission/241036

        启示:很多时候从a%b=a-(a/b)*b考虑很有用。以及根号优化和调和级数很有用。

      T2:又是一道好题,很像[PKUWC2018]随机算法。

        这两道题的共同点在于都是要求找到一个排列使解最优,并求出最优排列的个数,以及排列的每个元素是否会其作用根据排列而改变。

        首先发现a%b在b<=a时一定会其作用,在b>a时一定不会起作用。

        首先考虑如何求出最优解,由于上面的结论,我们可以将a[]排序然后DP,这样实际上枚举的是排列的哪些元素起了作用,显然这样可以保证包含了最优解。

        然后难点在于求出最优解的个数。

        我们用f[x]表示到当前为止值为x,只考虑a[i]<x的情况,的方案数。(显然a[i]<x的a[i]是不可能在之前出现的),枚举a[i],可以从f[x%a[i]]转移过来。

        这样,转移量就是大于a[i]而不大于x的那些元素的位置,乘上组合数即可。

        还有一种更为精妙的方法:http://uoj.ac/submission/243023

        延续起先的思路,将a[]排序,f[i][x]表示只考虑前i个中会起作用的元素并作用于原数之后,这个数会变成x的方案数,直接转移。

      T3:毒瘤仙人掌。

    UOJ Round #2

      T1:构造题,不要求最优,只要m<=n即可。

        构造题还有一个套路,你可以人为固定最后变成什么样子,这样就好做多了。

        比如这题,我们规定最后是(((...((()))...)))这样的,前n个全是左括号。

        这个怎么实现呢,从左往右扫,扫到一个右括号时,找到它右边第一个左括号,然后把这一段翻转(由于这两个中间一定全是右括号,所以实际上相当于只交换了这两位)。

        拿一个指针指向要找的左括号,显然指针单调移动,故总复杂度为线性。

      T2:我不行啊。

      T3:一道非常好的题目,就是很难写。

        先总结一下主定理(Master Theorem):

          https://blog.csdn.net/lanchunhui/article/details/52451362

          https://www.cnblogs.com/SBSOI/p/5640663.html

          几个表示法:o小于,Θ等于,O小于等于,Ω大于等于。

          主要就是:如果f(n)是$n^{log_{b}a}$的低阶,则结果就是$Θ(n^{log_{b}a})$,如果同阶,则为$Θ(n^{log_{b}a}log n)$,如果更高阶,在一定情况下就是$Θ(f(n))$。

        先看随机树部分:满足树高期望为$O(log n)$,所以每次开一个桶,统计的复杂度是子树高度的平方,总复杂度为$O(n log^2 n)$

        然后是链的部分:根据这个式子可以方便求出不同链之间的答案(实际上记录一个后缀和也行):$(x_1+cdots+x_k)^2=x_1^2+cdots + x_k^2 +2sum_{1leq i < jleq k}x_i x_j$

        然后是正解一:点分治。

          首先有一个巧妙的转化方式:对于与gcd有关的题目,可以先作莫比乌斯变换方便统计,最后再莫比乌斯反演回去。这部分的复杂度都是$O(n log n)$的。
          找到树的中心c,考虑点对(u,v),如果都在c的子树中那么可以直接统计。

          对于u在c子树内,v在c的祖先的其它子树内的情况,我们可以这样做:

            先找到c一直到根的所有祖先,然后求出它们的其它子树的深度数组,然后和c的子树合并计数。

            这样就有一个问题,每次可能会询问c的子树内的所有距离c为d的倍数的点的个数。我们发现对于相同的d,不同的序列只会有d个。那么我们可以在$dleq sqrt{H}$时用一个数组记录答案,大于时直接统计,因为单次询问复杂度已经不会超过$O(sqrt{H})$了。

          根据主定理,每层处理复杂度已经超过了$O(n^{log_b a})$,所以总复杂度就是$f(n)=O(nsqrt{n})$。

        正解二:启发式合并。

          同样是分块,对于$dleq sqrt{n}$的部分直接统计,大于的部分用vector记录答案并启发式合并,最后反演回去即可。

        总结:1.认真分析复杂度。2.Mobius反演的思想。 3.树上启发式合并和线段树合并。 4.分块与记忆化的思想。

     1 #include<cmath>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<algorithm>
     5 #define rep(i,l,r) for (int i=(l),_=(r); i<=_; i++)
     6 #define For(i,x) for (int i=h[x],k; i; i=nxt[i])
     7 typedef long long ll;
     8 using namespace std;
     9 
    10 const int N=200010,M=500,inf=1000000000;
    11 int n,dd,nd,rt,z,sq,S,h[N],f[N],sz[N],d[N],dep[N];
    12 int dp[M][M],ds[N],s1[N],fa[N],cnt[N],to[N<<1],nxt[N<<1];
    13 bool vis[N];
    14 ll ans[N],s2[N];
    15 void add(int u,int v){ to[++nd]=v; nxt[nd]=h[u]; h[u]=nd; }
    16 
    17 void getrt(int x,int fa){
    18     sz[x]=1; f[x]=0;
    19     For(i,x) if ((k=to[i])!=fa && !vis[k])
    20         getrt(k,x),sz[x]+=sz[k],f[x]=max(f[x],sz[k]);
    21     f[x]=max(f[x],S-sz[x]);
    22     if (f[x]<f[rt]) rt=x;
    23 }
    24 
    25 void getdep(int x,int fa){
    26     dep[x]=dep[fa]+1;
    27     if (dd<dep[x]) d[dd=dep[x]]=0;
    28     d[dep[x]]++;
    29     For(i,x) if (!vis[k=to[i]] && k!=fa) getdep(k,x);
    30 }
    31 
    32 ll ask(int x,int y){
    33     ll res=0;
    34     if (x<=sq && dp[x][y]) return dp[x][y];
    35     for (int i=y; i<=z; i+=x) res+=ds[i];//祖先的子树的深度数组(未做变换)
    36     if (x<=sq) dp[x][y]=res;
    37     return res;
    38 }
    39 
    40 void work(int x){
    41     dep[x]=0; z=0;
    42     rep(i,0,sz[x]+1) s1[i]=s2[i]=ds[i]=0;
    43     For(i,x) if (!vis[k=to[i]] && k!=fa[x]){
    44         dd=0; getdep(k,x); z=max(z,dd);
    45         rep(j,1,dd) ds[j]+=d[j];
    46         rep(j,1,dd) for (int l=j+j; l<=dd; l+=j) d[j]+=d[l];//Mobius变换
    47         rep(j,1,dd) s1[j]+=d[j],s2[j]+=1ll*d[j]*d[j];
    48     }
    49     rep(i,1,z) ans[i]+=(1ll*s1[i]*s1[i]-s2[i])>>1;
    50     sq=sqrt(z); memset(dp,0,sizeof(dp[0])*(sq+5));
    51     for (int i=x,y=1; fa[i] && !vis[fa[i]]; i=fa[i],y++)//枚举祖先的其它子树
    52         For(j,fa[i]) if (!vis[k=to[j]] && k!=i && k!=fa[fa[i]]){
    53             dd=0; dep[fa[i]]=0; getdep(k,fa[i]);
    54             rep(l,1,dd) for (int p=l+l; p<=dd; p+=l) d[l]+=d[p];
    55             rep(l,1,dd) ans[l]+=1ll*d[l]*(ask(l,l-y%l)+(y%l==0));//可能重心也是一个合法点
    56         }
    57 }
    58 
    59 void solve(int x){
    60     vis[x]=1;
    61     For(i,x) if (sz[k=to[i]]>sz[x]) sz[k]=S-sz[x];
    62     work(x);
    63     For(i,x) if (!vis[k=to[i]]) S=sz[k],f[rt=0]=inf,getrt(k,x),solve(rt);
    64 }
    65 
    66 int main(){
    67     freopen("gcd.in","r",stdin);
    68     freopen("gcd.out","w",stdout);
    69     scanf("%d",&n);
    70     rep(i,2,n) scanf("%d",&fa[i]),add(fa[i],i),add(i,fa[i]),cnt[dep[i]=dep[fa[i]]+1]++;
    71     for (int i=n-1; i; i--) cnt[i]+=cnt[i+1];
    72     S=n; f[rt=0]=inf; getrt(1,0); solve(rt);
    73     for (int i=n-1; i; i--) for (int j=i+i; j<n; j+=i) ans[i]-=ans[j];//Mobius反演
    74     rep(i,1,n-1) printf("%lld
    ",ans[i]+cnt[i]);//加上u==v的个数
    75     return 0;
    76 }
    解法一
     1 #include<cmath>
     2 #include<cstdio>
     3 #include<vector>
     4 #include<algorithm>
     5 #define rep(i,l,r) for (int i=l; i<=r; i++)
     6 typedef long long ll;
     7 using namespace std;
     8 
     9 const int N=200010;
    10 int n,m,fa[N],dep[N],cnt[N],to[N],g[N],f[N];
    11 ll ans[N];
    12 vector<int>V[N];
    13 
    14 int main(){
    15     freopen("gcd.in","r",stdin);
    16     freopen("gcd.out","w",stdout);
    17     scanf("%d",&n); m=sqrt(n);
    18     rep(i,2,n) scanf("%d",&fa[i]),cnt[dep[i]=dep[fa[i]]+1]++;
    19     rep(i,1,n) to[i]=i;
    20     for (int i=n; i; i--) cnt[i]+=cnt[i+1];
    21     rep(d,1,m){
    22         for (int i=n; i>1; i--){
    23             f[i]++; g[to[i]]+=f[i];
    24             ans[d]+=1ll*f[fa[i]]*g[i]; f[fa[i]]+=g[i];
    25         }
    26         rep(i,1,n) to[i]=fa[to[i]],f[i]=g[i]=0;
    27     }
    28     for (int i=n; i; i--){
    29         V[i].push_back(1);
    30         if (V[i].size()>V[fa[i]].size()) swap(V[i],V[fa[i]]);
    31         int x=V[i].size(),y=V[fa[i]].size();
    32         if (x>m) rep(d,m+1,x){
    33             int a=0,b=0;
    34             for (int j=d; j<=x; j+=d) a+=V[i][x-j];
    35             for (int j=d; j<=y; j+=d) b+=V[fa[i]][y-j];
    36             ans[d]+=1ll*a*b;
    37         }
    38         rep(j,1,x) V[fa[i]][y-j]+=V[i][x-j];
    39     }
    40     for (int i=n; i; i--) for (int j=i+i; j<=n; j+=i) ans[i]-=ans[j];
    41     for (int i=1; i<n; i++) printf("%lld
    ",ans[i]+cnt[i]);
    42     return 0;
    43 }
    解法二

            

  • 相关阅读:
    SVN服务器使用和搭建
    jenkins以.war包安装配置教程
    Django数据库与程序交互
    工作中常用到的linux命令
    js获取元素属性
    Jenkins利用插件持续集成的思路及安装
    web前端面试第一次[javascript函数和方法的区别]
    web前端面试第一次[定时器]
    linux卸载mysql
    mysql启动错误:Starting MySQL... ERROR! The server quit without updating PID file (/data/mysql/mysql.pid).
  • 原文地址:https://www.cnblogs.com/HocRiser/p/9076017.html
Copyright © 2011-2022 走看看