test 11-10 [水题 状压DP dfs序+线段树]

来源:互联网 发布:淘宝虚拟网店取消 编辑:程序博客网 时间:2024/06/04 18:01

第一题水题,略过。。。

T2

【题目描述】

我们要从n种食物选m个出来,安排一个顺序吃掉它(们),每种食物有个美味值ai,然后我们有k个规则,每个规则有 xi, yi 和 ci三个数,如果吃完第xi种食物接下来马上吃第yi种食物,第j种食物的美味值会增加ci。每种食物至多吃一个,求美味值最大的和是多少?

【输入格式】

第一行有三个数n,m,k,k代表有k个规则(0<=k<=n*(n-1))。第二行有n个数字代表每个食物的美味值。接下去有k行,每行三个数xi,yi,ci。保证没有任意两个规则的xi和yi同时相同。

【输出格式】

一行一个数代表答案

【sample input1】

2 2 11 12 1 1

【sample output1】

3

【sample input 2】

4 3 21 2 3 42 1 53 4 2

【sample output 2】

12

【数据范围】

30% m<=n<=5 ,0<=ci,ai<=1e5100% m<=n<=18,0<=ci,ai<=1e9

状压dp,用记忆化搜索比较简便,主要是再加一个状态,表示上一次取的,即可得转移方程:
dp(S,i,j)=max{dp(S’,k,j-1)}
其中,j,这个状态是指取了多少个,但是S已经表示了,记忆化的时候不用加这一维,这里只是为了方便。

代码样本:

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>using namespace std;#define maxn 25#define maxbit 300000typedef long long ll;ll f[maxbit][maxn],w[maxn],ad[maxn][maxn];int p[maxn],n,m,k;bool vis[maxbit][maxn];ll dp(int s,int las,int nu){    if(nu==m)return 0;    if(vis[s][las])return f[s][las];    ll &ans=f[s][las];    for(int i=1;i<=n;i++)    {        if(s&p[i])continue;        if(ad[las][i])            ans=(ll)max(ans,dp(s|p[i],i,nu+1)+ad[las][i]+w[i]);        else            ans=(ll)max(ans,dp(s|p[i],i,nu+1)+w[i]);    }    vis[s][las]=true;    return ans;}int main(){    scanf("%d%d%d",&n,&m,&k);    for(int i=1;i<=n;i++)    {        scanf("%I64d",&w[i]);        p[i]=1<<(i-1);    }    int a,b;    ll val;    while(k--)    {        scanf("%d%d%I64d",&a,&b,&val);        ad[a][b]=val;    }    printf("%I64d",dp(0,0,0));    return 0;}

T3

【题目描述】

    历史上有一个著名的王国。它的所有城市互相连通并且构成一棵树。城市1为首都也就是这棵树的根。     因为外来的入侵,国王决定在某些城市加派士兵。所有城市初始士兵数量为0。当城市i 被加派了k 名士兵时。城市i 的所有子城市需要被加派k+1 名士兵。这些子城市的所有子城市需要被加派k+2 名士兵。以此类推。    当然,加派士兵的同时,国王也需要不断了解当前的情况。于是他随时可能询问以城市i 为根的子树中的所有城市共被加派了多少士兵。     你现在是国王的军事大臣,你能回答出国王的每个询问么? 

【输入】

第一行,包含两个整数N,P 代表城市数量以及国王的命令的数量。 接下来的P 行,每行代表国王的一个命令,命令分两种 A X K :在城市X 加入K 个士兵 Q X :询问以城市X 为根的子树中所有士兵数量的和 

【输出】

对于每个Q,输出答案。 

【输入样例】

7 10 1 1 2 2 5 5 Q 1 A 2 1 Q 1 Q 2 Q 5 A 5 0 Q 5 A 3 1 Q 1 Q 2 

【输出样例】

0 11 11 8 10 14 13 

【数据范围】

对于50%的数据, 1<=N<=1000 1<=P<=300 对于100%的数据, 1<=N<=50000 1<=P<=100000 1<=X<=N 0<=K<=1000

经过推导,可得:ans[u]=kd[u]numson[u]+Sigma(d[son[u]])
然后把点用dfs序存下
最后线段树,区间修改,区间查找
即:最终的值等于k减去该点深度,乘以子树大小加上子树深度和。
记住要记录add和tim(加的次数)的值,而且add可能为0,因此判断pushdown的标准是tim不为0而不是add

代码样本:

#include<iostream>#include<cstdio>#include<cstring>#include<queue>#include<set>#include<algorithm>using namespace std;#define L(u) (u<<1)#define R(u) ((u<<1)|1)#define maxn 50005struct tre{    int l,r;}nod[maxn*4];typedef long long ll;ll d[maxn],ma[maxn],sum[maxn*4],ad[maxn*4],sd[maxn],ti[maxn*4],k;int head[maxn],tov[maxn],next[maxn],n,p,a,size[maxn],dfn[maxn],num;int dfs(int u){    size[u]=1;    dfn[u]=++num;    ma[num]=u;    int v=head[u];    while(v)    {        d[tov[v]]=d[u]+1;        size[u]+=dfs(tov[v]);        v=next[v];    }    return size[u];}void build(int u,int le,int ri){    nod[u].l=le,nod[u].r=ri;    if(le==ri)    {        return ;    }    int mid=(le+ri)/2;    build(L(u),le,mid);    build(R(u),mid+1,ri);}ll upg(ll val,int po){    return val-d[po];}void pushdown(int u){    sum[L(u)]+=(nod[L(u)].r-nod[L(u)].l+1)*ad[u]+(sd[nod[L(u)].r]-sd[nod[L(u)].l-1])*ti[u];    sum[R(u)]+=(nod[R(u)].r-nod[R(u)].l+1)*ad[u]+(sd[nod[R(u)].r]-sd[nod[R(u)].l-1])*ti[u];    ad[L(u)]+=ad[u];    ad[R(u)]+=ad[u];    ti[L(u)]+=ti[u];    ti[R(u)]+=ti[u];    ad[u]=0;    ti[u]=0;}void update(int u,int le,int ri,ll val,int po){    if(nod[u].l>=le&&nod[u].r<=ri)    {        sum[u]+=(nod[u].r-nod[u].l+1)*upg(val,po)+sd[nod[u].r]-sd[nod[u].l-1];        ad[u]+=upg(val,po);        ti[u]++;        return;    }    if(ad[u]||ti[u]) pushdown(u);    int mid=(nod[u].l+nod[u].r)/2;    if(ri<=mid)update(L(u),le,ri,val,po);    else if(le>mid)update(R(u),le,ri,val,po);    else    {        update(L(u),le,mid,val,po);        update(R(u),mid+1,ri,val,po);    }    sum[u]=sum[L(u)]+sum[R(u)];}ll query(int u,int le,int ri){    if(nod[u].l==le&&nod[u].r==ri)    {        return sum[u];    }    if(ad[u]||ti[u])pushdown(u);    int mid=(nod[u].l+nod[u].r)/2;    if(ri<=mid)return query(L(u),le,ri);    else if(le>mid) return query(R(u),le,ri);    else        return query(L(u),le,mid)+query(R(u),mid+1,ri);}int main(){    freopen("c.in","r",stdin);    freopen("c.out","w",stdout);    scanf("%d%d",&n,&p);    for(int i=2;i<=n;i++)    {        scanf("%d",&a);        tov[i]=i;        next[i]=head[a];        head[a]=i;    }    d[1]=1;    dfs(1);    for(int i=1;i<=n;i++)        sd[i]=sd[i-1]+d[ma[i]];    build(1,1,n);    char op;    while(p--)    {        scanf(" %c ",&op);        if(op=='A')        {            scanf("%d%I64d",&a,&k);            update(1,dfn[a],dfn[a]+size[a]-1,k,a);        }        else        {            scanf("%d",&a);            printf("%I64d\n",query(1,dfn[a],dfn[a]+size[a]-1));        }    }    return 0;}
0 0