模拟测试20190725

考了最近最水的一套题,然而并没有能像考得时候YY的那样干上200(数组开小真是对不起了啊)

后两个小时疯狂划水,盯着T1毫无意义的对拍发呆,并没有想着去检查T2的数组开没开够(下次再颓cbx就不是人)

T3想了个错误思路,手模了几个点还和暴力跑的一样(放假就买彩票),于是自信满满得想象自己能AK,出成绩后直接完戏

T1:匹配

刚看到这题以为是kmp然而kmp已经快忘干净了,刚想打个暴力,打了个for突然发现这不hash傻逼吗,20分钟码了一个,成功水过

T2:回家

看第一眼看出来是tarjan,第二眼看出要求割点,第三眼看出要缩点双,第四眼看出要在新图跑dfs......

然而码的时候经历了模板炸死,样例写错,最终因数组开小re80的苦逼经过

缩点双一定要把数组开大几倍!!!血的教训!!!

T3:寿司

考试的时候本来码出来了40分暴力,然后自己YY了一个好似是正解的算法,头铁没有交暴力,10分GG

这题我考试的时候以为一定是把一种颜色移到两边,但是没有考虑一个点从1交换到n的情况(完戏)

正确做法是枚举将序列分开的端点,然后统计这个区间把一种颜色移到两边的最优答案

具体分析:经过观察我们发现最优答案一定是把一个分界点左边的移到左端点,右边的移到右端点的答案

而我们还发现这个端点一定左右各有一半另一种颜色(往左往右都不会更优),所以这个端点可以二分找到,总复杂度O(nlogn),勉强卡过

而根据上面的结论,我们发现在左右端点变化的时候,这个中点只可能往右走而不可能往左走,即具有单调性

所以我们可以用一个指针来代表中点,每次右移到满足条件的位置就好了,总复杂度O(n)

PS:好多人问一个区间知道了中点怎么才能O(1)求,这里简单说一下

把原字符串复制接在原串后,处理出来在新串里[1,i]所有红色左移到最左的花费sl,[i,n*2]所有红色右移到最右的花费sr,每个点左右有多少个红色rl,rr和蓝色bl,br,我们考虑把区间[l,mid]的红色移到最左(右移同理)则有 ans=sl[mid](所有的)-sl[l-1](超出l的)-(rl[mid]-rl[l-1])*bl[l-1](跨l的)

 

 1 #include<bits/stdc++.h>
 2 #define ll long long
 3 #define re register
 4 using namespace std;
 5 inline ll min(const ll x,const ll y){
 6     return x<y? x:y;
 7 }
 8 int lb[2000010],rb[2000010],lr[2000010],rr[2000010];ll slb[2000010],srb[2000010];
 9 char s[2000010];
10 inline ll solve(const int l,const int r){
11     ll ans,L=l,R=r,to=(lr[r]-lr[l-1])>>1;
12     while(L<R){
13         re int mid=L+R>>1;
14         if(lr[mid]-lr[l-1]>to) R=mid;
15         else L=mid+1;
16     }
17     ans=slb[L-1]-1ll*(lb[L-1]-lb[l-1])*lr[l-1]+srb[L]-1ll*(rb[L]-rb[r+1])*rr[r+1]-slb[l-1]-srb[r+1];
18     return ans;
19 }
20 int main(){
21     int T,l,num,sum;ll ans=0;
22     scanf("%d",&T);
23     while(T--){
24         scanf("%s",s+1); l=strlen(s+1); sum=0;
25         for(re int i=1;i<=l;i++) s[i+l]=s[i];
26         lb[0]=lr[0]=slb[0]=0;
27         for(re int i=1;i<=l*2;i++){
28             slb[i]=slb[i-1];
29             lb[i]=lb[i-1]; lr[i]=lr[i-1];
30             if(s[i]=='R') lr[i]++;
31             else lb[i]++,slb[i]+=lr[i];
32         }
33         rb[l<<1|1]=rr[l<<1|1]=srb[l<<1|1]=0;
34         for(re int i=l*2;i>=1;i--){
35             srb[i]=srb[i+1];
36             rb[i]=rb[i+1]; rr[i]=rr[i+1];
37             if(s[i]=='R') rr[i]++;
38             else rb[i]++,srb[i]+=rr[i];
39         }
40         ans=0x7fffffffffffffff;
41         for(int i=1;i<=l;i++){
42             ans=min(ans,solve(i,i+l-1));
43         }
44         printf("%lld\n",ans);
45     }
46 }
二分

 

 1 #include<bits/stdc++.h>
 2 #define ll long long
 3 #define re register
 4 using namespace std;
 5 inline ll min(const ll x,const ll y){
 6     return x<y? x:y;
 7 }
 8 int lb[2000010],rb[2000010],lr[2000010],rr[2000010];ll slb[2000010],srb[2000010];
 9 char s[2000010];
10 inline ll solve(const int l,const int r,const int L){
11     return slb[L-1]-1ll*(lb[L-1]-lb[l-1])*lr[l-1]+srb[L]-1ll*(rb[L]-rb[r+1])*rr[r+1]-slb[l-1]-srb[r+1];
12 }
13 int main(){
14     int T,l,num,sum,div;ll ans=0;
15     scanf("%d",&T);
16     while(T--){
17         scanf("%s",s+1); l=strlen(s+1); sum=0;
18         for(re int i=1;i<=l;i++) s[i+l]=s[i];
19         lb[0]=lr[0]=slb[0]=0;
20         for(re int i=1;i<=l*2;i++){
21             slb[i]=slb[i-1];
22             lb[i]=lb[i-1]; lr[i]=lr[i-1];
23             if(s[i]=='R') lr[i]++;
24             else lb[i]++,slb[i]+=lr[i];
25         }
26         rb[l<<1|1]=rr[l<<1|1]=srb[l<<1|1]=0;
27         for(re int i=l*2;i>=1;i--){
28             srb[i]=srb[i+1];
29             rb[i]=rb[i+1]; rr[i]=rr[i+1];
30             if(s[i]=='R') rr[i]++;
31             else rb[i]++,srb[i]+=rr[i];
32         }
33         ans=0x7fffffffffffffff;div=1;
34         for(int i=1;i<=l;i++){
35             while(div!=i+l-1&&lb[div]-lb[i-1]<=(lb[i+l-1]-lb[i-1])/2) div++;
36             ans=min(ans,solve(i,i+l-1,div));
37         }
38         printf("%lld\n",ans);
39     }
40 }
正解

 

posted @ 2019-07-25 19:53  mikufun♘  阅读(237)  评论(3编辑  收藏  举报