USACO 2021 US Open

题外话

写篇题解攒rp

A

题意:
给定长度为\(n\)的序列\(\{A_i\}\),一组\((x,y,z)(1\le x<y<z\le 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\)
  • \(x\in (last_{last_i},last_i)的\)\(B_x-1\);对\(x\in(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\n",ret);
	return 0;
}

B

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

做法

建立超级源汇点\(U\)

对于每个起点\(i\)\(U\longrightarrow i\)
对于每个终点\(i\)\(i\longrightarrow U\)

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

对于有向图欧拉回路计数,可以使用\(\text{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\n",ret);
}
void Init1(){
	scanf("%d",&T);
	while(T--){
		Init2();
		Solve();
	}
}
int main(){
	Init1();
	return 0;
}

C

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

做法

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

对于\(i\in[x,y)\)\((l_i,r_i)\)应与\((l_{i+1},r_{i+1})\)有交。
\(l_i(i\in[x,y])\)应先递减再递增(非严格)
\(r_i(i\in[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\n",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\n",ret);
	return 0;
}
posted @ 2021-04-08 14:37  Grice  阅读(140)  评论(0编辑  收藏  举报