把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

九省联考 2018

一双木棋

状压轮廓线,\(0\)代表向上,\(1\)代表水平走,暴力转移。

时间复杂度\(O(C_{n+m}^n(n+m))\)反正到不了\(O(2^{n+m}(n+m))\)

code:

#include<bits/stdc++.h>
#define Gc() getchar() 
#define Me(x,y) memset(x,y,sizeof(x))
#define Mc(x,y) memcpy(x,y,sizeof(x))
#define d(x,y) ((m)*(x-1)+(y))
#define R(n) (rnd()%(n))
#define Pc(x) putchar(x)
#define LB lower_bound
#define UB upper_bound
#define PB push_back
using ll=long long;using db=double;using lb=long db;using ui=unsigned;using ull=unsigned ll;
using namespace std;const int N=10+5,M=(1<<20)+5,K=1e5+5,mod=1e9+7,Mod=mod-1,INF=2e9+7;const db eps=1e-5;mt19937 rnd(263082);
int n,m,k,x,y,px,py,A[N][N],B[N][N],f[M];
int main(){
	int i,j;scanf("%d%d",&n,&m);k=(1<<n+m);for(i=1;i<=n;i++) for(j=1;j<=m;j++) scanf("%d",&A[i][j]);for(i=1;i<=n;i++) for(j=1;j<=m;j++) scanf("%d",&B[i][j]);
	Me(f,-0x3f);f[((1<<m)-1)<<n]=0;for(i=k-1;~i;i--) {
		int Ct=0;for(j=0;j<n+m;j++) Ct+=(i>>j&1);if(Ct!=m) continue;
		x=m;y=0;for(j=0;j<n+m;j++){
			if(i>>j&1) x--;else y+=x;
		}y&=1;
		px=0;py=m;for(j=0;j<n+m-1;j++){
			if(i>>j&1) py--;else px++;
			if(i>>(j+1)&1&&!(i>>j&1)){
				f[i^(1<<j)^(1<<j+1)]=max(f[i^(1<<j)^(1<<j+1)],-f[i]+(y&1?A[px][py]:B[px][py]));
			}
		}
	}printf("%d\n",f[(1<<m)-1]);
}

IIIDX

写过了,不想写。

秘密袭击

三方碾标算是smg。

首先你显然可以写一个三方暴力出来,然后思考怎么优化。

你发现这个转移的形式是直接卷积,也就是说如果我们能先算\(n+1\)个点值出来然后最后插回去其实就好了。

拉格朗日插值公式\(f(k)=\sum\limits_{i=1}^{n}{y_i\prod\limits_{j\not =i}{\frac{k-j}{i-j}}}\)后面那部分可以暴力乘出来然后算贡献。

外围枚举当前算哪个数对应的点值,然后你发现直接暴力转移还是\(O(n^2)\)的合起来和暴力没区别。

其实实际上你只需要支持区间加,然后把两个数组每个点对应乘起来,然后对每个位置有一个权值算总和。

这个写个线段树合并就好了。时间复杂度\(O(n^2\log n)\)

code:

#include<bits/stdc++.h>
#define Gc() getchar() 
#define Me(x,y) memset(x,y,sizeof(x))
#define Mc(x,y) memcpy(x,y,sizeof(x))
#define d(x,y) ((m)*(x-1)+(y))
#define R(n) (rnd()%(n))
#define Pc(x) putchar(x)
#define LB lower_bound
#define UB upper_bound
#define PB push_back
using ll=long long;using db=double;using lb=long db;using ui=unsigned;using ull=unsigned ll;
using namespace std;const int N=1666+5,M=N*100+5,K=500+5,mod=64123,Mod=mod-1,INF=2e9+7;const db eps=1e-5;mt19937 rnd(263082);
int n,m,k,x,y,z,Si[N],A[N],B[N];ll Ans;vector<int> S[N];
//void GA(int x,int La,int w){
//	int i,j,h;Me(dp[x],0);dp[x][Si[x]=(A[x]>=B[w])]=1;for(int i:S[x]) if(i^La){
//		GA(i,x,w);Mc(g,dp[x]);for(j=min(Si[x],k);~j;j--){
//			for(h=min(Si[i],k);~h;h--) dp[x][min(j+h,k)]=(dp[x][min(j+h,k)]+dp[i][h]*g[j])%mod;
//		}Si[x]+=Si[i];
//	}Ans+=dp[x][k]*(B[w]-B[w-1]);
//}
ll mpow(ll x,int y=mod-2){ll Ans=1;while(y) y&1&&(Ans=Ans*x%mod),y>>=1,x=x*x%mod;return Ans;}
ll f1[N][N],f2[N][N];int Rt[N];
namespace Tree{
	int Ct,L[M],R[M];ll f[M],g1[M],g2[M];void Up(int v){f[v]=(f[L[v]]+f[R[v]])%mod;}int Nw(){g2[++Ct]=1;return Ct;}
	void Cl(){int i;for(i=1;i<=n;i++) Rt[i]=0;for(i=1;i<=Ct;i++) L[i]=R[i]=f[i]=g1[i]=g2[i]=0;Ct=0;}
	void P1(int &v,int l,int r,int w){if(!v) v=Nw();g1[v]+=w;f[v]=(f[v]+(B[r]-B[l-1])*w)%mod;}
	void P2(int &v,int w){if(!v) v=Nw();g2[v]=g2[v]*w%mod;f[v]=f[v]*w%mod;g1[v]=g1[v]*w%mod;}
	void P(int v,int l,int r){int m=l+r>>1;g2[v]^1&&(P2(L[v],g2[v]),P2(R[v],g2[v]),g2[v]=1);g1[v]&&(P1(L[v],l,m,g1[v]),P1(R[v],m+1,r,g1[v]),g1[v]=0);}
	void Ins(int x,int y,int z,int &v,int l=1,int r=n){if(x>y) return;!v&&(v=Nw());if(x<=l&&r<=y) return P1(v,l,r,z);int m=l+r>>1;P(v,l,r);x<=m&&(Ins(x,y,z,L[v],l,m),0);y>m&&(Ins(x,y,z,R[v],m+1,r),0);Up(v);}
	int Merge(int x,int y,int l=1,int r=n){
		if(!x||!y) return x|y;int m=l+r>>1;
		if(!L[x]&&!R[x]) return P2(y,g1[x]),y;
		if(!L[y]&&!R[y]) return P2(x,g1[y]),x;P(x,l,r);P(y,l,r);
		L[x]=Merge(L[x],L[y],l,m);R[x]=Merge(R[x],R[y],m+1,r);return Up(x),x;
	}
	void print(int v,int l=1,int r=n){if(l==r) {printf("%d ",f[v]);return;}int m=l+r>>1;P(v,l,r);print(L[v],l,m);print(R[v],m+1,r);}
}
void GA(int x,int La,int w,int Id){
//	int i,j,h;for(i=1;i<=n;i++) dp[x][i]=(A[x]>=B[i]?Id:1);for(int i:S[x]) if(i^La){
//		GA(i,x,w,Id);for(j=1;j<=n;j++) dp[x][j]=dp[x][j]*(dp[i][j]+1)%mod;
//	} for(i=1;i<=n;i++) Ans+=dp[x][i]*(B[i]-B[i-1])*w%mod;
	int i,j,h;int p=UB(B+1,B+n+1,A[x])-B-1;Tree::Ins(1,p,Id,Rt[x]);Tree::Ins(p+1,n,1,Rt[x]);for(int i:S[x]) if(i^La){
		GA(i,x,w,Id);Tree::P1(Rt[i],1,n,1);Rt[x]=Tree::Merge(Rt[x],Rt[i]);
		//printf("%d\n",x);Tree::print(Rt[x]);Pc('\n');
	}Ans+=Tree::f[Rt[x]]*w%mod;//printf("%d\n",x);Tree::print(Rt[x]);Pc('\n');
}
void calc(){
	int i,j,h;f1[0][0]=1;for(i=1;i<=n+1;i++){
		ll P=mod-i;for(j=0;j<=n+1;j++) f1[i][j]=(f1[i-1][j]*P+(j?f1[i-1][j-1]:0))%mod;
	}f2[n+2][0]=1;for(i=n+1;i;i--){
		ll P=mod-i;for(j=0;j<=n+1;j++) f2[i][j]=(f2[i+1][j]*P+(j?f2[i+1][j-1]:0))%mod;
	}
	for(i=n+2;i;i--) for(j=n+1;~j;j--) f2[i][j]=(f2[i][j+1]+f2[i][j])%mod;
	for(i=1;i<=n+1;i++){
		ll T1=0,T2=1;for(j=1;j<=n+1;j++) i^j&&(T2=T2*(i-j+mod)%mod);
		for(j=0;j<=n;j++) T1+=f1[i-1][j]*f2[i+1][max(k-j,0)]%mod;T1=T1%mod*mpow(T2)%mod;
		Tree::Cl();GA(1,0,T1,i);
	}
}
int main(){
	int i,j;scanf("%d%d%d",&n,&k,&m);for(i=1;i<=n;i++) scanf("%d",&A[i]),B[i]=A[i];sort(B+1,B+n+1);
	for(i=1;i<n;i++) scanf("%d%d",&x,&y),S[x].PB(y),S[y].PB(x);calc();printf("%lld\n",Ans%mod);
}

劈配

发现是一个网络流类似物,第一问直接暴力加边并查集网络流就可以做到\(O(n^4)\)

然后发现这个东西其实不优,中间一步直接二分可以做到\(O(n^3\log n)\)

但是这个每个点走出的边数不超过\(C\),所以其实暴力的复杂度是\(O(n^3c)\)的!

第二问就是在第一问的基础上直接暴力网络流复杂度也是\(O(n^3c)\)的。

具体就是对于每个导师算他少一个出边能撑到什么时候。

所以总复杂度看你怎么写,\(O(n^3c),O(n^2c\log n),O(n^3\log n)\)都可以写。

code:

#include<bits/stdc++.h>
#define Gc() getchar() 
#define Me(x,y) memset(x,y,sizeof(x))
#define Mc(x,y) memcpy(x,y,sizeof(x))
#define d(x,y) ((m)*(x-1)+(y))
#define R(n) (rnd()%(n))
#define Pc(x) putchar(x)
#define LB lower_bound
#define UB upper_bound
#define PB push_back
using ll=long long;using db=double;using lb=long db;using ui=unsigned;using ull=unsigned ll;
using namespace std;const int N=2e2+5,M=4e2+5,K=1e5+5,mod=1e9+7,Mod=mod-1,INF=2e9+7;const db eps=1e-5;mt19937 rnd(263082);
int n,m,k,x,y,z,C,A[N],B[N],S,T,P[N],To[N];vector<int> Id[N][N];
struct yyy{int to,w,z;};struct ljb{int head,h[M];yyy f[N*N*2];void Cl(){head=1;Me(h,0);}void add(int x,int y,int z){f[++head]=(yyy){y,z,h[x]};h[x]=head;}}s,g;
void con(int x,int y,int z){s.add(x,y,z);s.add(y,x,0);}
namespace Dicnic{
	int d[M],Ns[M];queue<int> Q;
	int BFS(){
		while(!Q.empty()) Q.pop();Me(d,0x3f);Me(Ns,0);Q.push(S);d[S]=0;Ns[S]=s.h[S];
		while(!Q.empty()){
			int x=Q.front();Q.pop();yyy tmp;for(int i=s.h[x];i;i=tmp.z){
				tmp=s.f[i];if(!tmp.w||d[tmp.to]<=d[x]+1) continue;
				d[tmp.to]=d[x]+1;Q.push(tmp.to);Ns[tmp.to]=s.h[tmp.to];if(tmp.to==T) return 1;
			}
		}return 0;
	}
	int DFS(int x,int sum){
		if(x==T) return sum;int pus=0,k;yyy tmp;
		for(int i=Ns[x];i;i=tmp.z){
			Ns[x]=i;tmp=s.f[i];if(!tmp.w||d[tmp.to]!=d[x]+1) continue;
			k=DFS(tmp.to,min(sum,tmp.w));if(!k) d[tmp.to]=1e9;
			s.f[i].w-=k;s.f[i^1].w+=k;
			sum-=k;pus+=k;if(!sum) break;
		} return pus;
	}
	int calc(){int Ans=0;while(BFS()) Ans+=DFS(S,1e9);return Ans;}
}
void Solve(){
	int i,j,h;scanf("%d%d",&n,&m);s.Cl();S=0;T=n+m+1;for(i=1;i<=m;i++) scanf("%d",&B[i]),con(i+n,T,B[i]);
	for(i=1;i<=n;i++) {
		for(j=1;j<=m;j++) Id[i][j].clear();
		for(j=1;j<=m;j++) scanf("%d",&x),Id[i][x].PB(j);
	}
	for(i=1;i<=n;i++){con(S,i,1);
		int l=0,r=m+1,mid;g=s;
		while(l+1<r){
			mid=l+r>>1;
			s=g;for(j=1;j<=mid;j++) {
				for(int h:Id[i][j]) con(i,h+n,1);
			}
			(Dicnic::calc()?r:l)=mid; 
		}P[i]=r;
		s=g;if(P[i]!=m+1){
			for(j=1;j<=P[i];j++){
				for(int h:Id[i][j]) con(i,h+n,1);
			}
		}Dicnic::calc();
		/*P[i]=m+1;for(j=1;j<=m;j++) {
			for(int h:Id[i][j]) con(i,h+n,1);
			if(Dicnic::calc()) {P[i]=j;break;}
		}*/printf("%d ",P[i]);
	}Pc('\n');
	for(i=1;i<=m;i++){
		s.Cl();for(j=1;j<=m;j++) con(j+n,T,B[j]-(i==j));
		To[i]=n;for(j=1;j<=n;j++){if(P[j]==m+1)continue;con(S,j,1);
			for(h=1;h<=P[j];h++) for(int p:Id[j][h]) con(j,p+n,1);
			if(!Dicnic::calc()) {To[i]=j-1;break;}
		}
	}
	for(i=1;i<=n;i++){
		scanf("%d",&x);int Ts=i;
		for(j=1;j<=x;j++){
			for(int h:Id[i][j]) Ts=min(Ts,i-To[h]-1);
		}printf("%d ",max(Ts,0));
	}Pc('\n');
}
int main(){
	freopen("mentor.in","r",stdin);freopen("mentor.out","w",stdout);
	int t;scanf("%d%d",&t,&C);while(t--) Solve();
} 

林克卡特树

WQS典题,不想讲。

制胡窜

我的评价是,出题人可以司马了。

首先考虑暴力怎么做,暴力是不是,先把所有出现的位置拉出来,然后容斥成两条线切到了所有子串。

对于两个排序后最近的子串的起点\(B_i,B_{i+1}\),对答案的贡献是\(\max(0,\min(B_{i+1},B_1+len-1)-B_i)\times \max(0,B_{i+1}+len-1-B_n)\)

然后把这些什么\(\min\)\(\max\)的全拆了可以发现困难的是\((B_{i+1}-B_i)B_{i+1}\)。SAM上线段树合并即可。

code:

#include<bits/stdc++.h>
#define Gc() getchar() 
#define Me(x,y) memset(x,y,sizeof(x))
#define Mc(x,y) memcpy(x,y,sizeof(x))
#define d(x,y) ((m)*(x-1)+(y))
#define R(n) (rnd()%(n))
#define Pc(x) putchar(x)
#define LB lower_bound
#define UB upper_bound
#define PB push_back
using ll=long long;using db=double;using lb=long db;using ui=unsigned;using ull=unsigned ll;
using namespace std;const int N=2e5+5,M=1e7+5,K=1e5+5,mod=1e9+7,Mod=mod-1;const ll INF=1e18+7;const db eps=1e-5;mt19937 rnd(263082);
int n,m,k,x,y,z,Pl[N];char c[N];
struct Node{ll Sum;int L,R;Node operator +(const Node &B)const{if(!L) return B;if(!B.L) return (Node){Sum,L,R};return (Node){Sum+B.Sum+1ll*(B.L-R)*B.L,L,B.R};};}Cl;
namespace Tree{
	int f[M],L[M],R[M],Ct;Node g[M];void Up(int v){g[v]=g[L[v]]+g[R[v]];}
	void Ins(int x,int &v,int l=1,int r=n){!v&&(v=++Ct);f[v]++;if(l==r){g[v]=(Node){0,l,l};return;}int m=l+r>>1;x<=m?Ins(x,L[v],l,m):Ins(x,R[v],m+1,r);Up(v);}
	int Merge(int x,int y){if(!x||!y) return x|y;int p=++Ct;f[p]=f[x]+f[y];L[p]=Merge(L[x],L[y]);R[p]=Merge(R[x],R[y]);return Up(p),p;}
	int Qry(int x,int y,int &v,int l=1,int r=n){if(x>y) return 0;if(x<=l&&r<=y) return f[v];int m=l+r>>1;return (x<=m?Qry(x,y,L[v],l,m):0)+(y>m?Qry(x,y,R[v],m+1,r):0);}
	int RK(int x,int v,int l=1,int r=n){if(l==r) return l;int m=l+r>>1;return f[L[v]]<x?RK(x-f[L[v]],R[v],m+1,r):RK(x,L[v],l,m);}
	Node Find(int x,int y,int v,int l=1,int r=n){
		if(!v||y<=0||x>f[v]) return Cl;if(x<=1&&y>=f[v]) return g[v];
		int m=l+r>>1;return Find(x,y,L[v],l,m)+Find(x-f[L[v]],y-f[L[v]],R[v],m+1,r);
	}
	//void print(int x,int y,int v,int &Bh,int *B,int l=1,int r=n){if(!v||y<=0||x>f[v]) return;if(l==r){B[++Bh]=l;return;}int m=l+r>>1;print(x,y,L[v],Bh,B,l,m);print(x-f[L[v]],y-f[L[v]],R[v],Bh,B,m+1,r);}
}
namespace SAM{
	int Ct,La,son[N][10],Le[N],Lk[N],d[N],fa[N][20],Rt[N],Mx[N],Mi[N],Id[N];vector<int> S[N];
	void BD(){Ct=La=1;Le[1]=0;Lk[1]=-1;}
	void Ins(int c){
		Le[++Ct]=Le[La]+1;Lk[Ct]=La;int p=La;La=Ct;
		while(~p&&!son[p][c]) son[p][c]=Ct,p=Lk[p];if(p==-1) {Lk[Ct]=1;return;}
		int q=son[p][c];if(Le[q]==Le[p]+1) {Lk[Ct]=q;return;}
		int P=++Ct;Mc(son[P],son[q]);Le[P]=Le[p]+1;Lk[P]=Lk[q];Lk[q]=Lk[La]=P;
		while(~p&&son[p][c]==q) son[p][c]=P,p=Lk[p];
	}
	int B[N],Bh;
	void Make(int x,int La){
		d[x]=d[fa[x][0]=La]+1;Id[x]?(Mx[x]=Mi[x]=Id[x]):(Mx[x]=-1e9,Mi[x]=1e9);
		for(int i=1;fa[x][i-1];i++) fa[x][i]=fa[fa[x][i-1]][i-1];
		for(int i:S[x]) Make(i,x),Rt[x]=Tree::Merge(Rt[x],Rt[i]),Mx[x]=max(Mx[x],Mx[i]),Mi[x]=min(Mi[x],Mi[i]);
	}
	void RB(){for(int i=2;i<=Ct;i++) S[Lk[i]].PB(i);Make(1,0);}
	ll Qry(int x,int y){
		ll Ans=1ll*(n-2)*(n-1)/2;int Si=y-x+1;
		int p=Pl[x];for(int i=19;~i;i--) Le[fa[p][i]]>=Si&&(p=fa[p][i]);
//		while(Le[Lk[p]]>=Si) p=Lk[p];
		if(Tree::f[Rt[p]]>2*Si) return Ans;
		int B1=Mi[p],Bn=Mx[p],Bh=Tree::f[Rt[p]];
		int LL=max(Tree::Qry(1,max(Bn-Si+1,0),Rt[p]),1),RR=0,i;
//		for(i=1;i<Bh;i++) if(B[i+1]>B[Bh]-Si+1){LL=i;break;}
		RR=min(Tree::Qry(1,B1+Si-1,Rt[p]),Bh-1)-1;
		if(RR>=0){
			int P1=Tree::RK(RR+1,Rt[p]),P2=Tree::RK(RR+2,Rt[p]);//cerr<<P1<<' '<<P2<<'\n';
			Ans-=1ll*(min(P2,B1+Si-1)-P1)*max(P2+Si-1-Bn,0);
		}
		Node PP=Tree::Find(LL,RR+1,Rt[p]);if(LL<=RR+1)Ans-=PP.Sum+(PP.R-PP.L)*(Si-1-Bn);
//		for(i=1;i<Bh;i++) Ans-=1ll*(B[i+1]-B[i])*(B[i+1]+Si-1-Bn);
		if(B1+Si-1>Bn){
			Ans-=1ll*(B1-1)*(B1+Si-1-Bn);
			int L=Bn,R=B1+Si-2;Ans-=1ll*(n-L-1+n-R-1)*(R-L+1)/2;
		}return Ans;
	}
}
int main(){
	freopen("cutting.in","r",stdin);freopen("cutting.out","w",stdout);
	int i,j;scanf("%d%d",&n,&m);scanf("%s",c+1);SAM::BD();
	for(i=n;i;i--) SAM::Ins(c[i]-'0'),Tree::Ins(i,SAM::Rt[SAM::La]),SAM::Id[SAM::La]=i,Pl[i]=SAM::La;
	SAM::RB();while(m--)scanf("%d%d",&x,&y),printf("%lld\n",SAM::Qry(x,y));
}
posted @ 2022-12-20 13:59  275307894a  阅读(43)  评论(0)    收藏  举报
浏览器标题切换
浏览器标题切换end