Codeforces Round #376 Div.2
A:题意:给定一个由字母表组成的首尾相接的环,环上有一个指针,最初指向字母a,每次可以顺时针或逆时针旋转一格,例如a顺时针转到z,逆时针转到b,然后问转出给定字符串最少需要转多少次。
思路:模拟。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 using namespace std; 7 8 char s[1000]; 9 10 int main(){ 11 scanf("%s",s+1); 12 int now=0,ans=0; 13 for (int i=1;i<=strlen(s+1);i++){ 14 int t=s[i]-'a'; 15 ans+=min(abs(t-now),26-abs(t-now)); 16 now=t; 17 } 18 printf("%d\n",ans); 19 return 0; 20 }
B:题意:一共有n天,然后每天需要买恰好ai个披萨,对于每天披萨的购买只有两种方式,要么一次买两个披萨,要么一次为今天买一个披萨同时为明天也买一个披萨,不能有其余任何的购买方式,且这两种购买方式可以使用无限次数,问是否每天都能恰好买到ai个。
思路:显然对于第i天的披萨,如果ai是偶数,则直接用第一种购买方式,因为这显然合法且对后面不产生影响,若ai是奇数,则应该先用第一种购买方式买到只剩一个,然后再用第二种购买方式,然后ai+1--,如果存在一个ai<0(包括an+1),则说明不合法,否则合法。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 using namespace std; 7 #define maxn 200005 8 9 int n; 10 int a[maxn]; 11 12 int main(){ 13 scanf("%d",&n); 14 for (int i=1;i<=n;i++) scanf("%d",&a[i]); 15 for (int i=1;i<=n;i++){ 16 if (a[i]<0){puts("NO");return 0;} 17 if (a[i]&1) a[i]=0,a[i+1]--; 18 } 19 if (a[n+1]<0) puts("NO");else puts("YES"); 20 return 0; 21 }
C:题意:一共n只袜子,m天,k种颜色,然后给出每一只袜子的初始颜色,然后m天给定每一天穿哪双袜子,然后要给最少的袜子染色使得每一天穿的袜子颜色相同。
思路:对于每一天穿的一双袜子看成两个点,然后连边,那么显然对于一条边连接的两个点颜色是要相同的,也就是说属于一个连通块内的所有点的颜色是要相同的,于是对于每一个连通块找出出现次数最多的颜色,然后用size减去它就是该连通块对答案的贡献。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 using namespace std; 7 #define maxn 200005 8 9 int n,m,k,tot,tmp,top,size,ans; 10 int col[maxn],now[maxn],pre[maxn*10],son[maxn*10],cnt[maxn],stack[maxn]; 11 bool vis[maxn]; 12 13 void add(int a,int b){ 14 son[++tot]=b; 15 pre[tot]=now[a]; 16 now[a]=tot; 17 } 18 19 void link(int a,int b){ 20 add(a,b),add(b,a); 21 } 22 23 void dfs(int x){ 24 size++; 25 if (!cnt[col[x]]) stack[++top]=col[x]; 26 cnt[col[x]]++,tmp=max(tmp,cnt[col[x]]); 27 for (int p=now[x];p;p=pre[p]) 28 if (!vis[son[p]]) vis[son[p]]=1,dfs(son[p]); 29 } 30 31 int main(){ 32 scanf("%d%d%d",&n,&m,&k); 33 for (int i=1;i<=n;i++) scanf("%d",&col[i]); 34 for (int i=1,a,b;i<=m;i++) scanf("%d%d",&a,&b),link(a,b); 35 for (int i=1;i<=n;i++) 36 if (!vis[i]){ 37 vis[i]=1; 38 size=0,tmp=0,dfs(i),ans+=size-tmp; 39 for (int j=1;j<=top;j++) cnt[stack[j]]=0; 40 top=0; 41 } 42 printf("%d\n",ans); 43 return 0; 44 }
D:题意:一共n个字符串,c种颜色,然后给出每一个字符串的长度(记为li)再给出每一个字符串(注意n<=500000,li<=500000,sigma(li)<=1000000),每一个字符串之间用空格隔开,然后你可以顺时针(也仅可以顺时针)置换,即对于任意一个字符串的任意一个数字,如果顺时针置换一次,将由1变成2,2变成3...c变成1,并且如果置换一次,所有的数字均会按照上述规则进行变换,问转几次恰好能使所有字符串按照字典序排列。、
思路:显然转c次就是一个循环,因此答案一定是[0,c-1]的一个数,或是0,然后因为数据范围的问题是不可能全部存下来然后离线处理的,因此每读入一个字符串就要在线处理,记录前一个字符串(记为pre[])和后一个字符串(记为now[]),然后一定要使后一个字符串比前一个字符串的字典序大,即找到第一个不同字符,然后如果pre[i]>now[i],那么显然一定要让pre[i]转过c,而使now[i]不能转过c,于是此处的转动的范围即[c-pre[i]+1,c-now[i]],否则如果pre[i]<now[i],那么显然要么不让now[i]转过c,要么pre[i]也转过c,那么此处转动的范围即[0,c-now[i]]∪[c-pre[i]+1,c-1],然后每一次都属于范围之内的元素即答案,用线段树维护,或差分一下均可。然后还要注意一个细节,就是如果pre[i]和now[i]前面所有的元素都相同且pre[i]的长度要大于now[i]的长度显然就无解。
PS:好气啊,比赛时这道题写了好久,结果还因为打错一个变量名GG了,然后后面两道大水题。。。。。。。。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cmath> 5 #include<cstring> 6 using namespace std; 7 #define maxn 500005 8 9 int n,c,ans; 10 int now[maxn],pre[maxn]; 11 bool first=1; 12 13 struct segment_tree{ 14 struct treenode{ 15 bool can; 16 }tree[maxn*4]; 17 void build(int p,int l,int r){ 18 tree[p].can=1; 19 if (l==r) return; 20 int mid=(l+r)>>1; 21 build(p<<1,l,mid); 22 build(p<<1|1,mid+1,r); 23 } 24 void change(int p,int l,int r,int x,int y){ 25 if (!tree[p].can) return; 26 if (x<=l && r<=y){ 27 tree[p].can=0; 28 return; 29 } 30 int mid=(l+r)>>1; 31 if (x<=mid) change(p<<1,l,mid,x,y); 32 if (y>mid) change(p<<1|1,mid+1,r,x,y); 33 } 34 void query(int p,int l,int r){ 35 if (!tree[p].can) return; 36 if (l==r){if (first) ans=l,first=0;return;} 37 int mid=(l+r)>>1; 38 query(p<<1,l,mid),query(p<<1|1,mid+1,r); 39 } 40 }T; 41 42 int main(){ 43 scanf("%d%d",&n,&c);int prelen=0; 44 T.build(1,0,c-1); 45 for (int i=1;i<=n;i++){ 46 int nowlen=0; 47 if (i==1){ 48 scanf("%d",&nowlen),prelen=nowlen; 49 for (int j=1;j<=nowlen;j++) scanf("%d",&now[j]),pre[j]=now[j]; 50 continue; 51 } 52 scanf("%d",&nowlen); 53 for (int j=1;j<=nowlen;j++) scanf("%d",&now[j]); 54 for (int j=1;j<=prelen;j++){ 55 if (j>nowlen/*WA=>prelen*/){T.change(1,0,c-1,0,c-1);break;} 56 if (pre[j]!=now[j]){ 57 if (pre[j]>now[j]) T.change(1,0,c-1,0,c-pre[j]),T.change(1,0,c-1,c-now[j]+1,c-1); 58 else T.change(1,0,c-1,c-now[j]+1,c-pre[j]); 59 break; 60 } 61 } 62 prelen=nowlen; 63 for (int j=1;j<=nowlen;j++) pre[j]=now[j]; 64 } 65 ans=-1; 66 T.query(1,0,c-1); 67 printf("%d\n",ans); 68 return 0; 69 }
E:题意:一共n张贴纸,然后每一张上写有一个数字,每次两人从左边选出至少两个数字(至多可以选完),然后将这些贴纸撕去,再贴一张新的贴纸代表所选的数字之和,然后收益就是所选的所有数字之和,问两人均采取最好策略的情况下,先手收益最多能比后手多多少(如果比后手收益少x则输出-x),数字可能为负。
思路:用f[i]表示从i开始玩(也就是前i个已经被缩为一张了)先手与后手的最大差距,然后显然f[i]一定是由某个j转移过来的,因为当前先手一定是将i到j这一段缩成一个数字,然后f[i]=max{sum[j]-f[j]},令f[i]表示真正的先手(后手也是一样,因为都是最优策略),此处的f[j]虽然表示的先手实际上是真正的后手,然后f[j]=j到n中真正后手的收益-j到n中真正先手的收益,因而sum[j]-f[j]=sum[i]+j到n中真正先手的收益-j到n中真正后手的收益,于是这个转移方程就很显然了,然后直接用ans表示sum[j]-f[j]的最大值,然后用ans=max{sum[i]-ans,ans}转移即可。时间复杂度O(n)。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 using namespace std; 7 #define maxn 200005 8 9 int n; 10 int a[maxn]; 11 long long sum[maxn]; 12 13 int main(){ 14 scanf("%d",&n); 15 for (int i=1;i<=n;i++) scanf("%d",&a[i]); 16 for (int i=1;i<=n;i++) sum[i]=sum[i-1]+a[i];long long ans=sum[n]; 17 for (int i=n-1;i>1;i--) ans=max(ans,sum[i]-ans); 18 printf("%lld\n",ans); 19 return 0; 20 }
F:题意:一共n个数,选定一个数使得其余所有数均为该数的倍数,然后该数大小不能变,其余数的大小均减小以满足是选定数的倍数,问减小后的序列之和的最大值是多少。
思路:枚举选定哪一个数i,然后显然可以分块,记录i*j到(i+1)*j-1中一共多少个数,这些数显然一定会变成i*j,然后统计答案即可,时间复杂度O(n*log(n)),最坏时间复杂度O(n*sqrt(n))。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 using namespace std; 7 #define maxn 500005 8 9 int n; 10 int a[maxn],f[maxn]; 11 long long ans; 12 13 int main(){ 14 scanf("%d",&n); 15 for (int i=1,x;i<=n;i++) scanf("%d",&x),a[x]++; 16 for (int i=200000;i;i--) f[i]=f[i+1]+a[i]; 17 for (int i=1;i<=200000;i++) 18 if (a[i]){ 19 long long sum=0; 20 for (int j=i;j<=200000;j+=i) sum+=1ll*(f[j]-f[i+j])*j; 21 ans=max(ans,sum); 22 } 23 printf("%lld\n",ans); 24 return 0; 25 }