题意
众所周知,桂木桂马是攻略之神,开启攻略之神模式后,他可以同时攻略k部游戏。今天他得到了一款新游戏《XX半岛》,这款游戏有n个场景(scene),某些场景可以通过不同的选择支到达其他场景。所有场景和选择支构成树状结构:开始游戏时在根节点(共通线),叶子节点为结局。每个场景有一个价值,现在桂马开启攻略之神模式,同时攻略k次该游戏,问他观赏到的场景的价值和最大是多少(同一场景观看多次是不能重复得到价值的)
题解
首先,你仔细想一想就可以得到一个结论,那就是每一次肯定是选择代价最大的点,这样肯定是最优的,我们就来考虑维护这一个过程、
解法1
直接dfs序,然后线段树维护一下就可以了
注意一下,每个点最多被修改一次,那么在你删点的时候暴力跳就可以了
CODE:
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<iostream>
using namespace std;
typedef long long LL;
const LL N=200005*2;
LL n,k;
LL A[N];
struct qq
{
LL x,y,last;
}e[N];LL num,last[N];
void init (LL x,LL y)
{
num++;
e[num].x=x;e[num].y=y;
e[num].last=last[x];
last[x]=num;
}
LL L[N],R[N],cnt=0;
LL belong[N];
bool vis[N];
LL dep[N];
struct qr
{
LL l,r;
LL s1,s2;
LL c,c1;
LL lazy;
}tr[N*2];
LL ff[N];
void dfs (LL x,LL fa)
{
L[x]=++cnt;belong[cnt]=x;
for (LL u=last[x];u!=-1;u=e[u].last)
{
LL y=e[u].y;
if (y==fa) continue;
dep[y]=dep[x]+A[y];ff[y]=x;dfs(y,x);
}
R[x]=cnt;
}
void update (LL x)
{
LL s1=tr[x].s1,s2=tr[x].s2;
if (tr[s1].c>tr[s2].c)
{
tr[x].c=tr[s1].c;
tr[x].c1=tr[s1].c1;
}
else
{
tr[x].c=tr[s2].c;
tr[x].c1=tr[s2].c1;
}
}
void bt (LL l,LL r)
{
LL a=++num;
tr[a].l=l;tr[a].r=r;
if (l==r)
{
tr[a].c=dep[belong[l]];
tr[a].c1=belong[l];
return ;
}
LL mid=(l+r)>>1;
tr[a].s1=num+1;bt(l,mid);
tr[a].s2=num+1;bt(mid+1,r);
update(a);
}
void push_down (LL x)
{
LL s1=tr[x].s1,s2=tr[x].s2;
tr[s1].c+=tr[x].lazy;
tr[s1].lazy+=tr[x].lazy;
tr[s2].c+=tr[x].lazy;
tr[s2].lazy+=tr[x].lazy;
tr[x].lazy=0;
}
void change (LL now,LL l,LL r,LL x)
{
/*printf("%lld %lld %lld %lld %lld\n",tr[now].l,tr[now].r,l,r,x);
system("pause");*/
if (tr[now].l==l&&tr[now].r==r)
{
tr[now].c-=x;
tr[now].lazy-=x;
return ;
}
push_down(now);
LL s1=tr[now].s1,s2=tr[now].s2;
LL mid=(tr[now].l+tr[now].r)>>1;
if (r<=mid) change(s1,l,r,x);
else if (l>mid) change(s2,l,r,x);
else change(s1,l,mid,x),change(s2,mid+1,r,x);
update(now);
}
void solve (LL x)
{
while (x!=0)
{
if (vis[x]==true) return ;
vis[x]=true;
// printf("%lld %lld %lld\n",x,L[x],R[x]);
change(1,L[x],R[x],A[x]);
x=ff[x];
}
}
int main()
{
memset(vis,false,sizeof(vis));
LL ans=0;
num=0;memset(last,-1,sizeof(last));
scanf("%lld%lld",&n,&k);
for (LL u=1;u<=n;u++) scanf("%lld",&A[u]);
for (LL u=1;u<n;u++)
{
LL x,y;
scanf("%lld%lld",&x,&y);
init(x,y);init(y,x);
}
dep[1]=A[1];dfs(1,0);
num=0;bt(1,n);
for (LL u=1;u<=k;u++)
{
// printf("%lld %lld\n",u,tr[1].c);
ans=ans+tr[1].c;
solve(tr[1].c1);
}
printf("%lld\n",ans);
return 0;
}
解法2
明显地长链剖分模型的啊。。
直接长链剖分就可以了。。
虽然我没有看出来这是长链剖分
Code
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<queue>
using namespace std;
typedef long long LL;
const LL N=200005*2;
LL n,k;
LL A[N];
struct qq
{
LL x,y,last;
}e[N];LL num,last[N];
void init (LL x,LL y)
{
num++;
e[num].x=x;e[num].y=y;
e[num].last=last[x];
last[x]=num;
}
bool vis[N];
LL dep[N];
LL mx[N];
LL son[N];
LL ff[N];
void dfs (LL x,LL fa)
{
mx[x]=dep[x];
for (LL u=last[x];u!=-1;u=e[u].last)
{
LL y=e[u].y;
if (y==fa) continue;
dep[y]=dep[x]+A[y];ff[y]=x;dfs(y,x);
mx[x]=max(mx[x],mx[y]);
if (mx[y]>mx[son[x]]) son[x]=y;
}
}
priority_queue<LL> q;
void Dfs (LL x,LL sum)
{
if (son[x]!=0) Dfs(son[x],sum+A[son[x]]);
else q.push(sum);
for (LL u=last[x];u!=-1;u=e[u].last)
{
LL y=e[u].y;
if (y==ff[x]||y==son[x]) continue;
Dfs(y,A[y]);
}
}
int main()
{
memset(vis,false,sizeof(vis));
LL ans=0;
num=0;memset(last,-1,sizeof(last));
scanf("%lld%lld",&n,&k);
for (LL u=1;u<=n;u++) scanf("%lld",&A[u]);
for (LL u=1;u<n;u++)
{
LL x,y;
scanf("%lld%lld",&x,&y);
init(x,y);init(y,x);
}
dep[1]=A[1];dfs(1,0);
Dfs(1,A[1]);
while (k!=0&&(!q.empty()))
{
ans=ans+q.top();
q.pop();
k--;
}
printf("%lld\n",ans);
return 0;
}