概述
参考:
Namori[agc-004F]-by ezhjw
editotial-AGC004
AGC004F-by 杨耀良
题意:
给你一棵树或者是基环树,每个节点可以为白色或者是黑色。你可以将相邻的,具有相同颜色的两个点同时反转颜色。初始的时候所有的节点都是白色的,你需要花费最少的步数来让所有的节点都变成黑色。如果无法达到输出-1。
数据范围:
1<=N<=10^5,N-1<=M<=N
思路:
首先可以想到的是这道题需要分类讨论。
首先是树的情况,因为树是一个二分图,所以说如果我们将深度为1,3,5…的节点看成是S集合,深度为2,4,6…的节点看成是T集合,那么就只能够是S集合中的元素与T集合中的元素进行同时反转。这样我们就可以再转换一下,假设S集合中的节点都是+1,并且假设有一个硬币,T集合中的节点都是-1,并且假设为空位,然后依次反转颜色就相当于是将+1和-1交换位置,也就是把硬币放到一个空位上去,然后目标状态就是将所有原来在S集合的点都变为T集合中的点,原来所有在T集合的点都变为S集合中的点,也就是所有的空位都被放上了硬币,并且原来有硬币的位置都变为了空位。如果说全局的-1和+1的数量不相同的时候,那么就是无解的状态。
然后我们来观察某棵子树,那么它也应该满足-1和+1的数量相同。但实际情况中可能是不相同的,那么就需要从外界引入硬币,或者是输出硬币。考虑子树的根节点为x,x的father为fa,那么就可以从x->fa的这条路径引入或者是输出硬币,而操作的次数正好是这个子树中+1的数目减去-1的数目的绝对值,也就是sum[x],也是原题中这条边需要操作的次数。那么答案就是 ∑ s u m [ i ] sum sum[i] ∑sum[i],sum[]表示这个子树中的 ∑ ( + 1 ) + ∑ ( − 1 ) sum (+1)+sum (-1) ∑(+1)+∑(−1),那么是一棵树的情况就解决了。
接下来是N=M的情况。
因为和二分图有关,所以说需要根据是奇环还是偶环进行分类讨论。
当是奇环的时候:
选择环上的一条边,那么删除这条边之后就是一棵树的情况了,那么我们就可以考虑这一条边所带来的影响,然后删掉它,在剩余的树里面继续讨论就可以了。因为是一个奇环,所以说这条边所连接的两个点可以认为就是出现矛盾的那个地方,那么这两个点就属于同一个集合。那么就可以在这两个点上面同时放上硬币或者是同时移除硬币。那么就可以将所有的矛盾的都转移到这条边上来,让它来消除矛盾(例如,如果硬币过多,就可以移过来,然后删掉,说过硬币少了,就可以在这里产生硬币,然后运出去)。我们发现,这条边能够让+1与-1的数目差同时-2,那么也就是说+1和-1的奇偶性相同的时候才是有解的,否则输出-1。然后,我们可以想成是这条边预先操作了固定的次数(就是abs(num[+1]-num[-1])/2),然后将对于两边的点的贡献先算出来,然后删去这条边,就可以归到树的情况了。
上面说的奇环上面的那条边能够做到同时+1或者是-1的问题,其实就是让硬币凭空消失或者是凭空出现。因为这两个点属于同一层,那么如果说这两个点都含有硬币,那么将这条边反转之后,这2个硬币都消失了。或者这样来解释:所有的点到这两个点的路径长度都相等,初始的时候这两个点上的颜色肯定是相同的,那么就可以让其中1个点沿着环运出去一种颜色,这时两个点的颜色就不同了。在下一次需求这种颜色时,就从另一个点运出去,那么这样子两个点的颜色就又相同了,那么就再反转一次这条边,变回了初始的状态。
当是偶环的时候:
这个时候还是一个二分图,所以说+1的数目和-1的数目仍然需要相等才会有解。然后仍然是选择一条边,然后考虑这条边操作了多少次。假设操作了x次(从u->v),那么它所造成的影响就是u,v(这条边的两端),u->lca(u,v)上所有点都-x,v->lca(u,v)上面所有点都+x,然后就可以删去这条边就可以是树的情况了。
这个时候答案就是:
A
n
s
=
m
i
n
(
a
b
s
(
x
)
+
∑
i
=
1
N
a
b
s
(
s
u
m
[
i
]
+
k
i
x
)
)
Ans=min(abs(x)+sum_{i=1}^{N}abs(sum[i]+k_ix))
Ans=min(abs(x)+i=1∑Nabs(sum[i]+kix))(x可以是负数)
ki可以是+1,-1,或者是0。0的话,就是在u->v这条路径之外的点,就可以直接按照树的算法来做;1的话就是v->lca(u,v)的情况;-1的话就是u->lca(u,v)的情况。然后就可以让k[u]=-1,k[v]=1,然后做一遍DFS将ki算出来,就可以做了。然后看上面的式子,可以发现其实是(-k)*sum[i]到x的距离的加和,而abs(x)就是0到x的距离。那么根据初中的知识,最优的一定是选择{(-k)*sum[]+0}中的中位数。
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define MAXN 100000
using namespace std;
typedef long long LL;
struct node
{
int to;
node *nxt;
}edges[MAXN*2+5];
node *ncnt,*Adj[MAXN+5];
int N,M,spx,spy;
LL sum[MAXN+5];
LL cirlen,dist[MAXN+5],xs[MAXN+5];
LL stk[MAXN+5],t;
bool vis[MAXN+5];
void Init()
{
ncnt=&edges[0];
}
void AddEdge(int u,int v)
{
node *p=++ncnt;
p->to=v;
p->nxt=Adj[u];
Adj[u]=p;
node *q=++ncnt;
q->to=u;
q->nxt=Adj[v];
Adj[v]=q;
}
void DFS(int u,int fa)
{
vis[u]=true;
for(node *p=Adj[u];p!=NULL;p=p->nxt)
{
int v=p->to;
if(v==fa)
continue;
if(vis[v]==true)
{
spx=u,spy=v;
cirlen=dist[u]-dist[v]+1;//之前莫名wa是因为这里u和v写反了...
continue;
}
dist[v]=dist[u]+1;
DFS(v,u);
}
}
void GetXs(int u,int fa)
{
vis[u]=true;
for(node *p=Adj[u];p!=NULL;p=p->nxt)
{
int v=p->to;
if(v==fa||vis[v]==true)//避免访问到之前找到的那一条特殊的边
continue;
GetXs(v,u);
xs[u]+=xs[v];
sum[u]+=sum[v];
}
}
int main()
{
// freopen("print.in","r",stdin);
// freopen("print.out","w",stdout);
Init();
scanf("%d %d",&N,&M);
int u,v;
for(int i=1;i<=M;i++)
{
scanf("%d %d",&u,&v);
AddEdge(u,v);
}
dist[1]=0;
DFS(1,-1);//计算深度/环的长度
LL ans=0,tot=0;
for(int i=1;i<=N;i++)
{
if(dist[i]%2==0)
sum[i]=-1LL;
else
sum[i]=1LL;
tot+=sum[i];
}
if(N-1==M)//处理树
{
if(tot!=0)
{
printf("-1n");
return 0;
}
}
else
{
if(cirlen%2==1)//处理奇环
{
if(tot%2!=0)
{
printf("-1n");
return 0;
}
sum[spx]-=tot/2;
sum[spy]-=tot/2;
ans+=abs(tot)/2;
}
else//处理偶环
{
if(tot!=0)
{
printf("-1n");
return 0;
}
xs[spx]=1LL;
xs[spy]=-1LL;
}
}
memset(vis,0,sizeof(vis));
GetXs(1,-1);//计算系数
for(int i=1;i<=N;i++)
{
if(xs[i]==0) ans+=abs(sum[i]);
else
{
if(xs[i]==1)
sum[i]=-sum[i];//乘上系数
stk[++t]=sum[i];
}
}
stk[++t]=0;
sort(stk+1,stk+1+t);
LL k=stk[(t+1)/2];
for(int i=1;i<=t;i++)
ans+=1LL*abs(stk[i]-k);
printf("%lldn",ans);
return 0;
}
最后
以上就是温暖百合为你收集整理的【AtCoder】【思维】【二分图】【模型转化】Namori(AGC004)的全部内容,希望文章能够帮你解决【AtCoder】【思维】【二分图】【模型转化】Namori(AGC004)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复