计蒜之道2019复赛题解
对每个i求出以它结尾的[1,i]中的LIS长度f1与个数g1,和以它开头的[i,n]中的LIS长度f2与个数g2,若f1+f2-1=整个数列的LIS长度,那么它出现在LIS中的概率就是g1*g2/整个数列LIS的个数。发现可以用线段树优化朴素DP转移,维护下区间最大值和最大值的个数即可快速求出f和g。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<iostream> 5 #define ls (x<<1) 6 #define rs (ls|1) 7 #define lson ls,L,mid 8 #define rson rs,mid+1,R 9 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 10 typedef long long ll; 11 using namespace std; 12 13 const int N=1000010,mod=998244353; 14 int n,tot,res,sm,a[N],b[N],f1[N],f2[N],g1[N],g2[N],v1[N<<2],v2[N<<2]; 15 16 int ksm(int a,int b){ 17 int res=1; 18 for (; b; a=1ll*a*a%mod,b>>=1) 19 if (b & 1) res=1ll*res*a%mod; 20 return res; 21 } 22 23 void upd(int x,int y,int &r1,int &r2){ 24 if (x>r1) r1=x,r2=y; else if (x==r1) r2=(r2+y)%mod; 25 } 26 27 void build(int x,int L,int R){ 28 v1[x]=v2[x]=0; 29 if (L==R) return; 30 int mid=(L+R)>>1; build(lson); build(rson); 31 } 32 33 void mdf(int x,int L,int R,int p,int k1,int k2){ 34 if (L==R){ upd(k1,k2,v1[x],v2[x]); return; } 35 upd(k1,k2,v1[x],v2[x]); 36 int mid=(L+R)>>1; 37 if (p<=mid) mdf(lson,p,k1,k2); else mdf(rson,p,k1,k2); 38 } 39 40 void que(int x,int L,int R,int l,int r,int &r1,int &r2){ 41 if (L==l && r==R){ upd(v1[x],v2[x],r1,r2); return; } 42 int mid=(L+R)>>1; 43 if (r<=mid) que(lson,l,r,r1,r2); 44 else if (l>mid) que(rson,l,r,r1,r2); 45 else que(lson,l,mid,r1,r2),que(rson,mid+1,r,r1,r2); 46 } 47 48 int main(){ 49 freopen("a.in","r",stdin); 50 freopen("a.out","w",stdout); 51 scanf("%d",&n); 52 rep(i,1,n) scanf("%d",&a[i]),tot=max(tot,a[i]),b[i]=a[i]; 53 sort(b+1,b+n+1); tot=unique(b+1,b+n+1)-b-1; 54 rep(i,1,n) a[i]=lower_bound(b+1,b+tot+1,a[i])-b; 55 build(1,0,tot+1); mdf(1,0,tot+1,0,0,1); 56 rep(i,1,n) que(1,0,tot+1,0,a[i]-1,f1[i],g1[i]),f1[i]++,mdf(1,0,tot+1,a[i],f1[i],g1[i]); 57 build(1,0,tot+1); mdf(1,0,tot+1,tot+1,0,1); 58 for (int i=n; i; i--) que(1,0,tot+1,a[i]+1,tot+1,f2[i],g2[i]),f2[i]++,mdf(1,0,tot+1,a[i],f2[i],g2[i]); 59 que(1,0,tot+1,0,tot+1,res,sm); sm=ksm(sm,mod-2); 60 rep(i,1,n) if (f1[i]+f2[i]-1==res) printf("%lld ",1ll*g1[i]*g2[i]%mod*sm%mod); else printf("0 "); 61 return 0; 62 }
B.个性化评测系统
先枚举听的牌,再枚举对子,然后就只剩判断剩下的能不能组成四副刻子了。从小到大枚举牌型,能面子就面子,剩下的只能组成顺子,很好判和。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<iostream> 5 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 6 typedef long long ll; 7 using namespace std; 8 9 const int N=110; 10 char s[N]; 11 int d[N],p[N]; 12 13 void work(char s[]){ 14 if (s[1]=='m') d[s[0]-'0']++; 15 if (s[1]=='s') d[s[0]-'0'+9]++; 16 if (s[1]=='p') d[s[0]-'0'+18]++; 17 if (s[1]=='z') d[s[0]-'0'+27]++; 18 } 19 20 void work2(int x){ 21 if (x<=9) printf("%dm\n",x); 22 if (x>9 && x<=18) printf("%ds\n",x-9); 23 if (x>18 && x<=27) printf("%dp\n",x-18); 24 if (x>27) printf("%dz\n",x-27); 25 } 26 27 bool chk(){ 28 rep(x,1,34) if (d[x]>=2){ 29 rep(i,1,34) p[i]=d[i]; 30 p[x]-=2; bool flag=0; 31 rep(i,1,34) if (p[i]){ 32 if (p[i]>=3) p[i]-=3; 33 if (!p[i]) continue; 34 if (i==8 || i==9 || i==17 || i==18 || i==26 || i==27 || i==33 || i==34 || p[i+1]<p[i] || p[i+2]<p[i]){ flag=1; break; } 35 p[i+1]-=p[i]; p[i+2]-=p[i]; p[i]=0; 36 } 37 if (!flag) return 1; 38 } 39 return 0; 40 } 41 42 int main(){ 43 freopen("b.in","r",stdin); 44 freopen("b.out","w",stdout); 45 while (~scanf("%s",s)){ 46 rep(i,0,34) d[i]=0; work(s); 47 rep(i,2,13) scanf("%s",s),work(s); 48 rep(i,1,34) if (d[i]<=3) { d[i]++; if (chk()) work2(i); d[i]--; } 49 } 50 return 0; 51 }
把SG表打出来发现就是末尾0的个数,然后问题就是想求有多少种方案能让整个数列异或和为0。先高精度对每个i统计出末尾有i个0的数有多少个,再FWT即可。注意这题似乎卡时,若高精度除法的时候没有余数(即被除数是偶数)则可以直接O(1)统计,不需要重新计算了。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 5 typedef long long ll; 6 using namespace std; 7 8 const int N=1000010,mod=998244353,i2=499122177; 9 char s[N]; 10 ll m; 11 int n,tot,a[N],c[N]; 12 13 int ksm(int a,int b){ 14 int res=1; 15 for (; b; a=1ll*a*a%mod,b>>=1) 16 if (b & 1) res=1ll*res*a%mod; 17 return res; 18 } 19 20 void FWT(int a[],int n,int f){ 21 for (int i=1; i<n; i<<=1) 22 for (int p=i<<1,j=0; j<n; j+=p) 23 for (int k=0; k<i; k++){ 24 int x=a[j+k],y=a[i+j+k]; 25 if (f) a[j+k]=(x+y)%mod,a[i+j+k]=(x-y+mod)%mod; 26 else a[j+k]=1ll*(x+y)*i2%mod,a[i+j+k]=1ll*(x-y+mod)*i2%mod; 27 } 28 } 29 30 int main(){ 31 freopen("c.in","r",stdin); 32 freopen("c.out","w",stdout); 33 scanf("%s%lld",s+1,&m); n=strlen(s+1); m%=mod-1; tot=-1; 34 rep(i,1,n) a[i]=s[n-i+1]-'0'; 35 c[++tot]=a[n]; 36 for (int i=n-1; i; i--) c[tot]=(c[tot]*10ll+a[i])%mod; 37 int p=ksm(c[tot],m); 38 while (n){ 39 for (int i=n; i; i--) a[i-1]+=(a[i]&1)*10,a[i]>>=1; 40 if (!a[0]) tot++,c[tot]=1ll*c[tot-1]*i2%mod; 41 else{ 42 c[++tot]=a[n]; 43 for (int i=n-1; i; i--) c[tot]=(c[tot]*10ll+a[i])%mod; 44 } 45 a[0]=0; while (n && !a[n]) n--; 46 } 47 rep(i,0,tot-1) c[i]=(c[i]-c[i+1]+mod)%mod; 48 for (n=1; n<=tot; n<<=1); 49 FWT(c,n,1); 50 rep(i,0,n-1) c[i]=ksm(c[i],m)%mod; 51 FWT(c,n,0); printf("%d\n",(p-c[0]+mod)%mod); 52 return 0; 53 }
D.“星云系统”
先写了一个序列自动机,MLE。
再写了一个枚举+二分,TLE。
考虑线性做法,从前往后枚举,当后面一个字典序更小的字符可以出现在答案中时,可以考虑用它替代前面的字符,栈维护。
1 #include<vector> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<iostream> 6 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 7 typedef long long ll; 8 using namespace std; 9 10 const int N=5000010; 11 char s[N],q[N]; 12 int n,K,top; 13 14 int main(){ 15 freopen("d.in","r",stdin); 16 freopen("d.out","w",stdout); 17 scanf("%s%d",s+1,&K); n=strlen(s+1); 18 rep(i,1,n){ 19 while (top && n-i+1+top>K && q[top]>s[i]) top--; 20 q[++top]=s[i]; 21 } 22 rep(i,1,K) putchar(q[i]); 23 return 0; 24 }
前驱:找到最靠后的一个可以被替代为'('的')',这个位置之前的不动,这个位置替代为')',之后重新构造。后继同理。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<iostream> 5 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 6 typedef long long ll; 7 using namespace std; 8 9 const int N=2000010; 10 char s[N]; 11 int n,sm[N]; 12 13 int main(){ 14 freopen("e.in","r",stdin); 15 freopen("e.out","w",stdout); 16 scanf("%s",s+1); n=strlen(s+1); 17 rep(i,1,n) if (s[i]=='(') sm[i]=sm[i-1]+1; else sm[i]=sm[i-1]-1; 18 for (int i=n; i; i--) if (s[i]==')' && ((sm[i-1]+1)&1)==((n-i)&1) && sm[i-1]+1<=n-i){ 19 rep(j,1,i-1) putchar(s[j]); putchar('('); int t=sm[i-1]+1; 20 rep(j,i+1,n) if (t) putchar(')'),t--; else putchar('('),t++; 21 break; 22 } 23 puts(""); 24 for (int i=n; i; i--) if (s[i]=='(' && ((sm[i-1]-1)&1)==((n-i)&1) && sm[i-1]){ 25 rep(j,1,i-1) putchar(s[j]); putchar(')'); int t=sm[i-1]-1; 26 rep(j,i+1,n) if (t+1<=n-j) putchar('('),t++; else putchar(')'),t--; 27 break; 28 } 29 return 0; 30 }