寿司(2019.07.25 T3)
20分算法
dfs我觉得好像比40分算法还难实现。
40分算法
O(n)枚举每个吸引点把所有同颜色的都吸过来。两种吸法取min即可。期望得分20~40分(像我只得了20分。。。)
80分算法
我们可以拆掉一个没用的点(因为是环,我们可以假设它还在那个位置,所以可以删去),之后把环变为序列。
这样之后我们就把问题转化为了给定一个序列,把所有与删去的点相同颜色的点移到左右两边的最小花费。
现在难找的就是在哪里断开使得左边的点向左移,右边的向右移动。
做法一:有个很显然的性质:这个费用函数是单峰的!求单峰可以用三分法但复杂度不够优秀,复杂度O(n*(2*log3(n))),期望得分85分。
做法二:这里有个小引理:假设要向两边移动的点是1,设0的个数是t,那么中间点一定在t/2处。这个引理其实可以通过40分的算法推出来,
40分算法中采用贪心取min的方式相当于是已经固定了每个1要走的方案,而这方案与左右两边的0个数有关,所以此引理是正确的。
有了这个,就可以二分找t/2的位置了。复杂度O(n*log2(n)),虽然比三分要优秀(对于任意n>0,2*(log3(n))>log2(n)),但是还不够,期望得分85分。
100分算法
有了上面的做法二,正解就好想了。
在左右端点不断移动的过程中t/2的位置其实是递增的,直接用一个指针即可。复杂度O(n),期望得分100分。
1 #include<iostream>
2 #include<cstdio>
3 #include<cmath>
4 #include<algorithm>
5 #include<queue>
6 #include<vector>
7 #include<string>
8 #include<cstring>
9 #define int long long
10 #define m(a) memset(a,0,sizeof(a))
11 #define AA cout<<"Alita"<<endl
12 using namespace std;
13 const int N=2e6+100;
14 int ans,n,T,f[N],s[2][N],c[2][N],g[2][N];
15 char ch[N];
16 inline int get(int l,int r,int col)
17 {
18 return c[col][r]-c[col][l-1]-(s[col][r]-s[col][l-1])*s[col^1][l-1];
19 }
20 inline int calc(int l,int m,int r,int col)
21 {
22 return get(l,m,col)+get(m+1,r,col^1);
23 }
24 inline void work()
25 {
26 ans=0x7fffffffffffff;
27 scanf("%s",ch);
28 n=strlen(ch);
29 for(int i=0;i<n;i++)
30 {
31 if(ch[i]=='B') f[i+1]=1;
32 else f[i+1]=0;
33 f[i+1+n]=f[i+1];
34 }
35 int l=0,r=0;
36 for(register int i=1;i<=n*2;i++) //s记录该颜色个数
37 {
38 if(f[i]==1) l++;
39 else r++;
40 s[1][i]=l;
41 s[0][i]=r;
42 }
43 l=0;r=0;
44 for(register int i=1;i<=n*2;i++) //g记录该点答案 c为答案前缀和
45 {
46 l++;r++;
47 if(f[i]==1) g[1][i]=--l,g[0][i]=0;
48 else g[0][i]=--r,g[1][i]=0;
49 c[1][i]=c[1][i-1]+g[1][i];
50 c[0][i]=c[0][i-1]+g[0][i];
51 }
52 int pos=0;
53 for(register int i=1;i<=n;i++)
54 {
55 int L=i+1,R=i+n-1,o=f[i]^1;
56 int t=s[o][R]-s[o][L-1];
57 while(s[o][pos]-s[o][L-1]<t/2) pos++;
58 ans=min(ans,calc(i+1,pos,i+n-1,f[i]));
59 }
60 printf("%lld\n",ans);
61 }
62 signed main()
63 {
64 //freopen("1.in","r",stdin);
65 scanf("%lld",&T);
66 while(T--) work();
67 return 0;
68 }