链接:http://codeforces.com/contest/820/problem/C
分析:A完一觉起来发现数据改了,WA掉了。。出题人觉得自己做法错了。。
首先把字符串记为A1 B1 A2 B2 A3 B3……,事实上,A3与A1完全一样,因此是个周期数列。为了方便,把a记做1,b记做2,以此类推。对于B来说,最优策略是选择与最后a-b个字母其中一个一样的,然后a>=b时,A2就会变成1,2,...,b,1+a,...,2a-b,
a<b时,就是1到a+1,跳过选择的那个字母。
周期性证明:按前面的取法,a>b时,A2中[b+1,a]这一段不能取,a<=b时,a不能取,因此A2为从a开始跳过不能取的一段,记A2=A21+A22,A3恰好只需和A22互异,所以可以从1取到a,形成周期,T=2(a+b)。
由周期性证明易得,在一个周期内,记互异元素个数为t,若a>=b,t=2a-b,否则t=a+1。
当r-l>=T时,由周期性知,答案为t,否则,对T取余,转化为一个周期内的问题,枚举选择的字母,求一下区间内的最小值即可。
B1、B2必然选择同一个字母。首先,只要满足贪心,是否选同一字母不影响周期性。考虑mod完的r和l,假设选择不同字母,先考虑l<=r,若r在B2,显然选择不同字母不会更优;若r在B2之前,B2取什么并不影响。如果l>r,即答案为[1,r]加上[l,a+b]的互异元素个数,
同样,选择不同字母并不会更优,因此枚举时只需要枚举同一个即可。
1 #include<iostream> 2 #include<cstring> 3 using namespace std; 4 typedef long long ll; 5 ll a,b,l,r,ans=100,T; 6 ll Cal(char c){ 7 ll count=0; 8 char s[100]; 9 for(int i=0;i<a;i++) 10 s[i]='a'+i; 11 for(int i=a;i<a+b;i++) 12 s[i]=c; 13 int have[26]; 14 memset(have,0,26*sizeof(int)); 15 for(ll i=a+b-1;i>=b;i--){ 16 have[s[i]-'a']=1; 17 } 18 ll i=a+b,j=0; 19 while(i<2*a+b){ 20 while(have[j]){ 21 j++; 22 } 23 s[i]=j+'a'; 24 i++;j++; 25 } 26 for(int i=2*a+b;i<T;i++) 27 s[i]=s[i-1]; 28 memset(have,0,26*sizeof(int)); 29 if(l<=r){ 30 for(ll i=l;i<=r;i++){ 31 if(!have[s[i]-'a']){ 32 count++;have[s[i]-'a']=1; 33 } 34 } 35 }else{ 36 for(ll i=0;i<=r;i++){ 37 if(!have[s[i]-'a']){ 38 count++;have[s[i]-'a']=1; 39 } 40 } 41 for(ll i=l;i<T;i++){ 42 if(!have[s[i]-'a']){ 43 count++;have[s[i]-'a']=1; 44 } 45 } 46 } 47 return count; 48 } 49 int main(){ 50 ll t; 51 cin>>a>>b>>l>>r; 52 l--;r--; 53 T=2*(a+b); 54 if(a>b+1)t=2*a-b; 55 else t=a+1; 56 if(r-l>=T)ans=t; 57 else{ 58 r%=T;l%=T; 59 for(int i=0;i<a;i++){ 60 ans=min(Cal('a'+i),ans); 61 } 62 } 63 cout<<ans<<endl; 64 return 0; 65 }