P5024 [NOIP2018 提高组] 保卫王国

思路:

首先想到每次询问两个点后就从这两个点开始往上爬,沿路更新 dp 值即可。

#include <bits/stdc++.h>
#define For(i,a,b) for(int i=a;i<=b;i++)
#define Rev(i,a,b) for(int i=a;i>=b;i--)
#define clr(a,v) memset(a,v,sizeof(a))
#define Freopen(file) \
freopen(file".in","r",stdin); \
freopen(file".out","w",stdout);
#define int long long
using namespace std;

const int N=2e5+5;

string CharlieVinnie;
int n,Q,a[N];
vector<int> to[N];
int fa[N],dep[N];
int f[N][2],g[N][2];
set<int> got[N];

void dfs(int u,int pa)
{
	fa[u]=pa;
	dep[u]=dep[pa]+1;
	
	f[u][0]=0;
	f[u][1]=a[u];
	
	int sz=to[u].size();
	For(i,0,sz-1){
		int v=to[u][i];
		if(v==pa) continue;
		dfs(v,u);
		
		f[u][0]+=f[v][1];
		f[u][1]+=min(f[v][0],f[v][1]);
	}
}

int lca(int x,int y)
{
	if(dep[x]<dep[y]) swap(x,y);
	while(dep[x]>dep[y]) x=fa[x];
	while(x!=y){
		x=fa[x];
		y=fa[y];
	}
	return x;
}

int read()
{
	char ch;
	while((ch=getchar())<'0'||ch>'9') ;
	int x=ch-'0';
	while((ch=getchar())>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0';
	return x;
}

signed main()
{
//	ios::sync_with_stdio(false);
//	cin.tie(0);
//	cout.tie(0);
	
//	cin>>n>>Q>>CharlieVinnie;
	n=read();
	Q=read();
	cin>>CharlieVinnie;
	
//	For(i,1,n) cin>>a[i];
	For(i,1,n) a[i]=read();
	
	For(i,1,n-1){
		int x,y;
		x=read();
		y=read();
//		cin>>x>>y;
		to[x].push_back(y);
		to[y].push_back(x);
		got[x].insert(y);
		got[y].insert(x);
	}
	
	dfs(1,0);
	
	while(Q--){
		int x,tx,y,ty;
//		cin>>x>>tx>>y>>ty;
		x=read();
		tx=read();
		y=read();
		ty=read();
		
		if(tx==0&&ty==0&&got[x].count(y)){
			puts("-1");
			continue;
		}
		
		if(x==y&&tx!=ty){
			puts("-1");
			continue;
		}
		
		if(dep[x]<dep[y]){
			swap(x,y);
			swap(tx,ty);
		}
		
		int z=lca(x,y);
		
		if(x!=z&&y!=z){
			g[x][tx]=f[x][tx];
			g[x][tx^1]=1e14;
			
			g[y][ty]=f[y][ty];
			g[y][ty^1]=1e14;
			
			while(fa[x]!=z){
				int fx=fa[x];
				g[fx][0]=f[fx][0]-f[x][1]+g[x][1];
				g[fx][1]=f[fx][1]-min(f[x][0],f[x][1])+min(g[x][0],g[x][1]);
				x=fx;
			}
			
			
			
			while(fa[y]!=z){
				int fy=fa[y];
				g[fy][0]=f[fy][0]-f[y][1]+g[y][1];
				g[fy][1]=f[fy][1]-min(f[y][0],f[y][1])+min(g[y][0],g[y][1]);
				y=fy;
			}
			
			g[z][0]=f[z][0]-f[x][1]+g[x][1]-f[y][1]+g[y][1];
			g[z][1]=f[z][1]-min(f[x][0],f[x][1])+min(g[x][0],g[x][1])-min(f[y][0],f[y][1])+min(g[y][0],g[y][1]);
		}
		else{
			g[x][tx]=f[x][tx];
			g[x][tx^1]=1e14;
			
			while(x!=z){
				int fx=fa[x];
				g[fx][0]=f[fx][0]-f[x][1]+g[x][1];
				g[fx][1]=f[fx][1]-min(f[x][0],f[x][1])+min(g[x][0],g[x][1]);
				x=fx;
			}
			
			g[z][ty^1]=1e14;
		}
		
		while(z!=1){
			int fz=fa[z];
			g[fz][0]=f[fz][0]-f[z][1]+g[z][1];
			g[fz][1]=f[fz][1]-min(f[z][0],f[z][1])+min(g[z][0],g[z][1]);
			z=fz;
		}
		
		cout<<g[1][0]<<' '<<g[1][1]<<endl;
		
		int ans=min(g[1][0],g[1][1]);
		
//		if(ans!=1e14) cout<<ans<<endl;
//		else cout<<-1<<endl;
		printf("%lld\n",ans);
//		if(ans!=1e14) 
//		else puts("-1");
	}
	
	return 0;
}

  

然后就是怎么优化这一过程。

原来是想着直接暴力推公式找规律,然后就开始写。

发现如果令 $f[x][1] \geq f[x][0]$ 恒成立的话转移方程很漂亮,于是就“如果 $f[x][1] < f[x][0]$,也就是放人比不放还赚,于是不妨设放人,即 $f[x][0]=\min(f[x][0],f[x][1])$”

写完一测样例就错了。

原因是国王会要求在某个点不放人,所以“放人比不放还赚”不成立。

 

正确的思路是:

假设一开始 dp 出来的结果为 $f[x][0/1]$,询问过程中的新 dp 结果为 $g[x][0/1]$。

可以发现 $g[x][0/1]$ 到 $g[fx][0/1]$ 的转移可以表示为 $g[fx][0/1]=min(g[x][0]+c0,g[x][1]+c1)$,其中 $c0,c1$ 为一个表达式。

于是就用广义矩阵乘法优化,树上倍增的事情也解决了。

最终代码:

#include <bits/stdc++.h>
#define For(i,a,b) for(int i=a;i<=b;i++)
#define Rev(i,a,b) for(int i=a;i>=b;i--)
#define clr(a,v) memset(a,v,sizeof(a))
#define Freopen(file) \
freopen(file".in","r",stdin); \
freopen(file".out","w",stdout);
#define int long long
using namespace std;

const int N=2e5+5;

// let df[x]=f[x][1]-f[x][0]
//
// d[x][0]=g[x][0]-f[x][0]
// d[x][1]=g[x][1]-f[x][1]
//
// d[fx][0]=d[x][1]=min(d[x][0]+inf,d[x][1]+0)
// d[fx][1]=min(f[x][0]+d[x][0],f[x][1]+d[x][1])-min(f[x][0],f[x][1])
//         =min(d[x][0],(f[x][1]-f[x][0])+d[x][1])-min(0,f[x][1]-f[x][0])
//         =min(d[x][0]-min(0,df[x]),d[x][1]+df[x]-min(0,df[x]))
//
// [ inf ,   -min(0,df[x])    ]
// [  0  , df[x]-min(0,df[x]) ]

struct Matrix{
	int a[2][2];
	
	Matrix()
	{
		clr(a,0x3f);
	}
	
	friend Matrix operator* (const Matrix& x,const Matrix& y)
	{
		Matrix z;
		For(i,0,1){
			For(j,0,1){
				For(k,0,1){
					z.a[i][j]=min(z.a[i][j],x.a[i][k]+y.a[k][j]);
				}
			}
		}
		return z;
	}
};

string CharlieVinnie;
int n,Q,a[N];
vector<int> to[N];
int fa[N][21],dep[N];
int f[N][2],df[N];
Matrix mat[N][21];

int read()
{
	char ch;
	while((ch=getchar())<'0'||ch>'9') ;
	int x=ch-'0';
	while((ch=getchar())>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0';
	return x;
}

void dfs(int u,int pa)
{
	fa[u][0]=pa;
	dep[u]=dep[pa]+1;
	
	f[u][0]=0;
	f[u][1]=a[u];
	
	int sz=to[u].size();
	For(i,0,sz-1){
		int v=to[u][i];
		if(v==pa) continue;
		dfs(v,u);
		
		f[u][0]+=f[v][1];
		f[u][1]+=min(f[v][0],f[v][1]);
	}
	
	df[u]=f[u][1]-f[u][0];
}

int lca(int x,int y)
{
	if(dep[x]<dep[y]) swap(x,y);
	
	Rev(i,20,0){
		if(dep[fa[x][i]]>=dep[y]) x=fa[x][i];
	}
	
	if(x==y) return x;
	
	Rev(i,20,0){
		if(fa[x][i]!=fa[y][i]){
			x=fa[x][i];
			y=fa[y][i];
		}
	}
	
	return fa[x][0];
}

void moveup(int& x,int z,int& dx0,int& dx1)
{
	Matrix res;
	res.a[0][0]=res.a[1][1]=0;
	
	Rev(k,20,0){
		if(dep[fa[x][k]]>dep[z]){
			res=res*mat[x][k];
			x=fa[x][k];
		}
	}
	
	int t0=dx0,t1=dx1;
	dx0=min(t0+res.a[0][0],t1+res.a[1][0]);
	dx1=min(t0+res.a[0][1],t1+res.a[1][1]);
}

signed main()
{
	n=read();
	Q=read();
	cin>>CharlieVinnie;
	
	For(i,1,n) a[i]=read();
	
	For(i,1,n-1){
		int x,y;
		x=read();
		y=read();
		to[x].push_back(y);
		to[y].push_back(x);
	}
	
	dfs(1,0);
	
	For(i,1,n){
		mat[i][0].a[0][0]=1e14;
		mat[i][0].a[0][1]=-min(0ll,df[i]);
		mat[i][0].a[1][0]=0;
		mat[i][0].a[1][1]=df[i]-min(0ll,df[i]);
	}
	
	For(j,1,20){
		For(i,1,n){
			fa[i][j]=fa[fa[i][j-1]][j-1];
			mat[i][j]=mat[i][j-1]*mat[fa[i][j-1]][j-1];
		}
	}
	
	while(Q--){
		int x,tx,y,ty;
		x=read();
		tx=read();
		y=read();
		ty=read();
		
		if(dep[x]<dep[y]){
			swap(x,y);
			swap(tx,ty);
		}
		
		if(tx==0&&ty==0&&fa[x][0]==y){
			puts("-1");
			continue;
		}
		
		if(x==y&&tx!=ty){
			puts("-1");
			continue;
		}
		
		int z=lca(x,y);
		
		int dz0=0,dz1=0;
		
		if(y!=z){
			int dx0=0,dx1=0;
			if(tx==0) dx1=1e14;
			else dx0=1e14;
			moveup(x,z,dx0,dx1);
			
			int dy0=0,dy1=0;
			if(ty==0) dy1=1e14;
			else dy0=1e14;
			moveup(y,z,dy0,dy1);
			
			dz0=dx1+dy1;
			dz1=min(dx0-min(0ll,df[x]),dx1+df[x]-min(0ll,df[x]))+min(dy0-min(0ll,df[y]),dy1+df[y]-min(0ll,df[y]));
		}
		else{
			int dx0=0,dx1=0;
			if(tx==0) dx1=1e14;
			else dx0=1e14;
			
			moveup(x,fa[z][0],dx0,dx1);
			
			dz0=dx0;
			dz1=dx1;
			
			if(ty==0) dz1=1e14;
			else dz0=1e14;
		}
		
		moveup(z,0,dz0,dz1);
		
		printf("%lld\n",min(f[1][0]+dz0,f[1][1]+dz1));
	}
	
	return 0;
}

  

posted @ 2021-12-11 13:24  CharlieVinnie  阅读(48)  评论(0编辑  收藏  举报