【BZOJ-3252】攻略 DFS序 + 线段树 + 贪心
3252: 攻略
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 339 Solved: 130
[Submit][Status][Discuss]
Description
题目简述:树版[k取方格数]
众所周知,桂木桂马是攻略之神,开启攻略之神模式后,他可以同时攻略k部游戏。
今天他得到了一款新游戏《XX半岛》,这款游戏有n个场景(scene),某些场景可以通过不同的选择支到达其他场景。所有场景和选择支构成树状结构:开始游戏时在根节点(共通线),叶子节点为结局。每个场景有一个价值,现在桂马开启攻略之神模式,同时攻略k次该游戏,问他观赏到的场景的价值和最大是多少(同一场景观看多次是不能重复得到价值的)
“为什么你还没玩就知道每个场景的价值呢?”
“我已经看到结局了。”
Input
第一行两个正整数n,k
第二行n个正整数,表示每个场景的价值
以下n-1行,每行2个整数a,b,表示a场景有个选择支通向b场景(即a是b的父亲)
保证场景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
HINT
对于100%的数据,n<=200000,1<=场景价值<=2^31-1
Source
Solution
比较好想的一道题
首先,K次攻略,使总价值最大,显然每次攻略当前最大即可
我们定义一个节点的Val为根到这个节点的∑val
那么我们用线段树维护一下这个东西,每次取最大。
考虑统计一次答案之后,这个点的价值就不存在了,那么我们只需要把这个点到根的路径上的所有点的val在它的子树中减去即可
每个点只减一次就可以了....
所以时间复杂度还是$O(nlogn)$的
Code
#include<iostream> #include<cstdio> #include<algorithm> #include<cmath> #include<cstring> using namespace std; int read() { int x=0,f=1; char ch=getchar(); while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();} while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();} return x*f; } #define MAXN 200010 #define LL long long int N,K; bool visit[MAXN]; LL sumV[MAXN],val[MAXN]; struct EdgeNode{int next,to;}edge[MAXN<<1]; int head[MAXN],cnt=1,fa[MAXN]; void AddEdge(int u,int v) {cnt++; edge[cnt].next=head[u]; head[u]=cnt; edge[cnt].to=v;} void InsertEdge(int u,int v) {fa[v]=u; AddEdge(u,v); AddEdge(v,u);} int pl[MAXN],pr[MAXN],dfn,pre[MAXN]; void DFS(int now,int last) { pl[now]=++dfn; pre[dfn]=now; for (int i=head[now]; i; i=edge[i].next) if (edge[i].to!=last) sumV[edge[i].to]=sumV[now]+val[edge[i].to],DFS(edge[i].to,now); pr[now]=dfn; } struct SegmentTreeNode{int l,r,maxp; LL maxx,tag;}tree[MAXN<<2]; int Maxp(SegmentTreeNode ls,SegmentTreeNode rs) {return ls.maxx>rs.maxx? ls.maxp:rs.maxp;} void Update(int now) { tree[now].maxx=max(tree[now<<1].maxx,tree[now<<1|1].maxx); tree[now].maxp=Maxp(tree[now<<1],tree[now<<1|1]); } void BuildTree(int now,int l,int r) { tree[now].l=l; tree[now].r=r; if (l==r) {tree[now].maxx=sumV[pre[l]]; tree[now].maxp=l; return;} int mid=(l+r)>>1; BuildTree(now<<1,l,mid); BuildTree(now<<1|1,mid+1,r); Update(now); } void PushDown(int now) { if (tree[now].l==tree[now].r || !tree[now].tag) return; LL tag=tree[now].tag; tree[now].tag=0; tree[now<<1].maxx+=tag; tree[now<<1|1].maxx+=tag; tree[now<<1].tag+=tag; tree[now<<1|1].tag+=tag; } void Change(int now,int L,int R,int val) { PushDown(now); int l=tree[now].l,r=tree[now].r; if (L<=l && R>=r) {tree[now].maxx+=val; tree[now].tag+=val; return;} int mid=(l+r)>>1; if (L<=mid) Change(now<<1,L,R,val); if (R>mid) Change(now<<1|1,L,R,val); Update(now); } int main() { N=read(); K=read(); for (int i=1; i<=N; i++) val[i]=read(); for (int x,y,i=1; i<=N-1; i++) x=read(),y=read(),InsertEdge(x,y); sumV[1]=val[1]; DFS(1,0); // for (int i=1; i<=N; i++) // printf("ID=%d [%d , %d] %d %d\n",pre[i],pl[pre[i]],pr[pre[i]],val[pre[i]],sumV[pre[i]]); LL ans=0; BuildTree(1,1,N); for (int i=1; i<=K; i++) { // printf("NOW=%d : %d , %d\n",i,tree[1].maxx,tree[1].maxp); if (tree[1].maxx>0) ans+=tree[1].maxx; else break; for (int x=pre[tree[1].maxp]; !visit[x]&&x; x=fa[x]) visit[x]=1,Change(1,pl[x],pr[x],-val[x]); } printf("%lld\n",ans); return 0; }
第一次明白攻略组的意思....大概是看刀剑的时候吧/....
——It's a lonely path. Don't make it any lonelier than it has to be.