• USACO 2021 US Open


    题外话

    写篇题解攒rp

    A

    题意:
    给定长度为(n)的序列({A_i}),一组((x,y,z)(1le x<y<zle n))合法,当且仅当在区间([x,z])(A_x,A_y,A_z)均只出现一次

    做法

    (z)进行扫描线,同时维护每个(x)右边有效(y)的数量(B_x)

    (last_i)(i)前面离(i)最近的与(A_i)相同的数字。

    在枚举到(i)

    • 查询(j<i)(B_j>0)(B_j)之和。
    • 初始化(B_i=0),令(B_{last_i}=-infty)
    • (xin (last_{last_i},last_i)的)(B_x-1);对(xin(last_i,i))(B_x+1)

    以上操作均可以用线段树优化。

    总复杂度(O(nlogn))

    #include<bits/stdc++.h>
    typedef long long LL;
    typedef double dl;
    #define opt operator
    #define pb push_back
    #define pii std::pair<LL,LL>
    const LL maxn=2e5+9,mod=998244353,inf=0x3f3f3f3f;
    LL Read(){
    	LL x(0),f(1); char c=getchar();
    	while(c<'0' || c>'9'){
    		if(c=='-') f=-1; c=getchar();
    	}
    	while(c>='0' && c<='9'){
    		x=(x<<3ll)+(x<<1ll)+c-'0'; c=getchar();
    	}return x*f;
    }
    void Chkmin(LL &x,LL y){
    	if(y<x) x=y;
    }
    void Chkmax(LL &x,LL y){
    	if(y>x) x=y;
    }
    LL add(LL x,LL y){
    	return x+=y,x>=mod?x-mod:x;
    }
    LL dec(LL x,LL y){
    	return x-=y,x<0?x+mod:x;
    }
    LL mul(LL x,LL y){
    	return 1ll*x*y%mod;
    }
    LL Pow(LL base,LL b){
    	LL ret(1); while(b){
    		if(b&1) ret=mul(ret,base); base=mul(base,base); b>>=1;
    	}return ret;
    }
    namespace Sgt{
    	LL sum[maxn<<2],num[maxn<<2],son[maxn<<2][2],tag[maxn<<2];
    	void Update(LL nw){
    		sum[nw]=sum[nw<<1]+sum[nw<<1|1];
    		num[nw]=num[nw<<1]+num[nw<<1|1];
    	}
    	void Addpush(LL nw,LL v){
    		tag[nw]+=v;
    		sum[nw]+=num[nw]*v;
    	}
    	void Pushdown(LL nw){
    		LL x(tag[nw]);
    		Addpush(nw<<1,x); Addpush(nw<<1|1,x);
    		tag[nw]=0;
    	}
    	void Modify(LL nw,LL l,LL r,LL lt,LL rt,LL v){
    		if(lt<=l && rt>=r){
    			Addpush(nw,v);
    			return;
    		}
    		Pushdown(nw);
    		LL mid(l+r>>1);
    		if(lt<=mid) Modify(nw<<1,l,mid,lt,rt,v);
    		if(rt>mid) Modify(nw<<1|1,mid+1,r,lt,rt,v);
    		Update(nw);
    	}
    	void Insert(LL nw,LL l,LL r,LL x){
    		if(l==r){
    			num[nw]++; assert(!sum[nw]);
    			return;
    		}
    		Pushdown(nw);
    		LL mid(l+r>>1);
    		if(x<=mid) Insert(nw<<1,l,mid,x);
    		else Insert(nw<<1|1,mid+1,r,x);
    		Update(nw);
    	}
    	void Del(LL nw,LL l,LL r,LL x){
    		if(l==r){
    			num[nw]--; sum[nw]=0;
    			return;
    		}
    		Pushdown(nw);
    		LL mid(l+r>>1);
    		if(x<=mid) Del(nw<<1,l,mid,x);
    		else Del(nw<<1|1,mid+1,r,x);
    		Update(nw);
    	}
    	LL Query(LL nw,LL l,LL r,LL lt,LL rt){
    		if(lt<=l && rt>=r) return sum[nw];
    		Pushdown(nw);
    		LL mid(l+r>>1),ret(0);
    		if(lt<=mid) ret+=Query(nw<<1,l,mid,lt,rt);
    		if(rt>mid) ret+=Query(nw<<1|1,mid+1,r,lt,rt);
    		return ret;
    	}
    	void Write(LL nw,LL l,LL r){
    		if(l==r){
    			printf("(%lld,%lld)",sum[nw],num[nw]);
    			return;
    		}Pushdown(nw);
    		LL mid(l+r>>1);
    		Write(nw<<1,l,mid); Write(nw<<1|1,mid+1,r);
    	}
    }
    LL n;
    LL a[maxn],pre[maxn],last[maxn];
    int main(){
    	n=Read();
    	for(LL i=1;i<=n;++i) a[i]=Read();
    	LL ret(0);
    	for(LL i=1;i<=n;++i){
    		LL c(a[i]);
    		LL x(last[c]);
    		pre[i]=x;
    		ret+=Sgt::Query(1,1,n,x+1,i);
    		if(x){
    			Sgt::Del(1,1,n,x);
    		}
    		Sgt::Insert(1,1,n,i);
    		if(pre[x]+1<=x-1){
    			Sgt::Modify(1,1,n,pre[x]+1,x-1,-1);
    		}
    		if(pre[i]+1<=i-1){
    		    Sgt::Modify(1,1,n,pre[i]+1,i-1,1);
    	    }
    		last[c]=i;
    	}
    	printf("%lld
    ",ret);
    	return 0;
    }
    

    B

    题意:
    给定(n,K)及一张有向图,保证(i<j,ilongleftarrow j)的边只有(K)条。
    再给定(n)个点的状态,空 or 起点 or 终点(起点数量与终点数量相同)。
    一组合法的方案为:起点与终点进行配对,之后每个起点向对应的终点走,可经过一个点多次,要求每条边被恰好经过一次。
    保证至少有一组合法方案。
    对合法方案进行计数。
    多测。
    (Kle 4,sum n^2le 20000)

    做法

    建立超级源汇点(U)

    对于每个起点(i)(Ulongrightarrow i)
    对于每个终点(i)(ilongrightarrow U)

    对于原题意的每种方案,对应新图的一条欧拉回路。

    对于有向图欧拉回路计数,可以使用( ext{BEST})定理。

    总复杂度(sum n^3)(与(K)无关),如果有与(K)有关的做法,欢迎一起交流/kel。

    #include<bits/stdc++.h>
    using namespace std;
    typedef double dl;
    #define opt operator
    #define pb push_back
    const int MAX=201;
    const int mod=1e9+7;
    int add(int x,int y){
    	return x+=y,x>=mod?x-mod:x;
    }
    int dec(int x,int y){
    	return x-=y,x<0?x+mod:x;
    }
    int mul(int x,int y){
    	return 1ll*x*y%mod;
    }
    int Pow(int base,int b){
    	int ret(1); while(b){
    		if(b&1) ret=mul(ret,base); base=mul(base,base); b>>=1;
    	}return ret;
    }
    int Inv(int x){
    	return Pow(x,mod-2);
    }
    struct mat{
    	int N;
    	int A[MAX][MAX];
    	void Init(int n,int op=0){
    		N=n;
    		memset(A,0,sizeof(A));
    		for(int i=1;i<=N;++i) A[i][i]=op;
    	}
    	mat opt * (const mat B){
    		mat ret; ret.Init(N,0);
    		for(int i=1;i<=N;++i){
    			for(int k=1;k<=N;++k){
    				for(int j=1;j<=N;++j){
    					ret.A[i][j]=add(ret.A[i][j],mul(A[i][k],B.A[k][j]));
    				}
    			}
    		}return ret;
    	}
    	int Getdet(){
    		int ret(1);
    		for(int i=1;i<=N;++i){
    			for(int j=i;j<=N;++j) if(A[j][i]){
    				if(j^i) ret=dec(0,ret);
    				std::swap(A[i],A[j]);
    				break;
    			}
    			int inv(Inv(A[i][i]));
    			for(int j=i+1;j<=N;++j){
    				int tmp=mul(inv,A[j][i]);
    				for(int k=i;k<=N;++k){
    					A[j][k]=dec(A[j][k],mul(tmp,A[i][k]));
    				}
    			}
    		}
    		for(int i=1;i<=N;++i) ret=mul(ret,A[i][i]);
    		return ret;
    	}
    	void Write(){
    		for(int i=1;i<=N;++i){
    			for(int j=1;j<=N;++j){
    				printf("%d ",A[i][j]<=100?A[i][j]:A[i][j]-mod);
    			}puts("");
    		}puts("-------------------");
    	}
    };
    int n,K,T;
    int star[MAX],en[MAX],fac[MAX],V[MAX][MAX];
    char s[MAX],start[MAX];
    void Init2(){
    	scanf("%d%d",&n,&K);
    	fac[0]=1; for(int i=1;i<=n;++i) fac[i]=mul(fac[i-1],i);
    	scanf(" %s",start+1);
    	for(int i=1;i<=n;++i) star[i]=start[i]=='S';
    	for(int i=1;i<=n;++i) en[i]=start[i]=='R';
    	for(int i=1;i<=n;++i){
    		static char s[MAX];
    		scanf(" %s",s+1);
    		for(int j=1;j<=n;++j) V[i][j]=s[j]-'0';
    	}
    }
    void Solve(){
    	static int degout[MAX],degin[MAX];
    	memset(degout,0,sizeof(degout));
    	memset(degin,0,sizeof(degin));
    	for(int i=1;i<=n;++i){
    		for(int j=1;j<=n;++j){
    			degout[i]+=V[i][j];
    			degin[j]+=V[i][j];
    		}
        }
    	for(int i=1;i<=n;++i){
    		degout[i]+=en[i];
    	}
    	for(int i=1;i<=n;++i){
    		degin[i]+=star[i];
    	}
    	static int id[MAX];
    	memset(id,0,sizeof(id));
    	int nod(0);
    	for(int i=1;i<=n;++i) if(degout[i] || degin[i]){
    		id[i]=++nod;
    	}
    	static mat M;
    	M.Init(nod,0);
    	for(int i=1;i<=n;++i)if(id[i]){
    		int nw(id[i]);
    		int &x=M.A[nw][nw];
    		x=add(x,degout[i]);
    		for(int j=1;j<=n;++j) if(V[i][j]){
    			int nxt(id[j]);
    			assert(nxt);
    			int &y=M.A[nw][nxt];
    			y=dec(y,1);
    		}
    	}
    	int ret(M.Getdet());
    	for(int i=1;i<=n;++i) if(degout[i])
    	    ret=mul(ret,fac[degout[i]-1]);
    	printf("%d
    ",ret);
    }
    void Init1(){
    	scanf("%d",&T);
    	while(T--){
    		Init2();
    		Solve();
    	}
    }
    int main(){
    	Init1();
    	return 0;
    }
    

    C

    题意:
    给定(n imes n)的一个草坪(一个位置要么为草要么为石头),求以下子图个数
    (1)子图四连通,且全部为草。
    (2)若存在两点((x_1,y)(x_2,y)(x_1le x_2)),那么对于((x,y)(xin[x_1,x_2]))也应在子图内。
    (3)若存在两点((x,y_1)(x,y_2)(y_1le y_2)),那么对于((x,y)(yin[y_1,y_2]))也应在子图内。
    (nle 150)

    做法

    ((l_x,r_x)ldots(l_y,r_y)(xle y))(xle y))描述一组合法方案。

    对于(iin[x,y))((l_i,r_i))应与((l_{i+1},r_{i+1}))有交。
    (l_i(iin[x,y]))应先递减再递增(非严格)
    (r_i(iin[x,y]))应先递增再递减(非严格)

    (f_{i,j,k,0/1,0/1})(l_i=j,r_i=k),目前左边界是否未递减,目前右边界是否未递增。
    暴力转移,复杂度为(O(n^5))(详见以下代码)。

    考虑前缀和,可以优化至(O(n^3))(由于有(2^6)倍常数,需要对减少取模次数,依次优化常数)。

    (O(n^5))

    #include<bits/stdc++.h>
    typedef int LL;
    typedef double dl;
    #define opt operator
    #define pb push_back
    #define pii std::pair<LL,LL>
    const LL maxn=151,mod=1e9+7,inf=0x3f3f3f3f;
    LL Read(){
    	LL x(0),f(1); char c=getchar();
    	while(c<'0' || c>'9'){
    		if(c=='-') f=-1; c=getchar();
    	}
    	while(c>='0' && c<='9'){
    		x=(x<<3ll)+(x<<1ll)+c-'0'; c=getchar();
    	}return x*f;
    }
    void Chkmin(LL &x,LL y){
    	if(y<x) x=y;
    }
    void Chkmax(LL &x,LL y){
    	if(y>x) x=y;
    }
    LL add(LL x,LL y){
    	return x+=y,x>=mod?x-mod:x;
    }
    LL dec(LL x,LL y){
    	return x-=y,x<0?x+mod:x;
    }
    LL mul(LL x,LL y){
    	return 1ll*x*y%mod;
    }
    LL Pow(LL base,LL b){
    	LL ret(1); while(b){
    		if(b&1) ret=mul(ret,base); base=mul(base,base); b>>=1;
    	}return ret;
    }
    LL n;
    LL f[maxn][maxn][maxn][2][2],mark[maxn][maxn];
    char s[maxn][maxn];
    bool Check(LL l1,LL r1,LL l2,LL r2){
    	return std::max(l1,l2)<=std::min(r1,r2);
    }
    int main(){
    	n=Read();
    	for(LL i=1;i<=n;++i){
    		scanf(" %s",s[i]+1);
    	}
    	LL ret(0);
    	for(LL i=1;i<=n;++i){
    		memset(mark,0,sizeof(mark));
    		for(LL l=1;l<=n;++l){
    			for(LL r=l;r<=n && s[i][r]=='G';++r){
    				f[i][l][r][1][1]=1;
    				mark[l][r]=1;
    			}
    		}
    		for(LL l1=1;l1<=n;++l1){
    			for(LL r1=l1;r1<=n;++r1)if(mark[l1][r1]){
    				for(LL l2=1;l2<=n;++l2){
    					for(LL r2=l2;r2<=n;++r2) if(Check(l1,r1,l2,r2)){
    						LL ff1(l1<l2),ff2(r1>r2);
    						LL flag1(l1<=l2),flag2(r1>=r2);
    						for(LL op0=0;op0<2;++op0)if((op0&ff1)==ff1){
    							for(LL op1=0;op1<2;++op1)if((op1&ff2)==ff2){
    								f[i][l1][r1][op0&flag1][op1&flag2]=add(f[i][l1][r1][op0&flag1][op1&flag2],f[i-1][l2][r2][op0][op1]);
    							}
    						}
    					}
    				}
    			}
    		}
    		for(LL l=1;l<=n;++l){
    			for(LL r=l;r<=n;++r){
    				LL tmp(0);
    				for(LL op0=0;op0<2;++op0){
    					for(LL op1=0;op1<2;++op1){
    						ret=add(ret,f[i][l][r][op0][op1]);
    						tmp=add(tmp,f[i][l][r][op0][op1]);
    					}
    				}
    			}
    		}
    	}
    	printf("%d
    ",ret);
    	return 0;
    }
    

    (O(n^3))

    #include<bits/stdc++.h>
    typedef int LL;
    typedef long long ll;
    typedef double dl;
    #define opt operator
    #define pb push_back
    #define pii std::pair<LL,LL>
    const LL maxn=151,mod=1e9+7,inf=0x3f3f3f3f;
    LL Read(){
    	LL x(0),f(1); char c=getchar();
    	while(c<'0' || c>'9'){
    		if(c=='-') f=-1; c=getchar();
    	}
    	while(c>='0' && c<='9'){
    		x=(x<<3ll)+(x<<1ll)+c-'0'; c=getchar();
    	}return x*f;
    }
    void Chkmin(LL &x,LL y){
    	if(y<x) x=y;
    }
    void Chkmax(LL &x,LL y){
    	if(y>x) x=y;
    }
    LL add(LL x,LL y){
    	return x+=y,x>=mod?x-mod:x;
    }
    LL dec(LL x,LL y){
    	return x-=y,x<0?x+mod:x;
    }
    LL mul(LL x,LL y){
    	return 1ll*x*y%mod;
    }
    LL Pow(LL base,LL b){
    	LL ret(1); while(b){
    		if(b&1) ret=mul(ret,base); base=mul(base,base); b>>=1;
    	}return ret;
    }
    LL n;
    LL mark[maxn][maxn];
    ll g[2][2][maxn][maxn],f[maxn][maxn][maxn][2][2];
    char s[maxn][maxn];
    bool Check(LL l1,LL r1,LL l2,LL r2){
    	return std::max(l1,l2)<=std::min(r1,r2);
    }
    void Fir(LL nw,LL op0,LL op1){
    	memset(g[op0][op1],0,sizeof(g[op0][op1]));
    	for(LL i=1;i<=n;++i){
    		for(LL j=1;j<=n;++j){
    			ll ret(g[op0][op1][i-1][j]+g[op0][op1][i][j-1]-g[op0][op1][i-1][j-1]+f[nw][i][j][op0][op1]);
    			g[op0][op1][i][j]=(ret%mod+mod)%mod;
    		}
    	}
    }
    ll Query(LL op0,LL op1,LL l_l,LL l_r,LL r_l,LL r_r){
    	ll ret(g[op0][op1][l_r][r_r]-g[op0][op1][l_l-1][r_r]-g[op0][op1][l_r][r_l-1]+g[op0][op1][l_l-1][r_l-1]);
    	return ret;
    }
    void Fir(LL nw){
    	for(LL op0=0;op0<2;++op0){
    		for(LL op1=0;op1<2;++op1){
    			Fir(nw,op0,op1);
    		}
    	}
    }
    int main(){
    	double ret1(clock());
    	n=Read();
    	for(LL i=1;i<=n;++i){
    		scanf(" %s",s[i]+1);
    	}
    	LL ret(0);
    	for(LL i=1;i<=n;++i){
    		memset(mark,0,sizeof(mark));
    		for(LL l=1;l<=n;++l){
    			for(LL r=l;r<=n && s[i][r]=='G';++r){
    				f[i][l][r][1][1]=1;
    				mark[l][r]=1;
    			}
    		}
    		Fir(i-1);
    		for(LL l1=1;l1<=n;++l1){
    			for(LL r1=l1;r1<=n;++r1)if(mark[l1][r1]){
    				for(LL ff1=0;ff1<2;++ff1){
    					for(LL ff2=0;ff2<2;++ff2){
    						for(LL flag1=0;flag1<2;++flag1){
    							if(!flag1 && ff1) continue;
    							for(LL flag2=0;flag2<2;++flag2){
    								if(!flag2 && ff2) continue;
    								LL l_l(1),l_r(n);
    								LL r_l(1),r_r(n);
    								if(ff1) Chkmax(l_l,l1+1); else Chkmin(l_r,l1);
    								if(flag1) Chkmax(l_l,l1); else Chkmin(l_r,l1-1);
    								if(ff2) Chkmin(r_r,r1-1); else Chkmax(r_l,r1);
    								if(flag2) Chkmin(r_r,r1); else Chkmax(r_l,r1+1);
    								Chkmin(l_r,r1);
    								Chkmax(r_l,l1);
    								if(l_l>l_r || r_l>r_r) continue;
    								for(LL op0=0;op0<2;++op0)if((op0&ff1)==ff1){
    							        for(LL op1=0;op1<2;++op1)if((op1&ff2)==ff2){
    								        f[i][l1][r1][op0&flag1][op1&flag2]+=Query(op0,op1,l_l,l_r,r_l,r_r);
    							        }
    						        }
    							}
    						}
    					}
    				}
    			}
    		}
    		for(LL l=1;l<=n;++l){
    			for(LL r=l;r<=n;++r){
    				for(LL op0=0;op0<2;++op0){
    					for(LL op1=0;op1<2;++op1){
    						ll &x(f[i][l][r][op0][op1]);
    						x=(x%mod+mod)%mod;
    					}
    				}
    			}
    		}
    		for(LL l=1;l<=n;++l){
    			for(LL r=l;r<=n;++r){
    				for(LL op0=0;op0<2;++op0){
    					for(LL op1=0;op1<2;++op1){
    						ret=add(ret,f[i][l][r][op0][op1]);
    					}
    				}
    			}
    		}
    	}
    	printf("%d
    ",ret);
    	return 0;
    }
    
  • 相关阅读:
    线程的两种实现方法
    wait() 与 sleep
    【BZOJ4566】找相同字符(后缀数组)
    【BZOJ2555】SubString(后缀自动机,Link-Cut Tree)
    【HDU4622】Reincarnation(后缀自动机)
    【SPOJ】Substrings(后缀自动机)
    【SPOJ】Longest Common Substring II (后缀自动机)
    【SPOJ】Longest Common Substring(后缀自动机)
    【Luogu3804】【模板】后缀自动机(后缀自动机)
    【SPOJ】Distinct Substrings(后缀自动机)
  • 原文地址:https://www.cnblogs.com/Grice/p/14632100.html
Copyright © 2020-2023  润新知