1. 程式人生 > >[DP](計蒜之道2016程式設計大賽初賽第六場)微軟的員工福利 題解.md

[DP](計蒜之道2016程式設計大賽初賽第六場)微軟的員工福利 題解.md

[DP] (計蒜之道2016初賽第六場) 微軟的員工福利 題解

題目大意

給出一個nn個節點的有根樹,每個點可以賦予給定的兩個值v[i][0/1]v[i][0/1]其中之一,這棵樹的權值就是所有節點的值,但是對於每個非葉節點節點ii而言,如果在它和它所有兒子節點中最大值與最小值的差大小為xx,那麼需要在樹的權值中扣除i666x1000i*666*\lceil\frac{x}{1000}\rceil,求這棵樹的最大權值。

n105,v[i][0/1]105n\le10^5,v[i][0/1]\le10^5

解題分析

這道題非A即B,所以典型的2-SAT

可以考慮套路1:先用其中一個,然後用差值調整,但是由於最大值與最小值的差要扣除,所以還需要套路2,由於x1000\lceil\frac{x}{1000}\rceil最多有101個,所以可以一開始用套路2列舉扣除值的大小,然後列舉在這個範圍內所有的可包圍點,用DP結合套路1來算出最優解。

什麼是two pointers

示例程式碼

題目傳送門

#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long LL;
const int maxn=100005,maxm=200005;
int n,now,
v[maxn][2],tot,son[maxm],nxt[maxm],lnk[maxn],ha[maxm]; LL f[maxn][2]; struct data{ int x,y; data (int x=0,int y=0):x(x),y(y){} bool operator < (const data b)const{return x<b.x;} }w[maxm]; inline void readi(int &x){ x=0; char ch=getchar(); while ('0'>ch||ch>'9') ch=getchar(); while (
'0'<=ch&&ch<='9') {x=x*10+ch-'0'; ch=getchar();} } void _add(int x,int y){son[++tot]=y; nxt[tot]=lnk[x]; lnk[x]=tot;} void _init(){ freopen("B.in","r",stdin); freopen("B.out","w",stdout); readi(n); tot=0; for (int i=1;i<=n;i++) {readi(v[i][0]); readi(v[i][1]); if(v[i][0]>v[i][1]) swap(v[i][0],v[i][1]);} for (int i=1,x,y;i<n;i++){ readi(x); readi(y); _add(x,y); _add(y,x); } } int gei(int L,int R,int x){ int A=(L<=v[x][0]&&v[x][0]<=R),B=(L<=v[x][1]&&v[x][1]<=R); return B*2+A; } void _dfs(int x,int fa){ for (int j=lnk[x];j;j=nxt[j]) if (son[j]!=fa) _dfs(son[j],x); now=0; for (int j=lnk[x];j;j=nxt[j]) if (son[j]!=fa) {w[++now]=data(v[son[j]][0],son[j]); w[++now]=data(v[son[j]][1],son[j]);} if (!now) {f[x][0]=v[x][0]; f[x][1]=v[x][1]; return;} w[++now]=data(v[x][0],x); w[++now]=data(v[x][1],x); sort(w+1,w+now+1); LL mx[2]; mx[0]=mx[1]=((LL)1<<60)*(-1); for (int D=0;D<=w[now].x-w[1].x+1000;D+=1000){ //列舉極差 int k=0; LL sum=-(LL)D/1000*666*x; for (int i=1;i<=now;i++) ha[w[i].y]=0; for (int i=1,j=1,t,L,R;i<=now;i=t){ if (v[x][1]<w[i].x) break; L=w[i].x; R=L+D; for (;j<=now&&w[j].x<=R;ha[w[j++].y]++) if (ha[w[j].y]) sum+=max(f[w[j].y][0],f[w[j].y][1])-f[w[j].y][0]; //如果兩個都可以選上,貪心選較大的 else {sum+=f[w[j].y][gei(L,R,w[j].y)==2]; k++;} //如果只有一個,優先考慮小的 if (k==(now>>1)){ int te=gei(L,R,x); if (te&1) mx[0]=max(mx[0],sum+v[x][0]); if (te>1) mx[1]=max(mx[1],sum+v[x][1]); } //更新答案 for (t=i;w[i].x==w[t].x&&t<=now;ha[w[t++].y]--){ if (ha[w[t].y]==1) {sum-=f[w[t].y][gei(L,R,w[t].y)==2]; k--;} else sum-=max(f[w[t].y][0],f[w[t].y][1])-f[w[t].y][1]; }//區間右移,去除前面的 } } f[x][0]=mx[0]; f[x][1]=mx[1]; } void _solve(){ _dfs(1,0); printf("%lld",max(f[1][0],f[1][1])); } int main() { _init(); _solve(); return 0; }