1. 程式人生 > >NOIP2018退役題解

NOIP2018退役題解

D1T1

原題不說了

D1T2

其實這道題跟去年的毒瘤D1T1毫無關係,看到樣例猜一個結論,剩下來的系統還是原來的那些數。那麼思考什麼時候幾個數可以替代另一個數,那不是廢話了嗎。FUCK

D1T3

容易想到二分。根據菊花圖我們可以知道一個配對的思路,就是一個點上來的兩條路徑一定是一一配對的,思考怎麼配對是最優的,如果我二分了答案,那麼我們只要再二分一次找到符號條件的最小的另外一條路即可,但是我們是在一顆樹上操作,那麼我們勢必要傳上去什麼,而且一個點只能傳上去一條路,那麼肯定是傳最大的一條不能配對的邊上去,如果底下能配對就配對掉一定是最優的,因為否則傳上去也最多隻能造成1的貢獻,和直接配對無異

D2T1

考慮到資料範圍應該是O(nm)O(nm)或者O(n2)O(n^2)的,我們發現樹的情況非常的好搞,然鵝m=n的時候並不是一棵樹,因為多了一條邊,但是我們刪掉就是了啊。所以列舉刪邊即可

D2T2

爆搜剪枝可過
正解玄學斜鏈DP
考場一堆人打表系列

D2T3

一道其實不怎麼難想的題,只要冷靜,FUCK
首先我們還是跟D1T3一樣先想部分分,如果只有一組詢問,我們可以在一開始給非法狀態賦上INF然後直接DP,f[i][0/1]f[i][0/1]表示這顆子樹的根的狀態。如果多組詢問我們還是用這個DP,考慮一下這個路徑,我們可以先把lca的這顆子樹拎出來考慮,那麼我們可以把這顆子樹的形態想象成一個從u

ulcalcavv的鏈下面掛著一棵一棵的樹。那麼如果我們知道每個點狀態下的資訊我們就可以用倍增得到答案。我們暫時只考慮uulcalca,那麼每一個節點的資訊就是這個點本來子樹的資訊減去上一個點本來子樹的資訊(想一想畫畫圖就可以知道了),我們只要還原一下就好了,把本來的DP式倒過來寫,扣掉那項即可,然後倍增轉移的時候只要列舉中間節點的狀態即可。最後我們還要把(整棵樹-lca子樹)的答案算上去,要麼我們再倍增一次,從lca倍增到1,要麼我們再做一個樹形DP,表示的是整棵樹減去這個點為根的子樹的代價。複雜度O(nlogn+qlogn)O(nlogn+qlogn),常數帶個222^2列舉狀態

程式碼

#include <bits/stdc++.h>
#define maxn 100005
#define LL long long
#define INF 100000000000
using namespace std;
int read(){
	int res=0,f=1; char c;
	while(!isdigit(c=getchar())) if(c=='-') f=-1;
	res=c^48;
	while(isdigit(c=getchar())) res=(res<<1)+(res<<3)+(c^48);
	return res*f;
}
struct EDGE{
	int u,v,nxt;
}e[maxn<<1];
int cnt,head[maxn];
void add(int u,int v){
	e[++cnt]=(EDGE){u,v,head[u]};
	head[u]=cnt;
}
int fa[maxn][21],dep[maxn],w[maxn];
LL f[maxn][2],g[maxn][2],d[maxn][21][2][2];
set<pair<int,int> > M;
int n,m;
void DFS(int u){
	dep[u]=dep[fa[u][0]]+1;
	f[u][1]=w[u];
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].v; if(v==fa[u][0]) continue; 
		fa[v][0]=u;
		DFS(v);
		f[u][0]+=f[v][1];
		f[u][1]+=min(f[v][0],f[v][1]);
	}
}
void DFS_2(int u){
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].v; if(v==fa[u][0]) continue;
		g[v][0]=g[u][1]+f[u][1]-min(f[v][0],f[v][1]);
		g[v][1]=min(g[v][0],g[u][0]+f[u][0]-f[v][1]);
		DFS_2(v);
	}
}
int main(){
	n=read(); m=read(); char s[20]; scanf("%s",s);
	for(int i=1;i<=n;i++) w[i]=read();
	for(int i=1;i<n;i++){
		int u=read(),v=read();
		add(u,v); add(v,u);
		M.insert(make_pair(u,v));
		M.insert(make_pair(v,u));
	}
	DFS(1); DFS_2(1);
	for(int i=2;i<=n;i++){
		d[i][0][0][0]=INF;
		d[i][0][1][0]=f[fa[i][0]][0]-f[i][1];
		d[i][0][0][1]=f[fa[i][0]][1]-min(f[i][1],f[i][0]);
		d[i][0][1][1]=f[fa[i][0]][1]-min(f[i][1],f[i][0]);
	}
	for(int j=1;j<=20;j++){
		for(int i=1;i<=n;i++){
			fa[i][j]=fa[fa[i][j-1]][j-1];
			int tmp=fa[i][j-1];
			for(int k=0;k<2;k++){
				for(int l=0;l<2;l++){
					d[i][j][k][l]=min(d[i][j-1][k][1]+d[tmp][j-1][1][l],d[i][j-1][k][0]+d[tmp][j-1][0][l]);
				}
			}
		}
	}
	for(int i=1;i<=m;i++){
		int x,y,p,q;
		LL ans[2][2],t[2];
		x=read(); p=read(); y=read(); q=read();
		if(!p && !q && M.find(make_pair(x,y))!=M.end()) {puts("-1"); continue;}
		if(dep[x]<dep[y]) swap(x,y),swap(p,q);
		ans[0][p]=f[x][p];
		ans[0][p^1]=INF;
		ans[1][q]=f[y][q];
		ans[1][q^1]=INF;
		for(int i=20;~i;i--){
			if(dep[fa[x][i]]>=dep[y]){
				t[0]=INF; t[1]=INF;
				for(int j=0;j<2;j++) for(int k=0;k<2;k++) t[j]=min(t[j],ans[0][k]+d[x][i][k][j]);
				ans[0][0]=t[0]; ans[0][1]=t[1];
				x=fa[x][i];
			}
		}
		if(x==y) {printf("%lld\n",ans[0][q]+g[y][q]); continue;}
		for(int i=20;~i;i--){
			if(fa[x][i]!=fa[y][i]){
				t[0]=INF; t[1]=INF;
				for(int j=0;j<2;j++) for(int k=0;k<2;k++) t[j]=min(t[j],ans[0][k]+d[x][i][k][j]);
				ans[0][0]=t[0]; ans[0][1]=t[1];
				t[0]=INF; t[1]=INF;
				for(int j=0;j<2;j++) for(int k=0;k<2;k++) t[j]=min(t[j],ans[1][k]+d[y][i][k][j]);
				ans[1][0]=t[0]; ans[1][1]=t[1];
				x=fa[x][i]; y=fa[y][i];
			}
		}
		int lca=fa[x][0];
		printf("%lld\n",min(f[lca][0]-f[x][1]-f[y][1]+ans[0][1]+ans[1][1]+g[lca][0],f[lca][1]-min(f[x][1],f[x][0])-min(f[y][0],f[y][1])+min(ans[0][0],ans[0][1])+min(ans[1][0],ans[1][1])+g[lca][1]));
	}
}