题目大意
给出 n n n 条边的左右端点和边的颜色( 1 1 1 或 − 1 -1 −1)和权值 w w w,边组成一棵树,根节点为 0 0 0,有 A , B A,B A,B 二人轮流删边, A A A 只能删掉颜色为 1 1 1 的边, B B B 只能删掉颜色为 − 1 -1 −1 的边,若一条边与根节点失去联系则自动消失,假设 s u m A sumA sumA 为 A A A 砍掉的边的权值和, s u m B sumB sumB 为 B B B 砍掉的边的权值和, A A A 想让 s u m A − s u m B sumA-sumB sumA−sumB 尽可能大, B B B 想让 s u m A − s u m B sumA-sumB sumA−sumB 尽可能小,且 A , B A,B A,B 足够聪明,A先进行操作,求最终的 s u m A − s u m B sumA-sumB sumA−sumB 的值。
数据范围
1 ≤ n ≤ 20 , 1 ≤ w ≤ 1000 1 \le n \le20,1 \le w \le 1000 1≤n≤20,1≤w≤1000
思路
大部分人的做法都是状压DP或记忆化搜索,讲讲我的暴力优化算法 (记搜,不打也罢!) 。
首先化线段为点,建虚点连向端点为
0
0
0 的线段,以虚点为根,那么删边问题就变成删点问题了。
我们假设
d
f
s
dfs
dfs 函数会返回进行操作后的值,那么
A
A
A 肯定希望这个值越大越好,
B
B
B 则反之,所以,我们在
d
f
s
dfs
dfs 函数中加入一个变量表示当前操作的是
A
A
A 还是
B
B
B ,就可以对
d
f
s
dfs
dfs 时的最大(小)值求解啦。
而对于消失的点,我们发现当某节点被删时,它的子树也一并被删去了,所以我们可以先预处理出来每个点的
d
f
s
dfs
dfs 序和子树大小
s
i
z
e
size
size ,再枚举子树节点打上标记即可。
int get_ans(int who,int ans)
{
// printf("%d %d\n",who,ans);
bool flag=1,pd=0;
int mzh=0,an1=-21000000,an2=21000000;
for(int i=1;i<=tot;i++)
{
if(bz[tree[i].dfn]==0)
{
flag=0;
if(tree[i].col==who)
{
pd=1;
for(int j=tree[i].dfn;j<=tree[i].dfn+tree[i].sz-1;j++)
bz[j]++;
int k=get_ans(change(who),tree[i].val*who+ans);
an1=max(an1,k),an2=min(an2,k);
for(int j=tree[i].dfn;j<=tree[i].dfn+tree[i].sz-1;j++)
bz[j]--;
}
}
}
if(flag)
return ans;
if(!pd)
an1=get_ans(change(who),ans),an2=an1;
if(who==1)
return an1;
else
return an2;
}
以上为
d
f
s
dfs
dfs 实现代码。
但是,很遗憾,这种做法有一个点T掉了。。。
难道真的要打记搜吗?
本着能不打记搜就不打记搜的理念,回顾一下刚刚的算法有什么可以优化的地方。。。
我们发现更新标记的步骤整整用掉了
O
(
n
)
O(n)
O(n) 的时间复杂度,这让本来就是
2
2
2 的幂次时间复杂度算法更是雪上加霜。
那我们怎么将这一段打上标记呢?
有一个很直观的想法就是打线段树,但是线段树很长,而且
O
(
n
)
O(n)
O(n) 优化到
O
(
l
o
g
n
)
O(log n)
O(logn) 似乎并没有多大的优化QAQ。
但我们有差分啊!按照
d
f
s
dfs
dfs 序枚举,设变量
a
a
a 表示这时的节点的
a
a
a 个祖先被删掉了,那我们修改时只需要将
d
f
s
i
dfs_i{}
dfsi 和
d
f
s
i
+
s
i
z
e
i
−
1
dfs_{i}+ size_{i}-1
dfsi+sizei−1 进行修改即可,时间复杂度是
O
(
1
)
O(1)
O(1) 的,所以暴力的时间复杂度就降为
O
(
2
n
n
)
O(2^n n)
O(2nn) 的啦。
AC代码
#include<cstring>
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
struct node{
int col,val,dfn,sz;
}tree[30];
struct zlz{
int head,to,nxt,val,col;
}edge[66],edge2[66];
int n,cnt,a,b,c,d,tot,cnt2,pop=-21000000,num,to[2020];
int bz1[2020],bz2[2020];
inline int read()
{
int num=0,f=1;
char ch=getchar();
while(!(ch>='0'&&ch<='9'))
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
num=num*10+ch-'0';
ch=getchar();
}
return num*f;
}
void xx(int u,int v,int w,int x)
{
edge[cnt].to=v;
edge[cnt].col=w;
edge[cnt].val=x;
edge[cnt].nxt=edge[u].head;
edge[u].head=cnt;
cnt++;
}
void xx2(int u,int v)
{
edge2[cnt2].to=v;
edge2[cnt2].nxt=edge2[u].head;
edge2[u].head=cnt2;
cnt2++;
}
void dfs(int x,int father,int lst)
{
for(int i=edge[x].head;i!=-1;i=edge[i].nxt)
{
int y=edge[i].to;
if(y==father)
continue;
tot++;
tree[tot].col=edge[i].col;
tree[tot].val=edge[i].val;
xx2(tot,lst),xx2(lst,tot);
dfs(y,x,tot);
}
}
void build(int x,int father)
{
num++,tree[x].dfn=num,to[num]=x;
tree[x].sz=1;
for(int i=edge2[x].head;i!=-1;i=edge2[i].nxt)
{
int y=edge2[i].to;
if(y==father)
continue;
build(y,x);
tree[x].sz+=tree[y].sz;
}
return;
}
int get_ans(int who,int ans)
{
bool flag=1,pd=0;
int an1=-21000000,an2=21000000,mzh=0;
for(register int i=2;i<=tot+1;i++)
{
mzh-=bz2[i];
mzh+=bz1[i];
if(mzh==0)
{
flag=0;
if(tree[to[i]].col==who)
{
pd=1;
bz1[i]++;
bz2[i+tree[to[i]].sz]++;
int k=0;
if(who==1)
k=get_ans(-1,ans+who*tree[to[i]].val);
else
k=get_ans(1,ans+who*tree[to[i]].val);
an1=max(an1,k),an2=min(an2,k);
bz1[i]--;
bz2[i+tree[to[i]].sz]--;
}
}
}
if(flag)
return ans;
if(!pd)
{
if(who==1)
an1=get_ans(-1,ans);
else
an1=get_ans(1,ans);
an2=an1;
}
if(who==1)
return an1;
else
return an2;
}
int main()
{
memset(edge,-1,sizeof(edge));
memset(edge2,-1,sizeof(edge2));
n=read();
for(register int i=1;i<=n;i++)
{
a=read(),b=read(),c=read(),d=read();
xx(a,b,c,d),xx(b,a,c,d);
}
dfs(0,0,0);
build(0,0);
bool p=0;
for(register int i=2;i<=tot+1;i++)
if(tree[to[i]].col==1)
{
p=1;
bz1[tree[to[i]].dfn]=1;
bz2[tree[to[i]].dfn+tree[to[i]].sz]=1;
pop=max(pop,get_ans(-1,tree[to[i]].val));
bz1[tree[to[i]].dfn]=0;
bz2[tree[to[i]].dfn+tree[to[i]].sz]=0;
}
if(!p)
{
pop=0;
for(int i=1;i<=tot;i++)
pop-=tree[i].val;
}
printf("%d\n",pop);
}