zoukankan      html  css  js  c++  java
  • 【BZOJ2164】采矿 树链剖分+线段树维护DP

    【BZOJ2164】采矿

    Description

    浩浩荡荡的cg大军发现了一座矿产资源极其丰富的城市,他们打算在这座城市实施新的采矿战略。这个城市可以看成一棵有n个节点的有根树,我们把每个节点用1到n的整数编号。为了方便起见,对于任何一个非根节点v,它任何一个祖先的编号都严格小于v。树上的每个节点表示一个矿点,每条边表示一条街道。作为cg大军的一个小队长,你拥有m个部下。你有一张二维的动态信息表,用Ti,j表示第i行第j列的数据。当你被允许开采某个区域时,你可以将你的部下分配至各个矿点。在第i个矿点安排j个人可以获得Ti,j单位的矿产。允许开采的区域是这样描述的:给你一对矿点(u,v),保证v是u的祖先(这里定义祖先包括u本身);u为你控制的区域,可以在以u为根的子树上任意分配部下;u到v的简单路径(不包括u但包括v,若u=v则包括u)为探险路径,在该路径上你可以选择至多一个矿点安排部下。你这次开采的收益为安排有部下的矿点的收益之和。

    Input

    输入的第一行包含5个正整数n、m、A、B、Q。n为矿点的个数,m为部下的数量。A、B、Q是与动态信息表有关的数据。第二行包含n-1个正整数,第i个数为Fi+1,表示节点i+1的父亲。接下来需要你用下文的方法依次生成n组数据,每组数据共m个。其中第i组的m个数据为信息表中第i行的m个数据。紧接着一行包含一个正整数C,表示事件的数量。最后给出C行,每行描述一个事件。每个事件会先给出一个0或1的整数。如果该数为0,则后面有一个正整数p,表示动态信息表有更新,你需要生成一组m个数据,来替换信息表中第p行的m个数据。如果该数为1,则后面有两个正整数u、v,表示出现了一个你可以开采的区域,你需要回答这次开采的收益。同一行的各个数之间均用一个空格隔开,没有多余的空格和换行。数据的生成方法如下:每次生成一组m个从小到大排列的数据,替换动态信息表的一行。其中,从小到大第j个数替换信息表中第j列的数。调用以下代码m次并排序得到一组数据。(注意可能会出现重复的数)函数GetInt A←((A xor B)+(B div X)+(B * X))and Y B←((A xor B)+(A div X)+(A * X))and Y 返回(A xor B)mod Q 其中A、B、Q均用32位有符号整数保存(C/C++的signed long int类型,pascal的longint类型),X=216(2的16次方),Y=231-1(2的31次方-1),xor为位异或运算,div为整除运算,and为位且运算,mod为取余运算。由于只保留了低31位,易得我们不用考虑数据的溢出问题。(注意每次A和B都会被改变)

    Output

    对于每个开采事件(开头为1的事件),输出一行一个整数,为每次的收益。

    Sample Input

    10 5 1 2 10
    1 1 3 3 4 4 6 6 9
    4
    1 6 3
    1 9 1
    0 1
    1 1 1

    Sample Output

    11
    9
    12
    【样例说明】
    最初的信息表如下
    1 2 3 4 5
    1 0 1 1 2 2 2 0 5 7 7 9 3 1 2 3 4 5
    4 0 1 2 4 5
    5 2 4 7 8 8
    6 0 2 3 8 9
    7 1 3 5 6 8
    8 3 3 3 7 8
    9 0 1 2 3 9
    10 0 0 1 4 4
    变化后的第1行为
    1 1 1 1 4 7
    第一次开采可以在矿点6、8、9、10任意安排,可以在矿点3或4中选取一个安排开采。一种最优安排是在矿点6安排4人,在矿点8安排1人。第二次开采可以在矿点9安排,可以在矿点6、4、3、1中选择一个安排。一种最优安排是在矿点9安排1人,在矿点6安排4人。

    HINT

    有50%的数据,对于满足2≤i≤n的整数i,Fi=i-1。这些数据中有40%的数据(即所有数据的20%)满足n≤500,m≤20,C≤500。除上述数据,另有40%的数据满足n≤500,m≤20,C≤500。对于100%的数据1≤n≤20000,1≤m≤50,1≤C≤2000。对于满足2≤i≤n的整数i,1≤Fi<i。1≤A,B≤231-1,1≤Q≤10000。

    题解:询问可以看成两种,一种是询问一段路径上每个人数的收益最大值,一种是询问子树中总共选出若干个人的收益。前者可以用树剖+线段树维护,后者可以在线段树上进行背包合并。时间复杂度$O(Clog^2 n*50+Clog n*50*50)$。

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #define lson x<<1
    #define rson x<<1|1
    #define X 65536
    #define Y 2147483647
    using namespace std;
    const int maxn=20010;
    int n,m,A,B,C,Q,cnt;
    int to[maxn],next[maxn],head[maxn],fa[maxn],siz[maxn],son[maxn],top[maxn],p1[maxn],p2[maxn],q[maxn],dep[maxn];
    struct node
    {
    	int a[55];
    	int & operator [] (int b) {return a[b];}
    	node() {memset(a,0,sizeof(a));}
    	node operator + (node b)
    	{
    		node c;
    		for(int i=0;i<=m;i++)	for(int j=0;i+j<=m;j++)	c[i+j]=max(c[i+j],a[i]+b[j]);
    		return c;
    	}
    	node operator * (node b)
    	{
    		node c;
    		for(int i=0;i<=m;i++)	c[i]=max(a[i],b[i]);
    		return c;
    	}
    }s[maxn<<2],sm[maxn<<2],v[maxn];
    inline int getint()
    {
    	A=((A^B)+(B/X)+(B*X))&Y,B=((A^B)+(A/X)+(A*X))&Y;
    	return (A^B)%Q;
    }
    void dfs1(int x)
    {
    	siz[x]=1;
    	for(int i=head[x];i!=-1;i=next[i])
    	{
    		dep[to[i]]=dep[x]+1,dfs1(to[i]),siz[x]+=siz[to[i]];
    		if(siz[to[i]]>siz[son[x]])	son[x]=to[i];
    	}
    }
    void dfs2(int x,int tp)
    {
    	top[x]=tp,p1[x]=++q[0],q[q[0]]=x;
    	if(son[x])	dfs2(son[x],tp);
    	for(int i=head[x];i!=-1;i=next[i])	if(to[i]!=son[x])	dfs2(to[i],to[i]);
    	p2[x]=q[0];
    }
    void build(int l,int r,int x)
    {
    	if(l==r)
    	{
    		s[x]=sm[x]=v[q[l]];
    		return ;
    	}
    	int mid=(l+r)>>1;
    	build(l,mid,lson),build(mid+1,r,rson);
    	s[x]=s[lson]+s[rson],sm[x]=sm[lson]*sm[rson];
    }
    void updata(int l,int r,int x,int a)
    {
    	if(l==r)
    	{
    		s[x]=sm[x]=v[q[l]];
    		return ;
    	}
    	int mid=(l+r)>>1;
    	if(a<=mid)	updata(l,mid,lson,a);
    	else	updata(mid+1,r,rson,a);
    	s[x]=s[lson]+s[rson],sm[x]=sm[lson]*sm[rson];
    }
    node q1(int l,int r,int x,int a,int b)
    {
    	if(a>b)	return node();
    	if(a<=l&&r<=b)	return s[x];
    	int mid=(l+r)>>1;
    	if(b<=mid)	return q1(l,mid,lson,a,b);
    	if(a>mid)	return q1(mid+1,r,rson,a,b);
    	return q1(l,mid,lson,a,b)+q1(mid+1,r,rson,a,b);
    }
    node q2(int l,int r,int x,int a,int b)
    {
    	if(a>b)	return node();
    	if(a<=l&&r<=b)	return sm[x];
    	int mid=(l+r)>>1;
    	if(b<=mid)	return q2(l,mid,lson,a,b);
    	if(a>mid)	return q2(mid+1,r,rson,a,b);
    	return q2(l,mid,lson,a,b)*q2(mid+1,r,rson,a,b);
    }
    inline node ask(int x,int y)
    {
    	if(x==y)	return node();
    	y=fa[y];
    	node ret;
    	while(top[x]!=top[y])
    	{
    		ret=ret*q2(1,n,1,p1[top[y]],p1[y]);
    		y=fa[top[y]];
    	}
    	ret=ret*q2(1,n,1,p1[x],p1[y]);
    	return ret;
    }
    inline int rd()
    {
    	int ret=0,f=1;	char gc=getchar();
    	while(gc<'0'||gc>'9')	{if(gc=='-')	f=-f;	gc=getchar();}
    	while(gc>='0'&&gc<='9')	ret=ret*10+(gc^'0'),gc=getchar();
    	return ret*f;
    }
    inline void add(int a,int b)
    {
    	to[cnt]=b,next[cnt]=head[a],head[a]=cnt++;
    }
    int main()
    {
    	n=rd(),m=rd(),A=rd(),B=rd(),Q=rd();
    	int i,j,a,b;
    	memset(head,-1,sizeof(head));
    	for(i=2;i<=n;i++)	fa[i]=rd(),add(fa[i],i);
    	dep[1]=1,dfs1(1),dfs2(1,1);
    	for(i=1;i<=n;i++)
    	{
    		for(j=1;j<=m;j++)	v[i][j]=getint();
    		sort(v[i].a+1,v[i].a+m+1);
    	}
    	build(1,n,1);
    	C=rd();
    	for(i=1;i<=C;i++)
    	{
    		if(!rd())
    		{
    			a=rd();
    			for(j=1;j<=m;j++)	v[a][j]=getint();
    			sort(v[a].a+1,v[a].a+m+1);
    			updata(1,n,1,p1[a]);
    		}
    		else	a=rd(),b=rd(),printf("%d
    ",(ask(b,a)+q1(1,n,1,p1[a],p2[a])).a[m]);
    	}
    	return 0;
    }//10 5 1 2 10 1 1 3 3 4 4 6 6 9 4 1 6 3 1 9 1 0 1 1 1 1 
  • 相关阅读:
    poj 1860 Currency Exchange(最短路径的应用)
    poj 2965 The Pilots Brothers' refrigerator
    zoj 1827 the game of 31 (有限制的博弈论)
    poj 3295 Tautology (构造法)
    poj 1753 Flip Game(枚举)
    poj 2109 (贪心)
    poj 1328(贪心)
    Qt 对单个控件美化
    Qt 4基础
    Bash Shell
  • 原文地址:https://www.cnblogs.com/CQzhangyu/p/7954687.html
Copyright © 2011-2022 走看看