P6185 [NOI Online #1 提高組] 序列

給定兩個長度爲n的序列\(A\)\(B\)

有m個可用的操作\((t_i,u_i,v_i)\)

\(t\)代表操作類型。

\(t=1\)時,表示能夠將\(A_{u_i}\)\(A_{v_i}\)同時\(+1\)

\(t=2\)時,表示能夠將\(A_{u_i}\)\(A_{v_i}\)其中一者\(+1\),另者\(-1\)

所有操作使用順序和次數不限,問能否使得\(A\)變成\(B\)

link

sol

先將類型\(2\)的邊連接。我們會得到一些連通塊。

由於連通分量中任意兩點都存在路徑,所以對於任意\((u,v)\)我們可以通過\((u,v)\)這條路徑使得\(A_u+1\)\(A_v-1\)

由此易得對於一個連通分量,我們可以在不改變整個連通分量點權和的前提下任意改變其中節點的值。

這部分就可以拿並查集縮點掉了(

縮點後將類型\(1\)的邊連接。

(1)此時的圖如果是二分圖,那麼顯然,我們可以在二分圖兩側增量相同的前提下任意改變節點的值。

(2)如果不是二分圖,也就是存在奇環,顯然我們可以通過【一個節點+這個節點到奇環的路徑+這個奇環】這樣一個組合來使得這個節點 \(\pm\) 一個偶數。
由此易得,我們可以在點權和增量爲偶數的前提下任意改變其中節點的值。

然後寫法就很顯然了()

先連類型\(2\)的邊,用並查集染色縮點並計算縮點後點權;再連\(1\)邊。對於新圖的每個連通分量,用黑白染色判定是否爲二分圖,染色過程分別統計黑白增量\(wb_i-wa_i\)的和。如果是二分圖就判斷黑白增量是否相等,如果不是就判斷(僞)黑白增量(即總增量)的總和是否爲偶數
我不知道負數mod會發生什麼()所以判斷增量奇偶的時候+了個巨大偶數

第一次編譯運行沒過樣例 + 第一次提交 WA 55pts 錯在同個地方,就是並查集染色後建邊的地方,前者是因爲沒按顏色建邊而按點編號建邊,後者是因爲沒建雙向邊

沒因爲多測初始化的問題掛掉,還好...

但雖然整體蠻順利,完成代碼本身還是花了將近1h的樣子。太慢了(sad(雖然我一邊寫還在反覆嘗試嚴謹證,不過最後一寫總結就清楚了

#include <algorithm> 
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#define MAXN (int)(1e5+233)
#define MAXM (int)(1e5+233)
#define MAXAB (int)(1e9+233)
using namespace std;
int n,m;
int a[MAXN],b[MAXN];
long long wa[MAXN],wb[MAXN];
long long sum1,sum2;
struct qwq
{
	int nex,to;
}e[MAXM<<1];
int h[MAXN],tot=0;
struct edgee
{
	int u,v;
}E[MAXM];
int Ecnt=0;
inline void add(int x,int y)
{
	e[++tot].to=y;
	e[tot].nex=h[x];
	h[x]=tot;
}
int fa[MAXN];
int found(int x)
{
	if (fa[x]==x) return x;
	return fa[x]=found(fa[x]);
}
int col[MAXN],c_count=0;
int typ[MAXN];
inline void INIT() { for (int i=1;i<=n;i++) fa[i]=i,h[i]=0,typ[i]=0,wa[i]=0,wb[i]=0,col[i]=0; tot=0; c_count=0; Ecnt=0; }

bool dfs_bina(int x)
{
	if (typ[x]==1) sum1+=(wb[x]-wa[x]);
	else if (typ[x]==2) sum2+=(wb[x]-wa[x]);
	bool wnw=true;
	for (int i=h[x],y;i;i=e[i].nex)
	{
		y=e[i].to;
		//printf("Edge: %d %d\n",x,y);
		if (!typ[y])
		{
			typ[y]=3-typ[x];//rem INIT typ[x]
			if (!dfs_bina(y)) wnw=false;
		}
		else if (typ[y]==typ[x]) wnw=false;
	}
	return wnw;
}

inline bool sol2(int x)
{
	sum1=sum2=0;
	typ[x]=1;
	bool wnw=dfs_bina(x);
	
//	printf(":::%d %lld %lld\n",wnw,sum1,sum2);
	
	if (wnw)
	{
		if (sum1==sum2) return true;
		else return false;
	}
	else
	{
		if ((sum1+sum2+1000000000)&1) return false;
		else return true;
	}
}

inline void sol()
{
	scanf("%d%d",&n,&m);
	INIT();
	for (int i=1;i<=n;i++) scanf("%d",&a[i]);
	for (int j=1;j<=n;j++) scanf("%d",&b[j]);
	for (int i=1,t,u,v,fu,fv;i<=m;i++)
	{
		scanf("%d%d%d",&t,&u,&v);
		if (t==1) E[++Ecnt]=(edgee){u,v};
		else
		{
			fu=found(u); fv=found(v);
			if (fu!=fv) fa[fu]=fv;
		}
	}
	for (int i=1;i<=n;i++)
		if (fa[i]==i) col[i]=++c_count;
	for (int i=1;i<=n;i++)
		col[i]=col[found(i)],wa[col[i]]+=a[i],wb[col[i]]+=b[i];
	for (int i=1;i<=Ecnt;i++)
//		if (col[E[i].u]!=col[E[i].v])
			add(col[E[i].u],col[E[i].v]),add(col[E[i].v],col[E[i].u]);
	bool answer=1;
	for (int i=1;i<=c_count;i++)
		if (!typ[i]) answer&=sol2(i);
	if (answer) printf("YES\n");
	else printf("NO\n");
	return;
}
int main()
{
	int T;
	scanf("%d",&T);
	while (T--) sol();
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章