2017/9/20模拟赛
题解:这题重点是怎样算出询问的答案,我们发现,若l[i]与r[i]间有1操作,在1操作前的操作都没意义了,我们就用前缀和,然后求出max(l[i],last[r[i]])间的和即可(last[i]为i前第一个1操作的位置)。注意该膜的要膜,不然会挂QAQ。
代码如下:
1 #include<cstdio> 2 #include<iostream> 3 #define MN 10000005 4 #define Mod 998244353 5 using namespace std; 6 unsigned int seed,a[MN]; 7 int n,m,l[MN],r[MN],last[MN]; 8 long long ans=0,la,w=1,sum[MN],now; 9 bool type[MN]; 10 unsigned int GetNext(){ 11 seed^=(seed<<7); 12 seed^=(seed>>8); 13 seed^=(seed<<13); 14 return seed; 15 } 16 int main() 17 { 18 freopen("math.in","r",stdin); 19 freopen("math.out","w",stdout); 20 scanf("%d%d%u",&n,&m,&seed); 21 for(int i=1;i<=n;++i) type[i]=GetNext()%2,a[i]=GetNext(); 22 for(int i=1;i<=m;++i){ 23 l[i]=GetNext()%n+1,r[i]=GetNext()%n+1; 24 int tmp; 25 if(l[i]>r[i]) tmp=l[i],l[i]=r[i],r[i]=tmp; 26 } 27 for(int i=1;i<=n;i++){ 28 if(type[i]==1) last[i]=i; 29 else last[i]=last[i-1]; 30 sum[i]=sum[i-1]+a[i]; 31 } 32 for(int i=1;i<=m;i++){ 33 now=sum[r[i]]-sum[max(l[i],last[r[i]])-1]; 34 w=w*233%Mod; 35 ans=(ans+now%Mod*w%Mod)%Mod; 36 } 37 cout<<ans; 38 return 0; 39 }
题解:可以发现,所有循环同构的字符串的最小表示法都相等,所以我们从每个字符串的最小表示法向后搜完整个字符,具体看代码吧,中间输出一下就秒理解了。
代码如下:
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #define MN 1000005 5 using namespace std; 6 int n,m,val[MN],c[MN][26],cnt; 7 char st[MN];long long ans; 8 int getmin(char *s){ 9 int len=strlen(s); 10 int i=0,j=1,k=0,t; 11 while(i<m&&j<m&&k<m){ 12 t=s[(i+k)%m]-s[(j+k)%m]; 13 if(!t) k++; 14 else{ 15 if(t>0) i+=k+1;else j+=k+1; 16 if(i==j) j++; k=0; 17 } 18 } 19 return i<j?i:j; 20 } 21 int main() 22 { 23 freopen("english.in","r",stdin); 24 freopen("english.out","w",stdout); 25 scanf("%d%d",&n,&m); 26 for(int i=1;i<=n;i++){ 27 scanf("%s",st); 28 int pos=getmin(st),x=0; 29 for(int i=0;i<m;i++){ 30 if(!c[x][st[(pos+i)%m]-'a']) c[x][st[(pos+i)%m]-'a']=++cnt; 31 x=c[x][st[(pos+i)%m]-'a']; 32 //cout<<x<<" "; 33 }//puts(""); 34 ans+=val[x]++; 35 } 36 cout<<ans; 37 return 0; 38 }
题解:首先答案肯定是那 m 个数字中的一个。我们考虑从大到小考虑每个数字,合法就输出。确定要判断的数字之后,就只剩下了两种数字,分别大等于或者小于要判断的数字。这里认为大等于的数字是合法的。我们用 f[i]表示 i 的能量值大等于要判断的数字最少需要填入几个合法的数字,那么对于确定的叶子点,如果他合法,f[i]=0,否则 f[i]=INF。对于不确定的,f[i]=1然后就有了转移, f[i]就等同于它的三个儿子中比较小的两个 f 的和。最后我们只需要判断是否有足够的数字填入即可。
复杂度 O(n^2) 总共可以获得70分。
在此基础上二分,即可100分。
代码如下:
1 #include<cstdio> 2 #include<iostream> 3 #include<algorithm> 4 #define MN 1000005 5 using namespace std; 6 inline int read(){ 7 int x=0,f=1;char ch=getchar(); 8 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 9 while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} 10 return x*f; 11 } 12 int s[MN],n,m,c[MN][3],cnt,res,mark[MN],L[MN],mid; 13 int Dp(int x){ 14 if(c[x][0]==-1) return mark[x]?1:(s[x]>=mid?0:1e9); 15 else{ 16 int s1=Dp(c[x][0]),s2=Dp(c[x][1]),s3=Dp(c[x][2]); 17 if(s1>s2) swap(s2,s1);if(s2>s3) swap(s2,s3);if(s1>s2) swap(s1,s2); 18 return min(n+1,s1+s2); 19 } 20 } 21 int main() 22 { 23 freopen("chemistry.in","r",stdin); 24 freopen("chemistry.out","w",stdout); 25 n=read(); m=read(); 26 for(int i=1;i<=n;++i) 27 if((c[i][0]=read())==-1) s[i]=read(); 28 else c[i][1]=read(),c[i][2]=read(); 29 for(int i=1;i<=m;++i) mark[read()]=1; 30 for(int i=1;i<=n;++i) if(mark[i]) L[++cnt]=s[i]; 31 sort(L+1,L+cnt+1);int l=1,r=1e9; 32 while(l<=r){ 33 mid=l+r>>1; 34 if(Dp(1)<=cnt-(lower_bound(L+1,L+cnt+1,mid)-L)+1) res=mid,l=mid+1; 35 else r=mid-1; 36 } 37 printf("%d\n",res); 38 return 0; 39 }