【2019集训队互测】公园(广义串并联图)(动态DP)

传送门


题解:

只提一下记个要用的性质,证明去集训队论文里面看。

  1. 满足题意限制的图称为广义串并联图。
  2. 任何一个广义串并联图,去掉重边之后边数不超过点数的两倍。
  3. 任何一个广义串并联图可以由如下的方式构造:初始只有一个点,每次可以选择 1)加一个点,并和图中原有点连一条边,2)选择两个直接相连的点,新加一个点与这两个点相连,并且可以选择是否删去原来连接这两个点的边。

当然广义串并联图也是平面图,不过这个性质这道题用不到。

而且广义串并联图上还可以出奇妙的 O ( n 2 ) O(n^2) O(n2) 异或高斯消元题(好几个月之前的idea了),等我退役之后可能会出出来吧。

那么就需要做的事情就很明显了,我们考虑对构造反着来,直到图只剩下一个节点即可。

需要支持的操作有1)删一个度数为 1 1 1 的点,2)缩一个度数为 2 2 2 的点并消去重边。

容易发现每个点维护两种颜色的权值,每条边要维护两边四种情况的权值,然后就可以DP了。

加上修改,考虑动态DP。

乍一看是没法用矩阵维护的,一条边的四个权值其实需要用 1 × 4 1\times 4 1×4 的向量而不是 2 × 2 2\times 2 2×2 的矩阵来维护。

然后随便维护一下动态DP就行了,算轻儿子之和分类讨论有点麻烦。


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const

namespace IO{

inline char gc(){
	static cs int Rlen=3e7+7;static char buf[Rlen],*p1,*p2;
	return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++; 
}template<typename T>T get_integer(){
	char c;bool f=false;while(!isdigit(c=gc()))f=c=='-';T x=c^48;
	while(isdigit(c=gc()))x=((x+(x<<2))<<1)+(c^48);return f?-x:x;
}inline int gi(){return get_integer<int>();}
inline ll gl(){return get_integer<ll>();}

char obuf[(int)(3e7+7)],*oh=obuf,ch[23];
template<typename T>void print(T a,char c){
	if(a<0)*oh++='-',a=-a;int tl=0;
	do ch[++tl]=a%10; while(a/=10);
	while(tl)*oh++=ch[tl--]^48;*oh++=c;
}struct obuf_flusher{~obuf_flusher(){fwrite(obuf,1,oh-obuf,stdout);}}Flusher;

}using IO::gi;
using IO::gl;
using IO::print;

using std::cerr;
using std::cout;
#define fi first
#define se second

template<typename T>void cmax(T &a,cs T &b){a<b?a=b:a;}

cs int N=1e5+7,M=N+N+1;

cs ll INF=1e12,I=-INF;

struct mat{
	ll a[4][4];int r,c;
	ll *operator[](int o){return a[o];}
	cs ll *operator[](int o)cs{return a[o];}
};

mat operator*(cs mat &A,cs mat &B){
	int r=A.r,m=A.c,c=B.c;
	assert(B.r==A.c);
	static mat C;C.r=r,C.c=c;
	for(int re i=0;i<r;++i)
		for(int re j=0;j<c;++j)
			C[i][j]=I; 
	for(int re i=0;i<r;++i)
		for(int re j=0;j<m;++j)if(A[i][j]>I)
		for(int re k=0;k<c;++k)
			cmax(C[i][k],A[i][j]+B[j][k]);
	return C;
}

struct Edge{int u,v,v0,v1;}E[M+N];

int n,m,rt;

void gmat(int u,mat &A){
	ll v0=E[u].v0,v1=E[u].v1;
	if(u<=n){
		A.r=2,A.c=1;A[0][0]=v0,A[1][0]=v1;
	}else {
		A.r=4,A.c=1;
		A[0][0]=A[3][0]=v0;
		A[1][0]=A[2][0]=v1;
	}
} 


namespace T{//Tree

cs int N=7e5+7;

int op[N],ch[N][3];
int sz[N],son[N],tg[N];
int el[N],nx[N];
void adde(int u,int v){
	nx[v]=el[u],el[u]=v;
}

int node(int o,int c1,int c2=0,int c3=0){
	int u=++rt;op[u]=o;
	if(c3){
		ch[u][2]=c3,adde(u,c3);sz[u]+=sz[c3];
		if(sz[son[u]]<sz[c3])son[u]=c3,tg[u]=3;
	}if(c2){
		ch[u][1]=c2,adde(u,c2);sz[u]+=sz[c2];
		if(sz[son[u]]<sz[c2])son[u]=c2,tg[u]=2;
	}if(c1){
		ch[u][0]=c1,adde(u,c1);sz[u]+=sz[c1];
		if(sz[son[u]]<sz[c1])son[u]=c1,tg[u]=1;
	}sz[u]++;return u;
}

int lsz[N],lc[N],rc[N],fa[N];
mat g[N],vl_son[N][2],prod[N];
int sm[N],st[N],tp,tg_son[N];

void calc_g(int u){
	mat &G=g[u];
	switch(op[u]){
		case 0:{gmat(u,G);break;}
		case 1:{
			auto &t=vl_son[u][0];
			ll a=t[0][0],b=t[1][0],c=t[2][0],d=t[3][0];
			G={a,I,I,I,I,b,I,I,I,I,c,I,I,I,I,d,4,4};
			break;
		}case 2:{
			G={0,I,I,I,I,I,0,I,I,0,I,I,I,I,I,0,4,4};
			break;
		}case 3:{
			switch(tg[u]){
				case 1:{
					auto &w=vl_son[u][0],&v=vl_son[u][1];
					G={
						w[0][0]+v[0][0],w[1][0]+v[2][0],I,I,
						w[0][0]+v[1][0],w[1][0]+v[3][0],I,I,
						I,I,w[0][0]+v[0][0],w[1][0]+v[2][0],
						I,I,w[0][0]+v[1][0],w[1][0]+v[3][0],4,4
					};
					break;
				}case 2:{
					auto &w=vl_son[u][0],&v=vl_son[u][1];
					G={
						w[0][0]+v[0][0],w[1][0]+v[2][0],I,I,
						w[0][0]+v[1][0],w[1][0]+v[3][0],I,I,
						w[2][0]+v[0][0],w[3][0]+v[2][0],I,I,
						w[2][0]+v[1][0],w[3][0]+v[3][0],I,I,4,2
					};
					break;
				}case 3:{
					auto &w=vl_son[u][0],&v=vl_son[u][1];
					G={
						w[0][0]+v[0][0],I,w[1][0]+v[1][0],I,
						I,w[0][0]+v[0][0],I,w[1][0]+v[1][0],
						w[2][0]+v[0][0],I,w[3][0]+v[1][0],I,
						I,w[2][0]+v[0][0],I,w[3][0]+v[1][0],4,4
					};
					break;
				}
			}break;
		}case 4:{
			switch(tg[u]){
				case 1:{
					auto &w=vl_son[u][0],&v=vl_son[u][1];
					G={
						w[0][0]+v[0][0],w[2][0]+v[0][0],I,I,
						w[1][0]+v[1][0],w[3][0]+v[1][0],I,I,
						I,I,I,I,I,I,I,I,2,2
					};
					break;
				}case 2:{
					auto &w=vl_son[u][0],&v=vl_son[u][1];
					G={
						w[0][0]+v[0][0],I,w[1][0]+v[0][0],I,
						I,w[0][0]+v[1][0],I,w[1][0]+v[1][0],
						I,I,I,I,I,I,I,I,2,4
					};
					break;
				}case 3:{
					auto &w=vl_son[u][0],&v=vl_son[u][1];
					G={
						std::max(w[0][0]+v[0][0],w[1][0]+v[2][0]),I,I,I,
						I,std::max(w[0][0]+v[1][0],w[1][0]+v[3][0]),I,I,
						I,I,I,I,I,I,I,I,2,2
					};
					break;
				}
			}break;
		}
	}
}

void pushup(int u){
	prod[u]=g[u];
	if(lc[u])prod[u]=prod[lc[u]]*prod[u];
	if(rc[u])prod[u]=prod[u]*prod[rc[u]];
}

int subbuild(int l,int r){
	if(l==r){calc_g(st[l]),pushup(st[l]);return st[l];}if(l>r)return 0;
	int m=std::lower_bound(sm+l,sm+r+1,(sm[l]+sm[r])>>1)-sm;
	int u=st[m];lc[u]=subbuild(l,m-1),rc[u]=subbuild(m+1,r);
	fa[lc[u]]=fa[rc[u]]=u,calc_g(u),pushup(u);return u;
}
int build(int u){
	for(int re p=u;p;p=son[p])
		lsz[p]=sz[p]-sz[son[p]];
	for(int re p=u;p;p=son[p]){int cso=0;
		for(int re w=el[p];w;w=nx[w])if(!lsz[w]){
			int v=build(w);tg_son[v]=cso;
			fa[v]=p,vl_son[p][cso++]=prod[v];
		}
	}tp=0;for(int re p=u;p;p=son[p])
		st[++tp]=p,sm[tp]=sm[tp-1]+lsz[p];
	return subbuild(1,tp);
}

void work(){
	rt=build(rt);int Q=gi();
	print(std::max(prod[rt][0][0],prod[rt][1][0]),'\n');
	while(Q--){
		int u=gi(),v0=gi(),v1=gi();
		E[u].v0=v0,E[u].v1=v1;calc_g(u);
		while(u){
			pushup(u);
			if(fa[u]&&(lc[fa[u]]!=u&&rc[fa[u]]!=u))
				vl_son[fa[u]][tg_son[u]]=prod[u],calc_g(fa[u]);
			u=fa[u];
		}print(std::max(prod[rt][0][0],prod[rt][1][0]),'\n');
	}
}

}

namespace G{//Graph

int id[N],d[N],vs[N];
std::unordered_map<int,int> eid[N];

void init(){
	for(int re i=1;i<=n;++i)id[i]=i;
	for(int re i=1;i<=rt;++i)T::sz[i]=1;
	for(int re i=1;i<=m;++i){
		int u=E[i+n].u,v=E[i+n].v;
		eid[u][v]=i+n,eid[v][u]=-i-n;
		++d[u],++d[v];
	}std::queue<int> q;
	for(int i=1;i<=n;++i)
		if(d[i]<=2)q.push(i),vs[i]=true;
	while(!q.empty()){
		int u=q.front();q.pop();
		switch(d[u]){
			case 1:{
				auto t=eid[u].begin();int v=t->fi,e=t->se;
				eid[u].clear();eid[v].erase(u);--d[v];
				if(e>0)id[v]=T::node(4,id[u],e,id[v]);
				else id[v]=T::node(4,id[u],T::node(2,-e),id[v]);
				if(!vs[v]&&d[v]<=2)q.push(v),vs[v]=true;
				break;
			}
			case 2:{
				int w=u,v,eu,ev,e;
				auto t=eid[w].begin();
				u=t->fi,eu=t->se;++t;
				v=t->fi,ev=t->se;eid[w].clear();
				eid[u].erase(w),eid[v].erase(w);
				if((ev>0)!=(eu<0)){
					if(ev<0)ev=T::node(2,-ev);
					else eu=-T::node(2,eu);
				}if(eu>0)std::swap(u,v),std::swap(eu,ev);
				e=T::node(3,-eu,id[w],ev);
				if(eid[u][v]){
					--d[u],--d[v];
					int et=eid[u][v];
					if(et<0)et=T::node(2,-et);
					e=T::node(1,e,et);
				}eid[u][v]=e,eid[v][u]=-e;
				if(!vs[u]&&d[u]<=2)q.push(u),vs[u]=true;
				if(!vs[v]&&d[v]<=2)q.push(v),vs[v]=true;
				break;
			}
		}
	}
} 

}

void Main(){
	n=gi(),m=gi();rt=n+m;
	for(int re i=1;i<=n;++i){
		int w=gi(),s=gi();
		E[i]={0,0,w,s};
	}for(int re i=1;i<=m;++i){
		int u=gi(),v=gi(),c=gi(),d=gi();
		E[i+n]={u,v,c,d};
	}G::init();T::work();
}

inline void file(){
#ifdef zxyoi
	freopen("park.in","r",stdin);
#endif
}signed main(){file();Main();return 0;}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值