BZOJ3252: 攻略
题解: 首先 考虑一个贪心策略是 每次选取必然是 每个点到根的路径和的最大值的点 然后考虑去掉这个点的影响 影响子树的价值 这样的话我们就可以dfs序维护子树了 去掉这个点只要把这点的权值在其子树中减去 然后维护到根的最大值和位置 线段树即可 然后顺着根往上爬找到第一个在之前被访问的点即可(因为这个点被访问 他的祖先也必然被访问 做k次得出答案即可
#include <bits/stdc++.h> const int MAXN=2e5+10; #define ll long long using namespace std; struct FastIO { static const int S=200; int wpos; char wbuf[S]; FastIO():wpos(0){} inline int xchar() { static char buf[S]; static int len=0,pos=0; if(pos==len) pos=0,len=fread(buf,1,S,stdin); if(pos==len) exit(0); return buf[pos++]; } inline int read() { int s=1,c=xchar(),x=0; while(c<=32) c=xchar(); if(c=='-') s=-1,c=xchar(); for(;'0'<=c&&c<='9';c=xchar()) x=x*10+c-'0'; return x*s; } ~FastIO() { if(wpos) fwrite(wbuf,1,wpos,stdout),wpos=0; } }io; int n,k; int a[MAXN];bool vis[MAXN]; vector<int>vec[MAXN]; int fa[MAXN],p[MAXN],fp[MAXN],cnt; ll dis[MAXN];int num[MAXN]; void dfs(int v,int pre){ p[v]=++cnt;fp[p[v]]=v;fa[v]=pre;dis[v]=dis[pre]+a[v];num[v]=1; for(int i=0;i<vec[v].size();i++)if(vec[v][i]!=pre){dfs(vec[v][i],v);num[v]+=num[vec[v][i]];} } ll maxx[MAXN<<2],flag[MAXN<<2];int pos[MAXN<<2]; void push(int rt){ if(flag[rt]!=0){ maxx[rt<<1]+=flag[rt];maxx[rt<<1|1]+=flag[rt]; flag[rt<<1]+=flag[rt];flag[rt<<1|1]+=flag[rt]; flag[rt]=0; } } void up(int rt){ maxx[rt]=maxx[rt<<1];pos[rt]=pos[rt<<1]; if(maxx[rt]<maxx[rt<<1|1])maxx[rt]=maxx[rt<<1|1],pos[rt]=pos[rt<<1|1]; } void built(int rt,int l,int r){ flag[rt]=0; if(l==r){maxx[rt]=dis[fp[l]];pos[rt]=l;return ;} int mid=(l+r)>>1; built(rt<<1,l,mid); built(rt<<1|1,mid+1,r); up(rt); } void update(int rt,int l,int r,int ql,int qr,ll vul){ if(ql<=l&&r<=qr){maxx[rt]+=vul;flag[rt]+=vul;return ;} int mid=(l+r)>>1; push(rt); if(ql<=mid)update(rt<<1,l,mid,ql,qr,vul); if(qr>mid)update(rt<<1|1,mid+1,r,ql,qr,vul); up(rt); } int main(){ n=io.read();k=io.read(); for(int i=1;i<=n;i++)a[i]=io.read(); int u,v; for(int i=1;i<n;i++)u=io.read(),v=io.read(),vec[u].push_back(v),vis[v]=1; for(int i=1;i<=n;i++)if(!vis[i]){u=i;break;} for(int i=1;i<=n;i++)vis[i]=0; vis[0]=1; dfs(u,0);built(1,1,n);int cnt=n; ll ans=0;int t1; for(int i=1;i<=k;i++){ ans+=maxx[1];t1=fp[pos[1]]; while(!vis[t1]&&cnt>0){update(1,1,n,p[t1],p[t1]+num[t1]-1,-a[t1]);vis[t1]=1;cnt--;t1=fa[t1];} if(cnt==0)break; } printf("%lld\n",ans); }
3252: 攻略
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 967 Solved: 461
[Submit][Status][Discuss]
Description
题目简述:树版[k取方格数]
众所周知,桂木桂马是攻略之神,开启攻略之神模式后,他可以同时攻略k部游戏。今天他得到了一款新游戏《XX
半岛》,这款游戏有n个场景(scene),某些场景可以通过不同的选择支到达其他场景。所有场景和选择支构成树状
结构:开始游戏时在根节点(共通线),叶子节点为结局。每个场景有一个价值,现在桂马开启攻略之神模式,同
时攻略k次该游戏,问他观赏到的场景的价值和最大是多少(同一场景观看多次是不能重复得到价值的)
“为什么你还没玩就知道每个场景的价值呢?”
“我已经看到结局了。”
Input
第一行两个正整数n,k
第二行n个正整数,表示每个场景的价值
以下n-1行,每行2个整数a,b,表示a场景有个选择支通向b场景(即a是b的父亲)
保证场景1为根节点
n<=200000,1<=场景价值<=2^31-1
Output
输出一个整数表示答案
Sample Input
5 2
4 3 2 1 1
1 2
1 5
2 3
2 4
4 3 2 1 1
1 2
1 5
2 3
2 4
Sample Output
10