1. 程式人生 > 其它 >【考試總結】2022-05-10

【考試總結】2022-05-10

簡單題

使用能維護這個區間歷史被加了多少的線段樹即可,在每個 \(a_l=a_{l+1}=\dots=a_r\) 的線段上對 \(b\) 進行加法即可

Code Display
const int N=5e5+10;
int len[N<<2],tag[N<<2],cov[N<<2],b[N],n,m,Q;
signed main(){
	freopen("easy.in","r",stdin); freopen("easy.out","w",stdout);
	n=read(); m=read(); Q=read();
	#define ls p<<1
	#define rs p<<1|1
	#define lson p<<1,l,mid
	#define rson p<<1|1,mid+1,r
	auto push_add=[&](const int p,const int v){
		if(~cov[p])	b[cov[p]]+=len[p]*v;
		else tag[p]+=v;
	};
	auto push_down=[&](const int p){
		if(tag[p]){
			push_add(ls,tag[p]);
			push_add(rs,tag[p]);
			tag[p]=0;
		}
		if(~cov[p]){
			cov[ls]=cov[p];
			cov[rs]=cov[p];
			cov[p]=-1;
		}
	};
	function<void(int,int,int)>build=[&](const int p,const int l,const int r){
		len[p]=r-l+1; cov[p]=-1;
		if(l==r) return cov[p]=read(),void();
		int mid=(l+r)>>1;
		build(lson); build(rson);
	};
	build(1,1,n);
	while(Q--){
		int opt=read(),st=read(),ed=read(),v=read();
		function<void(int,int,int)> Plus=[&](int p,int l,int r){
			if(st<=l&&r<=ed) return push_add(p,v);
			int mid=(l+r)>>1; push_down(p);
			if(st<=mid) Plus(lson); if(ed>mid) Plus(rson);
			return ;	
		};
		function<void(int,int,int)> Cover=[&](int p,int l,int r){
			if(st<=l&&r<=ed) return cov[p]=v,void();
			int mid=(l+r)>>1; push_down(p);
			if(st<=mid) Cover(lson); if(ed>mid) Cover(rson);
			return ;
		};
		if(opt==1) Cover(1,1,n); else Plus(1,1,n);
	}
	function<void(int,int,int)>dfs=[&](const int p,const int l,const int r){
		if(l==r) return ; 
		int mid=(l+r)>>1; push_down(p);
		dfs(lson); dfs(rson);
		return ;
	};
	dfs(1,1,n);
	rep(i,1,m) print(b[i]);
	#undef ls
	#undef rs
	#undef lson
	#undef rson
	return 0;
}

樹上游走

\(f_{x}\) 表示第一次經過 \(x\)\(x\) 的子孫都沒有經過的,父親也已經消失的情況下得到的期望權值,\(g_x\) 表示同樣狀況下第二次經過 \(x\) 得到的期望權值

\(h_{x}\) 表示第一次經過 \(x\)\(x\) 的父親依然存在,不經過 \(x\) 的父親得到的期望答案,而 \(p_x\) 表示不返回父親的概率

\(g_x\) 的轉移是平凡的,列舉走到了哪個兒子使用 \(f_{son}\) 轉移即可

剩下三種轉移考慮走到兒子之後的處境:

  • 走到子樹裡面不再回頭,分別使用 \(h_{son},p_{son}\) 來轉移即可

  • 走到兒子節點立刻返回,此時仍然可以走到任何一個兒子,不過先前走的再走就得用 \(g_{son}\)

    轉移了

  • 走到兒子子樹的另一節點再返回,區別在於返回之後兒子不存在了,概率上的處理要減掉上一個情況發生的 \(\frac{1}{deg_{son}}\) 以及不再返回的概率

    而這個情況在只有一個出邊的時候的 \(f\) 轉移需要用到自己的權值

邊界就是 \(f_x=g_x=a_x,p_x=h_x=0\)

Code Display
const int N=1e5+10;
int inv[N],f[N],g[N],h[N],p[N];
int n,S,a[N];
vector<int> G[N];
signed main(){
	freopen("walk.in","r",stdin); freopen("walk.out","w",stdout);
	n=read(); S=read();
	for(int i=inv[1]=inv[0]=1;i<=n;++i) a[i]=read();
	rep(i,2,n){
		inv[i]=mod-mul(mod/i,inv[mod%i]);
		int u=read(),v=read();
		G[u].emplace_back(v);
		G[v].emplace_back(u);
	}
	function<void(int,int)>solve=[&](int x,int fat){
		int deg=0,sumf=0;
		for(auto t:G[x]) if(t!=fat){
			deg++;
			solve(t,x);
			ckadd(sumf,f[t]);
		}
		if(!deg){
			f[x]=g[x]=a[x];
			return ;
		}
		for(auto t:G[x]) if(t!=fat){
			ckadd(f[x],h[t]);
			int dt=G[t].size()-1;
			int down=del(1,add(p[t],inv[dt+1])),oth=del(sumf,f[t]);
			ckadd(f[x],mul(mul(inv[deg],inv[dt+1]),add(g[t],oth)));
			if(deg==1){
				ckadd(f[x],mul(a[x],down));
			}else{
				ckadd(f[x],mul(down,mul(inv[deg-1],oth)));	
			}
			ckadd(g[x],f[t]);

			ckadd(h[x],h[t]);
			ckadd(p[x],p[t]);
			
			ckadd(h[x],mul(mul(inv[dt+1],inv[deg+1]),add(g[t],oth)));
			ckadd(p[x],mul(mul(inv[dt+1],inv[deg+1]),deg));
			
			ckadd(h[x],mul(down,mul(inv[deg],oth)));
			ckadd(p[x],mul(down,mul(inv[deg],deg-1)));		
		}
		ckmul(f[x],inv[deg]);
		ckmul(g[x],inv[deg]);
		ckmul(p[x],inv[deg+1]);
		ckmul(h[x],inv[deg+1]);
		return ;
	};
	solve(S,0);
	print(f[S]);
	return 0;
}

樹的同構

列舉每個點作為根作為 \(S\) 點集中深度最小的點,再列舉此時有根樹上的另外一個點作為點集 \(T\) 中深度最小的點

\(f_{x,y}\) 表示 \(x\) 子樹和 \(y\) 子樹中包含根的聯通塊同構最多能在聯通塊中包含幾個點

轉移考慮計算出來所有 \(f_{u,v}[u\in son_x,v\in son_y]\),那麼可以進行一個二分圖最大帶權匹配即可,使用費用流時複雜度 \(\Theta(poly(n))\)

Code Display
const int N=500,inf=0x3f3f3f3f3f3f3f3f;
struct Network_Flow{
	int incf[N],pre[N],dst[N];
	bool inq[N];
	int S,T,ecnt=1,tot,head[N];
	struct edge{int to,nxt,lim,cst;}e[N<<3];
	inline bool spfa(){
		for(int i=1;i<=tot;++i) incf[i]=0,dst[i]=-inf; 
		dst[S]=0; incf[S]=inf;
		queue<int> q; q.push(S);
		while(q.size()){
			int fr=q.front(); q.pop(); inq[fr]=0;
			for(int i=head[fr];i;i=e[i].nxt) if(e[i].lim){
				int t=e[i].to; 
				if(dst[fr]+e[i].cst>dst[t]){
					dst[t]=dst[fr]+e[i].cst; 
					incf[t]=min(incf[fr],e[i].lim); 
					pre[t]=i;
					if(!inq[t]) inq[t]=1,q.push(t);
				}
			}
		}
		return dst[T]!=-inf;
	}
	inline void adde(int u,int v,int w,int c){
		e[++ecnt]={v,head[u],w,c};
		head[u]=ecnt;
	}
	inline void add(int u,int v,int w,int c){return adde(u,v,w,c),adde(v,u,0,-c);}
	inline void clear(){
		rep(i,1,tot) head[i]=0;
		ecnt=1; tot=0;
	}
	inline int EK(){
		int sum=0;
		while(spfa()){
			int x=T;
			while(x^S){
				e[pre[x]].lim-=incf[T]; e[pre[x]^1].lim+=incf[T];
				x=e[pre[x]^1].to;
			}
			sum+=dst[T];
		}
		return sum;
	}
}F;
int u[N],v[N],n,fa[N];
vector<int> G[N];
int dp[N][N],id[N];

signed main(){
	freopen("isomorphism.in","r",stdin); freopen("isomorphism.out","w",stdout);
	while(~scanf("%lld",&u[++n])) ++u[n];
	for(int i=n/2+1;i<=n;++i) v[i-n/2]=u[i];
	n=n/2+1;
	for(int i=1;i<n;++i) G[u[i]].emplace_back(v[i]),G[v[i]].emplace_back(u[i]);
	int ban,ans=0;
	function<void(int,int)>get_fa=[&](const int x,const int fat){
		fa[x]=fat;
		for(auto t:G[x]) if(t!=fat) get_fa(t,x);
		return ;
	};
	function<void(int,int)>DP=[&](const int x,const int y){
		if(~dp[x][y]) return ;
		if(x==ban){
			dp[x][y]=0;
			return ;
		}
		for(auto a:G[x]) if(a!=fa[x]&&a!=ban) for(auto b:G[y]) if(b!=fa[y]) DP(a,b);
		F.clear(); 
		F.S=++F.tot; F.T=++F.tot;
		for(auto a:G[x]) if(a!=fa[x]&&a!=ban) F.add(F.S,id[a]=++F.tot,1,0);
		for(auto a:G[y]) if(a!=fa[y]) F.add(id[a]=++F.tot,F.T,1,0);
		for(auto a:G[x]) if(a!=fa[x]&&a!=ban){
			for(auto b:G[y]) if(b!=fa[y]){
				F.add(id[a],id[b],1,dp[a][b]);
			}
		}
		dp[x][y]=F.EK()+1;
		return ;
	};
	rep(i,1,n){
		memset(dp,-1,sizeof(dp));
		get_fa(i,0);
		rep(j,1,n) ban=j,DP(i,j);
		rep(j,1,n) ckmax(ans,dp[i][j]);
	}
	print(ans);
	return 0;
}