2021.9.12考试总结[NOIP模拟51]
T1 茅山道术
仔细观察发现对于每个点只考虑它前面第一个与它颜色相同的点即可。
又仔细观察发现对一段区间染色后以这个区间内点为端点的区间不能染色。
于是对区间右端点而言,区间染色的贡献为遍历到区间左端点时的方案数。线性$DP$。
$code:$
1 #include<bits/stdc++.h> 2 #define int long long 3 using namespace std; 4 5 namespace IO{ 6 inline int read(){ 7 char ch=getchar(); int x=0,f=1; 8 while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); } 9 while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } 10 return x*f; 11 } 12 inline void write(int x,char sp){ 13 char ch[20]; int len=0; 14 if(x<0){ putchar('-'); x=~x+1; } 15 do{ ch[len++]=x%10+(1<<4)+(1<<5); x/=10; }while(x); 16 for(int i=len-1;~i;--i) putchar(ch[i]); putchar(sp); 17 } 18 inline int max(int x,int y){ return x<y?y:x; } 19 inline int min(int x,int y){ return x<y?x:y; } 20 inline void swap(int &x,int &y){ x^=y^=x^=y; } 21 inline void chmax(int &x,int y){ x=x<y?y:x; } 22 inline void chmin(int &x,int y){ x=x<y?x:y; } 23 } using namespace IO; 24 25 const int NN=1e6+5,p=1e9+7; 26 int n,ext,ans,pre[NN],c[NN],f[NN]; 27 28 signed main(){ 29 FILE *a=freopen("magic.in","r",stdin); 30 FILE *b=freopen("magic.out","w",stdout); 31 n=read(); 32 for(int i=1;i<=n;i++) c[i]=read(); 33 for(int i=1;i<=n;i++) 34 if(c[i]!=c[i-1]) c[++ext]=c[i]; 35 n=ext; pre[c[1]]=1; f[1]=1; 36 for(int i=2;i<=n;i++){ 37 f[i]=f[i-1]; 38 if(pre[c[i]]) (f[i]+=f[pre[c[i]]])%=p; 39 pre[c[i]]=i; 40 } 41 write(f[n],'\n'); 42 return 0; 43 }
T2 泰拳警告
发现输赢的可能性相等。
枚举平局个数,设非平局个数为$s$,那么这种情况在固定平局位置的前提下总方案数为$2^s$。
因为输赢可能性相等,所以在排除输赢局数相同后赢大于输的方案为总方案的一半。
输赢相等的情况仅在$s$为偶时出现,为$C^s_{\frac{n}{2}}$。最后再乘上$C^n_{n-s}$即为这个平局个数的总方案。
期望就好说了。
推式子也能做。
$code:$
1 #include<bits/stdc++.h> 2 #define int long long 3 using namespace std; 4 5 namespace IO{ 6 inline int read(){ 7 char ch=getchar(); int x=0,f=1; 8 while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); } 9 while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } 10 return x*f; 11 } 12 inline void write(int x,char sp){ 13 char ch[20]; int len=0; 14 if(x<0){ putchar('-'); x=~x+1; } 15 do{ ch[len++]=x%10+(1<<4)+(1<<5); x/=10; }while(x); 16 for(int i=len-1;~i;--i) putchar(ch[i]); putchar(sp); 17 } 18 inline int max(int x,int y){ return x<y?y:x; } 19 inline int min(int x,int y){ return x<y?x:y; } 20 inline void swap(int &x,int &y){ x^=y^=x^=y; } 21 inline void chmax(int &x,int y){ x=x<y?y:x; } 22 inline void chmin(int &x,int y){ x=x<y?x:y; } 23 } using namespace IO; 24 25 const int NN=3e6+5,mod=998244353; 26 int n,p,ans,win[NN],flt[NN],fac[NN],inv[NN],mi[NN]; 27 28 inline int C(int n,int m){ return n<m?0:fac[n]*inv[m]%mod*inv[n-m]%mod; } 29 inline int qpow(int a,int b){ 30 int res=1; 31 while(b){ 32 if(b&1) res=res*a%mod; 33 a=a*a%mod; 34 b>>=1; 35 } 36 return res; 37 } 38 39 signed main(){ 40 FILE *a=freopen("fight.in","r",stdin); 41 FILE *b=freopen("fight.out","w",stdout); 42 n=read(); p=read()+2; fac[0]=inv[0]=win[0]=flt[0]=mi[0]=1; 43 for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%mod; 44 inv[n]=qpow(fac[n],mod-2); 45 for(int i=n-1;i;i--) inv[i]=inv[i+1]*(i+1)%mod; 46 win[1]=qpow(p,mod-2); flt[1]=win[1]*(p-2)%mod; mi[1]=2; 47 for(int i=2;i<=n;i++){ 48 win[i]=win[i-1]*win[1]%mod; 49 flt[i]=flt[i-1]*flt[1]%mod; 50 mi[i]=(mi[i-1]<<1)%mod; 51 } 52 for(int i=1;i<=n;i++){ 53 int rest=n-i+1,num=mi[rest]; 54 if(!(rest&1)) (num+=mod-C(rest,rest>>1))%=mod; 55 num=num*inv[2]%mod; (num*=C(n,i-1))%=mod; 56 (ans+=i*flt[i-1]%mod*win[n-i+1]%mod*num%mod)%=mod; 57 } 58 write(ans,'\n'); 59 return 0; 60 }
T3 万猪拱塔
发现矩形有贡献仅当里面的数是连续的。
考虑对权值为$[l,r]$的位置染色,那么考虑$(n+1)\times (m+1)$个$2\times 2$的小正方形(不全也算),这些染色的位置能组成矩形,当且仅当被染$\frac{1}{4}$的小正方形只有$4$个,且没有被染$\frac{3}{4}$的小正方形。
被染$\frac{1}{4}$的小正方形个数总大于等于$4$于是可以枚举区间的$r$,线段树上枚举对于每个$l$,被染色$\frac{1}{4}$或$\frac{3}{4}$的小正方形个数的最小值$mn$,达到最小值的$l$个数$num$和总和$sum$。
发现每个$r$最小值必定为$4$($[r,r]$),每个$r$的贡献为$r\times num-sum+num$。
每次$r$移动时会影响$4$个小正方形,记录小正方形中四个权值的大小顺序,按权值顺序修改即可。
注意最小值$4$可能是由$r$贡献的,但下放标记时因为$r$初始是$0$不会被更新,因此要手动给$r$下方标记。
$code:$
1 #include<bits/stdc++.h> 2 #define int long long 3 using namespace std; 4 5 namespace IO{ 6 inline int read(){ 7 char ch=getchar(); int x=0,f=1; 8 while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); } 9 while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } 10 return x*f; 11 } 12 inline void write(int x,char sp){ 13 char ch[20]; int len=0; 14 if(x<0){ putchar('-'); x=~x+1; } 15 do{ ch[len++]=x%10+(1<<4)+(1<<5); x/=10; }while(x); 16 for(int i=len-1;~i;--i) putchar(ch[i]); putchar(sp); 17 } 18 inline int max(int x,int y){ return x<y?y:x; } 19 inline int min(int x,int y){ return x<y?x:y; } 20 inline void swap(int &x,int &y){ x^=y^=x^=y; } 21 inline void chmax(int &x,int y){ x=x<y?y:x; } 22 inline void chmin(int &x,int y){ x=x<y?x:y; } 23 } using namespace IO; 24 25 const int NN=5e5+5,p=998244353,inf=0x7fffffff; 26 int n,m,ext,ex,ans,tmp,w[NN],pos[NN],cnt[NN][5]; 27 vector<int>sot; 28 inline int id(int x,int y){ return (m+1)*x+y; } 29 30 namespace segment_tree{ 31 #define ld rt<<1 32 #define rd (rt<<1)|1 33 int mn[NN<<2],sum[NN<<2],num[NN<<2],laz[NN<<2]; 34 void pushup(int rt){ 35 if(!mn[ld]&&!mn[rd]) return; 36 if(!mn[ld]){ mn[rt]=mn[rd], sum[rt]=sum[rd], num[rt]=num[rd]; return; } 37 if(!mn[rd]){ mn[rt]=mn[ld], sum[rt]=sum[ld], num[rt]=num[ld]; return; } 38 if(mn[ld]==mn[rd]){ 39 mn[rt]=mn[ld]; 40 sum[rt]=(sum[ld]+sum[rd])%p; 41 num[rt]=(num[ld]+num[rd])%p; 42 return; 43 } 44 mn[rt]=min(mn[ld],mn[rd]); 45 sum[rt]=mn[ld]<mn[rd]?sum[ld]:sum[rd]; 46 num[rt]=mn[ld]<mn[rd]?num[ld]:num[rd]; 47 } 48 void pushdown(int rt){ 49 laz[ld]+=laz[rt]; laz[rd]+=laz[rt]; 50 mn [ld]+=laz[rt]; mn [rd]+=laz[rt]; 51 laz[rt]=0; 52 } 53 void build(int rt,int l,int r){ 54 if(l==r){ 55 num[rt]=1; sum[rt]=l; 56 return; 57 } 58 int mid=l+r>>1; 59 build(ld,l,mid); 60 build(rd,mid+1,r); 61 } 62 void modify(int rt,int l,int r,int opl,int opr,int v){ 63 if(l>=opl&&r<=opr){ 64 mn[rt]+=v; laz[rt]+=v; 65 return; 66 } 67 pushdown(rt); 68 int mid=l+r>>1; 69 if(opl<=mid) modify(ld,l,mid,opl,opr,v); 70 if(opr>mid) modify(rd,mid+1,r,opl,opr,v); 71 pushup(rt); 72 } 73 void down(int rt,int l,int r,int pos){ 74 if(l==r) return; 75 pushdown(rt); 76 int mid=l+r>>1; 77 if(pos<=mid) down(ld,l,mid,pos); 78 else down(rd,mid+1,r,pos); 79 pushup(rt); 80 } 81 } using namespace segment_tree; 82 83 void update(int i,int p){ 84 if(i==cnt[p][1]) 85 modify(1,1,ext,1 ,cnt[p][1], 1); 86 if(cnt[p][2]>1e9) return; 87 if(i==cnt[p][2]){ 88 modify(1,1,ext,1 ,cnt[p][1],-1); 89 modify(1,1,ext,cnt[p][1]+1,cnt[p][2], 1); 90 } 91 if(cnt[p][3]>1e9) return; 92 if(i==cnt[p][3]){ 93 modify(1,1,ext,1 ,cnt[p][1], 1); 94 modify(1,1,ext,cnt[p][1]+1,cnt[p][2],-1); 95 modify(1,1,ext,cnt[p][2]+1,cnt[p][3], 1); 96 } 97 if(cnt[p][4]>1e9) return; 98 if(i==cnt[p][4]){ 99 modify(1,1,ext,1 ,cnt[p][1],-1); 100 modify(1,1,ext,cnt[p][1]+1,cnt[p][2], 1); 101 modify(1,1,ext,cnt[p][2]+1,cnt[p][3],-1); 102 modify(1,1,ext,cnt[p][3]+1,cnt[p][4], 1); 103 } 104 } 105 106 signed main(){ 107 FILE *a=freopen("pig.in","r",stdin); 108 FILE *b=freopen("pig.out","w",stdout); 109 n=read(); m=read(); ext=n*m; build(1,1,ext); 110 memset(w,0x7f,sizeof(w)); 111 for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) 112 w[id(i,j)]=read(), pos[w[id(i,j)]]=id(i,j); 113 for(int x=0;x<=n;x++) for(int y=0;y<=m;y++){ 114 int i=id(x,y); sot.clear(); 115 sot.push_back(w[i]); 116 if(x==n) sot.push_back(inf); 117 else sot.push_back(w[i+m+1]); 118 if(y==m) sot.push_back(inf); 119 else sot.push_back(w[i+1]); 120 if(x==n||y==m) sot.push_back(inf); 121 else sot.push_back(w[i+m+2]); 122 sort(sot.begin(),sot.end()); 123 cnt[i][1]=sot[0]; cnt[i][2]=sot[1]; 124 cnt[i][3]=sot[2]; cnt[i][4]=sot[3]; 125 } 126 for(int i=1;i<=ext;i++){ 127 update(i,pos[i]-m-2); 128 update(i,pos[i]-m-1); 129 update(i,pos[i]-1); 130 update(i,pos[i]); 131 down(1,1,ext,i); 132 (ans+=i*num[1]%p-sum[1]+num[1]+p)%=p; 133 } 134 write(ans,'\n'); 135 return 0; 136 }
T4 抑郁刀法
删点太难调了。。
gu了