寿司(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 }

 



 

posted @ 2019-07-26 06:22  ATHOSD  阅读(117)  评论(2编辑  收藏  举报