bzoj-2278 Garbage

141 篇文章 0 订阅
44 篇文章 0 订阅

题意:

给出一个n个点m条边无向图,每个边有一个01状态;

现在可以选择一些环,使环上的所有状态取反;

给出初始与结束状态,求一个方案;

1<=n<=100000,1<=m<=1000000;


题解:

做完这题深刻的体会到自己的too young too naive;

首先有一个性质,如果有解,一定存在一种方案使选择的所有环不重复经过一条边;

这个性质也说明了,所有不需要更改的边是可以忽视的;

因为如果将其选了偶数次,也可以构造出另一种方案不选它;

所以问题转化成从原图选出状态不同的边,然后求多个欧拉回路覆盖所有边的方案;

无向图存在欧拉回路的条件是结点的度都是偶数;

判掉NIE的情况之后,开始求图的欧拉回路。。

然后我写个搜索就被卡了!并不是常数的问题而是复杂度不对;

首先大多数人的图都是用链式前向星存对吧;

那么每次搜到一个点遍历一遍边表找没被删除的边的时候。。

这就给了丧心病狂的人可乘之机!

给出一个是:

这个样子的菊花图,5W个褶,然后扫着扫着就超时了;

所以我们要更新前向星中的head[x],每次删除的边都是最上面的,只要将head[x]赋为某个next就行了;

时间复杂度O(n+m),读入较大,输出较大,常数较大;

在连续几道题垫底的节奏下终于有一道题Rank1啦好开心;


代码:


#include<cctype>
#include<vector>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 131072
#define M 2100000
#define LEN 1<<20
using namespace std;
int to[M],next[M],head[N],ce=1;
int du[N];
bool ban[M],vis[N];
int cnt;
char getc()
{
	static char *S,*T,buf[LEN];
	if(S==T)
	{
		T=(S=buf)+fread(buf,1,LEN,stdin);
		if(S==T)
			return EOF;
	}
	return *S++;
}
int read()
{
	static char ch;
	static int D;
	while(!isdigit(ch=getc()));
	for(D=ch-'0';isdigit(ch=getc());)
		D=D*10+ch-'0';
	return D;
}
namespace ans
{
	int val[M],next[M],head[N<<1],size[N<<1],ce;
	void add(int x,int y)
	{
		size[x]++;
		val[++ce]=y;
		next[ce]=head[x];
		head[x]=ce;
	}
}
void add(int x,int y)
{
	to[++ce]=y;
	next[ce]=head[x];
	head[x]=ce;
}
int dfs(int x,int pre)
{
	vis[x]=1;
	int i,y;
	for(i=head[x];i;i=next[i])
	{
		if(ban[i]||(i^1)==pre)	continue;
		if(!vis[to[i]])
		{
			y=dfs(to[i],i);
			ans::add(cnt,x);
			ban[i]=ban[i^1]=1;
			head[x]=next[i];
			if(x!=y)
			{
				vis[x]=0;
				return y;
			}
		}
		else
		{
			cnt++;
			ans::add(cnt,to[i]);
			ans::add(cnt,x);
			ban[i]=ban[i^1]=1;
			head[x]=next[i];
			vis[x]=0;
			return to[i];
		}
	}
	vis[x]=0;
	return 0;
}
int main()
{
	int n,m,i,j,k,x,y,z,w;
	n=read(),m=read();
	for(i=1;i<=m;i++)
	{
		x=read(),y=read(),z=read(),w=read();
		if(z!=w)
			add(x,y),add(y,x),du[x]++,du[y]++;
	}
	for(i=1;i<=n;i++)
	{
		if(du[i]&1)
		{
			puts("NIE");
			return 0;
		}
	}
	for(i=1;i<=n;i++)
	{
		if(head[i])
			dfs(i,0);
	}
	printf("%d\n",cnt);
	for(i=1;i<=cnt;i++)
	{
		printf("%d ",ans::size[i]-1);
		for(j=ans::head[i];j;j=ans::next[j])
		{
			printf("%d ",ans::val[j]);
		}
		printf("\n");
	}
	return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值