Codeforces Round #629 (Div. 3)
D. Carousel
题意:给一个n(2e5)个点的环,每个点上有一个数字ai,现在要求你再给每个点一个值bi,使得不存在相邻的两个点他们ai不同,bi相同,问bi最少取几个值。
思路:如果所有ai都相同,bi只取一个即可。如果存在ai不同的情况,则至少取两个bi,如果环是偶数长度,那么可以1212这样赋值,一定符合题意,为2。如果是奇数长度,假如存在相邻点ai相同,我们可以121211212这样赋值,而且就算是最后一个和第一个相同的情况这样用也不会出错,即121212121这样。如果不存在相邻点ai相同,即两两都不相同,第一个点取1后,为了尽可能少用bi后面一定是21212这样跟,到了最后一个只能另取一个3,答案为3。
1 #include<bits/stdc++.h> 2 #define LL long long 3 #define dl double 4 void rd(int &x){ 5 x=0;int f=1;char ch=getchar(); 6 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 7 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 8 } 9 void lrd(LL &x){ 10 x=0;int f=1;char ch=getchar(); 11 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 12 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 13 } 14 const int INF=1e9; 15 const LL LINF=1e18; 16 const int N=2e5+10; 17 using namespace std; 18 int q,n,a[N]; 19 bool check1(){ 20 for(int i=1;i<n;i++)if(a[i] != a[i+1])return 0; 21 return 1; 22 } 23 int main(){ 24 freopen("in.txt","r",stdin); 25 rd(q); 26 while(q--){ 27 rd(n);for(int i=1;i<=n;i++)rd(a[i]); 28 if(check1()){ 29 printf("1\n"); 30 for(int i=1;i<=n;i++)printf("1 "); 31 puts(""); 32 } 33 else { 34 if(n % 2 == 0){ 35 printf("2\n"); 36 for(int i=1;i<=n;i++) 37 if(i & 1)printf("1 "); 38 else printf("2 "); 39 puts(""); 40 } 41 else { 42 if(a[n] == a[1]){ 43 printf("2\n"); 44 for(int i=1;i<n;i++) 45 if(i & 1)printf("1 "); 46 else printf("2 "); 47 printf("1\n"); 48 } 49 else if(a[n] == a[n-1]){ 50 printf("2\n"); 51 for(int i=1;i<n;i++) 52 if(i & 1)printf("1 "); 53 else printf("2 "); 54 printf("2\n"); 55 } 56 else { 57 int id=0; 58 for(int i=1;i<n;i++)if(a[i] == a[i+1]){id=i;break;} 59 if(id){ 60 printf("2\n"); 61 for(int i=1;i<=id;i++) 62 if(i & 1)printf("1 "); 63 else printf("2 "); 64 for(int i=id+1;i<=n;i++) 65 if(i & 1)printf("2 "); 66 else printf("1 "); 67 puts(""); 68 } 69 else { 70 printf("3\n"); 71 for(int i=1;i<n;i++) 72 if(i & 1)printf("1 "); 73 else printf("2 "); 74 printf("3\n"); 75 } 76 } 77 } 78 } 79 } 80 return 0; 81 } 82 /**/
E. Tree Queries
题意:n(2e5)个节点的树,m(2e5)次询问,每次询问给出若干个点,问是否存在一条到根的简单路径使得给出的点在路径上或者距离路径上的某个点距离为1。
思路:可以发现如果我们考虑给出点的父亲,问题就可以被简化为是否能使若干点在一条到根的简单路径上。然后取最深的点,判断其他点是否为它的祖先即可。判断方法很多,我的代码中用了很蠢的倍增方法,甚至还用了完全没有必要的sort。反思中将给出一种线性的方法。
1 #include<bits/stdc++.h> 2 #define LL long long 3 #define dl double 4 void rd(int &x){ 5 x=0;int f=1;char ch=getchar(); 6 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 7 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 8 } 9 void lrd(LL &x){ 10 x=0;int f=1;char ch=getchar(); 11 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 12 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 13 } 14 const int INF=1e9; 15 const LL LINF=1e18; 16 const int N=2e5+10; 17 using namespace std; 18 int n,m; 19 int fi[N],nxt[N<<1],to[N<<1],tot; 20 void link(int x,int y){nxt[++tot]=fi[x];fi[x]=tot;to[tot]=y;} 21 int fa[N],dep[N]; 22 int lc[N][20]; 23 void dfs(int x){ 24 lc[x][0]=fa[x]; 25 for(int i=1;i<20;i++)lc[x][i]=lc[lc[x][i-1]][i-1]; 26 for(int i=fi[x];i;i=nxt[i]){ 27 if(to[i] == fa[x])continue; 28 dep[to[i]]=dep[x]+1; 29 fa[to[i]]=x;dfs(to[i]); 30 } 31 } 32 int a[N],cnt; 33 bool cmp(int x,int y){return dep[x] > dep[y];} 34 bool check(int x,int y){ 35 for(int i=19;i>=0;i--)if(dep[lc[x][i]] >= dep[y])x=lc[x][i]; 36 return x == y; 37 } 38 int main(){ 39 // freopen("in.txt","r",stdin); 40 rd(n);rd(m); 41 for(int i=1;i<n;i++){int x,y;rd(x);rd(y);link(x,y);link(y,x);} 42 dep[1]=1;dfs(1);//这里dep1必须赋初值1,不然会和0的深度混淆导致check过程出现问题 43 for(int i=1;i<=m;i++){ 44 rd(cnt); 45 for(int j=1;j<=cnt;j++){ 46 rd(a[j]); 47 if(a[j] != 1)a[j]=fa[a[j]]; 48 } 49 sort(a+1,a+cnt+1,cmp);//不排序的话下面的做法应该是错误的 50 bool flg=0; 51 for(int j=1;j<cnt;j++) 52 if(!check(a[j],a[j+1]))flg=1; 53 if(flg)printf("NO\n"); 54 else printf("YES\n"); 55 } 56 return 0; 57 } 58 /**/
反思:1.将节点转换为其父亲节点的思想需要掌握。2.线性判断是否为祖先。记录dfn序,进入一个点和离开一个点都记录下来,分别为l_u和r_u,可以发现所有的l_u~r_u都不相交,而且如果v是u的祖先,充要条件为l_v < l_u && r_v > r_u,也就是u的子树都包含在l_u~r_u之间。这种思想也经常用于一些需要对子树操作的题目中。
F. Make k Equal
题意:给n(2e5)个数字,每次操作可以将最大的数字-1或者将最小的数字+1,问操作多少次后可以出现k(2e5)个相同的数字。
思路:先将相同的数字合并为1个。最终结果一定有一些数字是不变的,不妨枚举那个数字是多少。不妨设其为x,我们发现,如果想要让<x的数字变成x,那么必须所有数字先全变成x-1,再变成x,而变为x-1后只需要1的代价就可以变出一个x,所以不可能出现<x的数字变一部分,同时>x的数字变一部分的情况。我们可以分为四种情况,<x的数字足够多,变成x的数字全是<x的,或者不够多,在将所有<x的数字变为x之后,再将一部分>x的数字变为x,这是前两种情况,后两种情况即是<x和>x地位的对调。而这四种情况的答案计算,可以通过记录若干前缀后缀和来O(1)计算,最终复杂度是线性的。当然,排序是必要的,最终复杂度nlog。
1 #include<bits/stdc++.h> 2 #define LL long long 3 #define dl double 4 void rd(int &x){ 5 x=0;int f=1;char ch=getchar(); 6 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 7 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 8 } 9 void lrd(LL &x){ 10 x=0;int f=1;char ch=getchar(); 11 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 12 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 13 } 14 const int INF=1e9; 15 const LL LINF=1e18; 16 const int N=2e5+10; 17 using namespace std; 18 int n,k; 19 int a[N],b[N],cnt[N],now; 20 LL s1[N],s2[N]; 21 int s3[N],s4[N]; 22 int main(){ 23 // freopen("in.txt","r",stdin); 24 rd(n);rd(k); 25 for(int i=1;i<=n;i++)rd(a[i]); 26 sort(a+1,a+n+1); 27 for(int i=1;i<=n;i++){ 28 if(a[i] != a[i-1])b[++now]=a[i]; 29 cnt[now]++; 30 }n=now; 31 for(int i=1;i<=n;i++)s1[i]=s1[i-1]+1ll*cnt[i]*b[i],s3[i]=s3[i-1]+cnt[i]; 32 for(int i=n;i>=1;i--)s2[i]=s2[i+1]+1ll*cnt[i]*b[i],s4[i]=s4[i+1]+cnt[i]; 33 LL mn=1e18; 34 for(int i=1;i<=n;i++){ 35 int u=k-cnt[i]; 36 if(u <= 0){mn=0;break;} 37 if(s3[i-1] >= u)mn=min(mn,1ll*s3[i-1]*(b[i]-1)-s1[i-1]+u); 38 else mn=min(mn,1ll*s3[i-1]*b[i]-s1[i-1]+s2[i+1]-1ll*s4[i+1]*(b[i]+1)+u-s3[i-1]); 39 if(s4[i+1] >= u)mn=min(mn,s2[i+1]-1ll*s4[i+1]*(b[i]+1)+u); 40 else mn=min(mn,s2[i+1]-1ll*s4[i+1]*b[i]+1ll*s3[i-1]*(b[i]-1)-s1[i-1]+u-s4[i+1]); 41 } 42 printf("%lld\n",mn); 43 return 0; 44 } 45 /**/