您的位置:首页 > 其它

Tsinsen A1219. 采矿(陈许旻) (树链剖分,线段树 + DP)

2016-03-27 16:06 183 查看
【题目链接】

http://www.tsinsen.com/A1219

【题意】

给定一棵树,a[u][i]代表u结点分配i人的收益,可以随时改变a[u],查询(u,v)代表在u子树的所有节点,在u->v(不含u)路径上的节点分配人数的最优收益。

【思路】

树链剖分:构造重链时先访问重儿子,因此一个重链的区间连续,同时一个子树的区间连续。

查询分为两部分:构造在u子树内分配人数i的最大收益ans1[i],以及构造在u->v路径上一个结点分配人数i的最大收益ans2[i]。则ans=max{ ans1[i]+ans2[m-i] }。

考虑链剖:一棵线段树维护一条重链上的两类信息,c[i]为任意分配的最优,g[i]为在其中一个分配的最优。则ans1可以通过询问u子树的连续区间得,ans2可以询问u->v路径上的重链得。

需要注意的是线段树中c的递推应该逆序枚举,否则覆盖原值。

【代码】

#include<set>
#include<cmath>
#include<queue>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define trav(u,i) for(int i=front[u];i;i=e[i].nxt)
#define FOR(a,b,c) for(int a=(b);a<=(c);a++)
using namespace std;

typedef long long ll;
const int N = 4e4+10;
const int M = 55;
const ll X = 1<<16;
const ll Y = 2147483647;

ll read() {
char c=getchar();
ll f=1,x=0;
while(!isdigit(c)) {
if(c=='-') f=-1; c=getchar();
}
while(isdigit(c))
x=x*10+c-'0',c=getchar();
return x*f;
}

struct Edge {
int v,nxt;
}e[N<<1];
int en=1,front
;
void adde(int u,int v)
{
e[++en]=(Edge){v,front[u]}; front[u]=en;
}

int n,m,C,a
[M],dfs_list
,SZ,dfn; ll A,B,Q;
int dep
,son
,top
,siz
,fa
,pl
,L
,R
;

struct Tnode {
int u,l,r,c[M],g[M];
void maintain() ;
} T[N<<1];
void Tnode::maintain() {
if(T[u].l==T[u].r) return ;
memset(c,0,sizeof(c));
FOR(i,0,m) FOR(j,0,m-i)
c[i+j]=max(c[i+j],T[u<<1].c[i]+T[u<<1|1].c[j]);
FOR(i,0,m)
g[i]=max(T[u<<1].g[i],T[u<<1|1].g[i]);
}

void build(int u,int l,int r)
{
T[u].u=u,T[u].l=l,T[u].r=r;
if(l==r) {
memcpy(T[u].c,a[dfs_list[l]],sizeof(T[u].c));
memcpy(T[u].g,a[dfs_list[l]],sizeof(T[u].g));
return ;
}
int mid=l+r>>1;
build(u<<1,l,mid);
build(u<<1|1,mid+1,r);
T[u].maintain();
}
void update(int u,int x,int* A)
{
if(T[u].l==T[u].r) {
memcpy(T[u].c,A,sizeof(T[u].c));
memcpy(T[u].g,A,sizeof(T[u].g));
} else {
int mid=T[u].l+T[u].r>>1;
if(x<=mid) update(u<<1,x,A);
else update(u<<1|1,x,A);
T[u].maintain();
}
}
void query1(int u,int L,int R,int* ans)
{
if(L<=T[u].l&&T[u].r<=R) {
for(int i=m;i;i--) for(int j=i;j;j--)        //逆序枚举i 避免覆盖
ans[i]=max(ans[i],ans[i-j]+T[u].c[j]);
} else {
int mid=T[u].l+T[u].r>>1;
if(L<=mid) query1(u<<1,L,R,ans);
if(mid<R) query1(u<<1|1,L,R,ans);
}
}
void query2(int u,int L,int R,int* ans)
{
if(L<=T[u].l&&T[u].r<=R) {
FOR(i,0,m) ans[i]=max(ans[i],T[u].g[i]);
} else {
int mid=T[u].l+T[u].r>>1;
if(L<=mid) query2(u<<1,L,R,ans);
if(mid<R) query2(u<<1|1,L,R,ans);
}
}

void dfs1(int u)
{
siz[u]=1; son[u]=0;
trav(u,i) if(e[i].v!=fa[u]) {
int v=e[i].v;
fa[v]=u;
dep[v]=dep[u]+1;
dfs1(v);
siz[u]+=siz[v];
if(siz[v]>siz[son[u]]) son[u]=v;
}
}
void dfs2(int u,int tp)
{
top[u]=tp; pl[u]=L[u]=++SZ; dfs_list[SZ]=u;
if(son[u]) dfs2(son[u],tp);
trav(u,i) if(e[i].v!=fa[u]&&e[i].v!=son[u])
dfs2(e[i].v,e[i].v);
R[u]=SZ;
}
void query2(int u,int v,int*ans)
{
while(top[u]!=top[v]) {
if(dep[top[u]]<dep[top[v]]) swap(u,v);
query2(1,pl[top[u]],pl[u],ans);
u=fa[top[u]];
}
if(dep[u]>dep[v]) swap(u,v);
query2(1,pl[u],pl[v],ans);
}

int get_int()
{
A=((A^B)+(B/X)+(ll)(B*X))&Y;
B=((A^B)+(A/X)+(ll)(A*X))&Y;
return (int)(A^B)%Q;
}

int ans1[M],ans2[M];

int main()
{
//    freopen("in.in","r",stdin);
//    freopen("out.out","w",stdout);
n=read(),m=read(),A=read(),B=read(),Q=read();
int op,u,v;
FOR(i,2,n) {
u=read(); adde(u,i);
}
FOR(i,1,n) {
FOR(j,1,m) a[i][j]=get_int();
sort(a[i]+1,a[i]+m+1);
}
dfs1(1); dfs2(1,1);
build(1,1,SZ);
C=read();
FOR(i,1,C) {
op=read(),u=read();
if(op==0) {
FOR(j,1,m) a[u][j]=get_int();
sort(a[u]+1,a[u]+m+1);
update(1,L[u],a[u]);
} else {
v=read();
memset(ans1,0,sizeof(ans1));
memset(ans2,0,sizeof(ans2));
query1(1,L[u],R[u],ans1);
if(u!=v) query2(fa[u],v,ans2);
int ans=0;
FOR(i,0,m)
ans=max(ans,ans1[i]+ans2[m-i]);
printf("%d\n",ans);
}
}
return 0;
}


P.S.拿线段树DP,这道题实在是太太太太太神辣
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: