HDU 5743 Join The Future 给定部分区间和奇偶,和数字范围,求方案数 DP

题目大意:

给若干个(40个)数字,每个数字有一个取值范围。

给若干个区间【l,r】和flag表示奇偶

问,让[L,R]区间的数字,的数字和为奇/偶, 的方案总数


假设s[i]为前i个数字的前缀和。 对于L,R而言,就是s[r]和s[l-1]。


如果[L,R]为奇数,那么就是s[r]-s[l-1]为奇数。


我们并不十分关心s[i]是多少,只在意是奇还是偶,并且是否可行。


f[i][0/1]表示s[i]为偶/奇。


这样如果题目给了 【L,R】的话,那么s[l-1]和s[r]的奇偶关系就是关联的。


最后题目一定给出若干个连通快,连通块内的取值,决定一个值,就决定了整个连通快。

对于所有连通快,暴力一下就行了。题解大致描述的就是这样的方法


暴力后,可以知道每个s[i]的取值是任意,还是必须奇还是必须偶。这个问题DP就行了,记录方案比较麻烦。


切记: 题目可以出现一个数字的取值范围是[5,5]或者[7,7]这样的。 所以多出一些细节。


数据十分弱,不考虑也能过(网上我搜到的所有人的程序都过不了下面这组数据……2016年8月15日19:44:56  当然我能过QAQ,对拍怎么都过不了……)

1
3 2
2 5
4 4
4 6
1 2 1
1 3 1

答案无论如何,最小字典序的第二个数字也不会是5吧~? 网上不少人程序都是错的……

因为数据弱,我也不能保证我程序就是对的。

#include <bits/stdc++.h>
#include <ext/pb_ds/priority_queue.hpp>
#include <tr1/unordered_map>
using namespace std::tr1;
using std::sort;
using std::max;
using std::cout;
using std::stack;
using std::cin;
using std::endl;
using std::swap;
using std::pair;
using std::vector;
using std::set;
using std::map;
using std::multiset;
using std::queue;
using std::greater;
using std::string;
using std::priority_queue;
using std::max_element;
using std::min_element;
using __gnu_pbds::pairing_heap_tag;
__gnu_pbds::priority_queue<int, greater<int>, pairing_heap_tag> heap;
#define Hash unordered_map
#define pr(x) cout<<#x<<" = "<<x<<" "
#define prln(x)    cout<<#x<<" = "<<x<<endl



typedef long long LL;

LL mymin(LL a, LL b)
{
	if (a>b)	return b;
	return a;
}

const int maxn = 50;
int n, m;
int low[maxn], up[maxn];
struct edge{int will,relationship;};
vector<edge>g[maxn];
int block_cnt;
int belong[maxn], block_idx[maxn];
int f[maxn];
const LL mod = 1e9+7;
const int INF = 0x3f3f3f3f;

void init()
{
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; ++ i)
	{
		scanf("%d%d", &low[i], &up[i]);
	}
	for (int i = 0; i <= n; ++ i)    g[i].clear();
	for (int i = 1; i <= m; ++ i)
	{
		int a, b, c;
		scanf("%d%d%d", &a, &b, &c);
		--a;
		g[a].push_back({b,c});
		g[b].push_back({a,c});
	}
	memset(belong, 0, sizeof(belong));
	memset(f, -1, sizeof(f));
	block_cnt=0;
}
queue<int>q;
void bfs(int k)
{
	if (g[k].size() == 0)    return ;//孤立点
	while (!q.empty())q.pop();
	q.push(k);
	belong[k] = ++block_cnt;
	block_idx[block_cnt] = k;
	while (!q.empty())
	{
		int now = q.front();
		q.pop();
		for (auto x : g[now])
		{
			int will = x.will;
			if (belong[will])    continue;
			belong[will] = block_cnt;
			q.push(will);
		}
	}
}

bool ranse(int block, int colo)    //返回false则为无法染色
{
	int now = block_idx[block];
	for (int i = 0; i <= n; ++ i)
		if (belong[i] == block)    f[i] = -1;
	while (!q.empty())q.pop();
	q.push(now);
	f[now] = colo;
	while (!q.empty())
	{
		int now = q.front();
		q.pop();
		for (auto x : g[now])
		{
			int will = x.will;
			if (f[will] != -1 && f[will] != f[now] ^ x.relationship)    return false;
			if (f[will] == -1)
			{
				f[will] = f[now] ^ x.relationship;
				q.push(will);
			}
		}
	}
	return true;
}

int output[maxn];

LL get(int x, int flag)//flag 为0或1。  第x个数字,选0偶数,1奇数个数字,的总方案数
{
	if (low[x]%2 ==1 && up[x]%2==1)    return (up[x] - low[x]) / 2 + flag;
	if (low[x]%2  != up[x]%2)    return (up[x] - low[x] + 1) / 2;
	return (up[x] - low[x])/2 + (flag^1);
}

int getnum(int x, int flag)//得到x的最小 0偶数  1奇数
{
	if (low[x]%2 == flag)    return low[x];
	//else return low[x]+1;
	if (low[x] + 1 <= up[x])    return low[x] + 1;
	return 0x3f3f3f3f;    //表示无解
}


vector<int>gg[maxn][2];
vector<int>ww[maxn][2];
LL h[maxn][2];
LL p[maxn][2];
int ans;
int tmp_output[maxn];
int vis[maxn][2];

void find(int a, int b)
{
	vis[a][b] = 1;
	if (h[a][b] == 0)    return;
	if (a == n)
	{
		for (int i = 1; i <= n; ++ i)
		{
			if (tmp_output[i] == output[i])    continue;
			if (tmp_output[i] > output[i])    return;
			if (tmp_output[i] < output[i])    break;
		}
		for (int i = 1; i <= n; ++ i)
			output[i] = tmp_output[i];
		return;
	}
	if (gg[a][b].size() == 0)    return;

	if (gg[a][b].size() == 1)
	{
		int j = gg[a][b][0];
		tmp_output[a+1] = ww[a][b][0];
		if (vis[a+1][j])    return;
		find(a + 1, j);
		return;
	}
	//否则先走字典序小的

	if (ww[a][b][0] > ww[a][b][1])
	{
		swap(ww[a][b][0], ww[a][b][1]);
		swap(gg[a][b][0], gg[a][b][1]);
	}


	int j1 = gg[a][b][0];
	int j2 = gg[a][b][1];
	tmp_output[a+1] = ww[a][b][0];
	if (!vis[a+1][j1])    find(a +1, j1);
	tmp_output[a+1] = ww[a][b][1];
	if (!vis[a+1][j2])    find(a + 1, j2);
}

void check()
{
	for (int i = 0; i <=n;++i)
		for (int j = 0; j <=1;++j)    
		{
			gg[i][j].clear();
			ww[i][j].clear();
		}
	memset(h, 0, sizeof(h));
	memset(p,0x3f, sizeof(p));

	h[0][0] = 1;//方案总数

	for (int i = 1; i <= n; ++ i)
	{
		if (f[i] == -1)
		{
			if (h[i - 1][0])    //前一位可以是偶数
			{

				if (h[i - 1][0] * get(i , 0))
				{
					gg[i - 1][0].push_back(0);            
					ww[i - 1][0].push_back(getnum(i, 0));
					h[i][0] += h[i - 1][0] * get(i , 0);
					h[i][0] %= mod;
					p[i][0] = mymin(getnum(i, 0), p[i][0]);
				}

				if (h[i - 1][0] * get(i,  1))
				{
					gg[i - 1][0].push_back(1);
					h[i][1] += h[i - 1][0] * get(i,  1);
					h[i][1] %= mod;
					p[i][1] = mymin(getnum(i, 1), p[i][1]);
					ww[i - 1][0].push_back(getnum(i, 1));
				}

			}
			if (h[i - 1][1])
			{

				if (h[i - 1][1] * get(i, 1))
				{
					gg[i - 1][1].push_back(0);
					h[i][0] += h[i - 1][1] * get(i, 1);
					h[i][0] %= mod;
					p[i][0] = mymin(getnum(i, 1), p[i][0]);
					ww[i - 1][1].push_back(getnum(i, 1));
				}
				if (h[i - 1][1] * get(i, 0))
				{
					gg[i - 1][1].push_back(1);
					h[i][1] += h[i - 1][1] * get(i, 0);
					h[i][1] %= mod;
					p[i][1] = mymin(getnum(i, 0), p[i][1]);
					ww[i - 1][1].push_back(getnum(i, 0));
				}
			}

		}
		else
		{
			int v=f[i];
			if (h[i - 1][0] * get(i, v ^ 0))
			{
				gg[i - 1][0].push_back(v);
				h[i][v] += h[i - 1][0] * get(i, v ^ 0);
				h[i][v] %= mod;
				p[i][v] = mymin(getnum(i, v ^ 0), p[i][v]);
				ww[i - 1][0].push_back(getnum(i, v^0));
			}
			if (h[i - 1][1] * get(i, v ^ 1))
			{
				gg[i - 1][1].push_back(v);
				h[i][v] += h[i - 1][1] * get(i, v ^ 1);
				h[i][v] %= mod;
				p[i][v] = mymin(getnum(i, v ^ 1), p[i][v]);
				ww[i - 1][1].push_back(getnum(i, v^1));
			}
		}
		if (!h[i][0] && !h[i][1])
		{
			//无解
			return;
		}

	}
	memset(vis, 0, sizeof(vis));
	find(0, 0);
	ans = (ans + h[n][0] + h[n][1]) % mod;
}

void dfs(int deep)
{
	if (deep > block_cnt)
	{
		check();
		return;
	}
	if (ranse(deep, 0))    dfs(deep + 1);
	if (ranse(deep, 1))    dfs(deep + 1);
}

void doit()
{
	ans = 0;
	for (int i = 0; i <= n; ++ i)
		if (!belong[i])    bfs(i);
	memset(output, 0x3f, sizeof(output));
	memset(f, -1, sizeof(f));
	if (belong[0] == 1)
	{
		//cout<<"!"<<endl;
		if (ranse(1, 0))    dfs(2);
		//否则显然无解啊
	}else
	{
		dfs(1);
	}
	printf("%lld\n", ans);
	if (ans == 0)
	{
		printf("-1\n");
		return;
	}
	for (int i = 1; i < n; ++ i)    printf("%d ", output[i]);
	printf("%d\n", output[n]);
}

int main()
{
	int T;
	scanf("%d", &T);
	while (T--)
	{
		init();
		doit();
	}
	return 0;
}


数据生成器:

#include <cstdio>
#include <stack>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <ctime>
#include <queue>
#include <ext/pb_ds/priority_queue.hpp>
using namespace std;

#define pr(x) cout<<#x<<" = "<<x<<" "
#define prln(x)	cout<<#x<<" = "<<x<<endl
//#define pr(x) x
//#define prln(x) x

typedef long long LL;
const LL mod = 1e9 + 7;

int n, m;
int output[2][1000];

int get(int a,int b)//返回a~b之间的数字
{
	return rand()%(b-a+1)+a;
}

void pg(int l , int r, int h, int p)
{
	cout<<get(l,r)<<" "<<get(h,p)<<endl;
}

int ot1[10000],ot2[10000], ot3[10000],t;
int mp[100][100];

/*

2
3 3
4 6
2 3
3 4
1 3 0
2 3 1
3 3 1
3 2
3 5
1 3
3 4
2 2 0
2 3 0
   
*/

int main()
{
	srand(time(0));
	ios::sync_with_stdio(false);
	int T=5;
	cout <<T<<endl;
	while (T--)
	{
		int n = 40;
		t=0;
		memset(mp,-1,sizeof(mp));
		for (int i = 1; i <= rand()%71 ; ++ i)	//穷举大概有多少约数
		{
			int a,b;
			a=get(1,n);
			b=get(1,n);
			mp[a][b]=mp[b][a]=rand()%2;
		}
		for (int i = 1; i<=n;++i)
			for (int j = i; j <= n; ++ j)
				if (mp[i][j] !=-1)
				{
					++t;
					ot1[t]=i;
					ot2[t]=j;
					ot3[t]=mp[i][j];
				}
		cout <<n<<" "<<t<<endl;
		for (int i = 1; i  <= n; ++ i)
		{
			LL x,y;
			LL p=1e9;
			x=((LL)rand()*(LL)rand())%p+1;
			y=((LL)rand()*(LL)rand())%p+1;

			if (x>y)	swap(x,y);

			if (x==y)	y+=1;
			cout<<x<<" "<<y<<endl;
		}
		for (int i = 1;i<=t;++i)	cout<<ot1[i]<<" "<<ot2[i]<<" "<<ot3[i]<<endl;

	}
	return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值