您的位置:首页 > 其它

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

2016-11-10 16:55 387 查看
第一题水题,略过。。。

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 1
1 1
2 1 1


【sample output1】

3


【sample input 2】

4 3 2
1 2 3 4
2 1 5
3 4 2


【sample output 2】

12


【数据范围】

30% m<=n<=5 ,0<=ci,ai<=1e5
100% 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 300000
typedef 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]=(k−d[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 50005
struct 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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  dp dfs 线段树 状压dp