1. 程式人生 > >NOIP2018三校聯考--電壓機制(voltage)

NOIP2018三校聯考--電壓機制(voltage)

很容易發現與奇環和偶環有關,但是仔細想清楚和怎麼用程式實現還是很難的

但是我們dfs一邊後,會發現能形成環的邊都變成了返祖邊

而且這些返祖邊對於答案大部分情況下都沒有貢獻

偶環上的邊都不能做出貢獻,當且僅當一條邊被所有奇環包含時才能做出貢獻

有兩個性質如下:

性質1:如果有超過兩個奇環,則所有非樹邊都沒有貢獻,否則有1的貢獻

性質2:兩條特殊邊組合也不會對答案產生任何貢獻

情況1:                                情況2:                              情況3:

 

列舉後就可以證明以上的規律

那麼每條返祖邊都是修改通往根節點的一條鏈,就可以用查分非常簡單的實現

程式碼如下:(昨天被STL迷住了,所以寫了又慢又醜的迭代器STL程式碼)

#include<bits/stdc++.h>
using namespace std;
vector <pair<int,int> > edge[400005];
int vis[400005],n,m,one[400005],two[400005],ans,depth[400005];
int shu1,shu2,sum1,sum2,pd[400005],father[400005];
void dfs1(int x)
{
	pd[x]=1;
	for(vector <pair<int,int> > ::iterator it=edge[x].begin(),ed=edge[x].end();it!=ed;it++)
	{
		int y=it->second;
		if(vis[it->first]) continue;
		vis[it->first]=vis[(it->first)^1]=1;
		if(pd[y])
		{
			if((depth[x]&1)==(depth[y]&1))
			{
				one[x]++;
				one[y]--;
				sum1++;
			}
			else 
			{
				two[x]++;
				two[y]--;
				sum2++;
			}
		}
		else 
		{
			father[y]=it->first;
			depth[y]=depth[x]+1;
			dfs1(y);
		}
	}
}
void dfs2(int x)
{
	for(vector <pair<int,int> > ::iterator it=edge[x].begin(),ed=edge[x].end();it!=ed;it++)
	{
		int y=it->second;
		if(father[y]==it->first)
		{
			dfs2(y);
			one[x]+=one[y];
			two[x]+=two[y];
		}
	}
}
int main()
{
	freopen("voltage.in","r",stdin);
	freopen("voltage.out","w",stdout);
	cin>>n>>m;
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&shu1,&shu2);
		edge[shu1].push_back(make_pair(i*2,shu2));
		edge[shu2].push_back(make_pair(i*2+1,shu1));
	}
	depth[1]=1;
	dfs1(1);
	dfs2(1);
	for(int i=1;i<=n;i++)
	{
		if(father[i]&&one[i]==sum1&&two[i]==0) ans++;
	}
	if(sum1==1) ans++;
	cout<<ans;
	return 0;
}