【HDU5743 2016 Multi-University Training Contest 2J】【dfs展开式DP 前缀和思想】Join The Future 40个数已知区间和为奇或偶输出方案
2016-07-28 10:39
555 查看
Join The Future
Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 118 Accepted Submission(s): 43
[align=left]Problem Description[/align]
Professor Zhang has an array of n integers.
He writes down some properties about the array in the paper. Each property is described by three integers li, ri and si,
which means that the sum of elements modulo 2 on interval [li,ri] of
the array is equal to si.
After that, he tries to recover the array only using the above properties. Apparently, there are many such arrays. So, Professor Zhang decides to limits the lower bound and upper bound of each integer in the array.
Given the properties, the lower bounds and the upper bounds, find the number of possible arrays and the lexicographically smallest array.
[align=left]Input[/align]
There are multiple test cases. The first line of input contains an integer T,
indicating the number of test cases. For each test case:
The first line contains two integers n and m (1≤n≤40,0≤m≤n(n+1)2) --
the length off array and the number of properties.
Each of the next n lines
contains two integers xi and yi (0≤xi≤yi≤109) --
the lower bound and upper bound of the i-th
integer.
Each of the next m lines
contains three integers li, ri and si (1≤li≤ri≤n,0≤si≤1) denoting
the i-th
property.
[align=left]Output[/align]
For each case, output the number of possible arrays in the first line. As the value could be very large, print it modulo 109+7.
Then, output the lexicographically smallest array in the second line. If the number of possible arrays equals to zero, just output "-1" (without the quotes) in the second line.
[align=left]Sample Input[/align]
3
3 3
1 10
0 21
3 15
2 2 1
3 3 0
2 3 1
3 0
0 1
1 3
3 4
3 3
1 10
0 21
3 3
2 2 1
3 3 0
2 3 1
[align=left]Sample Output[/align]
660
1 1 4
12
0 1 3
0
-1
[align=left]Author[/align]
zimpha
[align=left]Source[/align]
2016 Multi-University Training
Contest 2
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); }
#define MS(x,y) memset(x,y,sizeof(x))
#define MC(x,y) memcpy(x,y,sizeof(x))
#define MP(x,y) make_pair(x,y)
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b>a)a = b; }
template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b<a)a = b; }
const int N = 42, M = 0, Z = 1e9 + 7, ms63 = 0x3f3f3f3f;
int casenum, casei;
int n, m;
LL b
; //对应二进制,b[i] = 1ll << i
int l
, r
; //记录对于每个点,其可以选数的区间范围
LL cnt
[2]; //记录对于每个点,其可以选择奇数或偶数的方案数
vector< pair<int, int> >a
; //存边,用于找到状态之间的制约关系
int vis
; //确定每个点的遍历状态,从而找出制约关系
LL con
; //con[i]表示在i状态确定的条件下,哪些点的状态是确定的
LL odd
[2]; //odd[i][j]表示在i状态为偶数(j==0)或奇数(j==1)的条件下,哪些点的状态确定为奇数
int f
[2]; //记录每次dfs的状态
int ans; //记录答案方案数
int way
; //记录最小字典序的解
void add(int &x, int y)
{
if ((x += y) >= Z)x -= Z;
}
void update(LL vis, LL sta)
{
LL pre = 0;
bool better = 0;
for (int i = 1; i <= n; ++i)
{
int now;
if (vis & b[i])
{
now = l[i] + ((l[i] & 1) != ((sta >> i & 1) ^ pre));
}
else now = l[i];
if (better)way[i] = now;
else
{
if (now > way[i])return;
if (now < way[i])
{
better = 1;
way[i] = now;
}
}
pre = pre ^ (now & 1);
}
}
void dfs(int p, LL vis, LL sta)
{
if (p > n)
{
int tmp = 0;
for (int i = 0; i < 2; ++i)
{
if ((vis & b
) && (sta >> n & 1) != i)continue;
add(tmp, f
[i]);
}
if (tmp)update(vis, sta), add(ans, tmp);
return;
}
f[p][0] = f[p][1] = 0;
for (int i = 0; i < 2; ++i) //枚举当前该点的前缀和状态
{
if ((vis & b[p]) && (sta >> p & 1) != i)continue;
for (int j = 0; j < 2; ++j) //枚举上一个点的前缀和状态
{
if ((vis & b[p - 1]) && (sta >> p - 1 & 1) != j)continue;
add(f[p][i], f[p - 1][j] * cnt[p][i ^ j] % Z);
}
}
if (a[p].empty())dfs(p + 1, vis, sta);
else
{
for (int i = 0; i < 2; ++i)if (f[p][i])
{
dfs(p + 1, vis | con[p], sta | odd[p][i]);
}
}
}
void solve()
{
//找制约关系
for (int i = 0; i <= n; ++i)
{
con[i] = odd[i][0] = odd[i][1] = 0;
MS(vis, -1); queue<int>q;
vis[i] = 1; q.push(i);
while (!q.empty())
{
int x = q.front(); q.pop();
con[i] |= b[x];
odd[i][vis[x]] |= b[x];
for (int j = a[x].size() - 1; ~j; --j)
{
int y = a[x][j].first; int s = a[x][j].second;
if (~vis[y])
{
if ((vis[x] ^ vis[y]) != s)
{
puts("0"); puts("-1");
return;
}
}
else
{
vis[y] = vis[x] ^ s; q.push(y);
}
}
}
}
//求方案数与最优方案
for (int i = 1; i <= n; ++i)way[i] = Z; ans = 0;
f[0][0] = 1; dfs(1, con[0], odd[0][0]);
if (way[1] == Z) { puts("0"); puts("-1"); }
else
{
printf("%d\n", ans);
for (int i = 1; i <= n; ++i)printf("%d%c", way[i], " \n"[i == n]);
}
}
int main()
{
for (int i = 0; i < N; ++i)b[i] = 1ll << i;
scanf("%d", &casenum);
for (casei = 1; casei <= casenum; ++casei)
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i)
{
scanf("%d%d", &l[i], &r[i]);
int x = l[i] & 1; int y = x ^ 1;
cnt[i][x] = (r[i] - l[i]) / 2 + 1;
cnt[i][y] = (r[i] - l[i] + 1) - cnt[i][x];
}
for (int i = 0; i <= n; ++i)a[i].clear();
for (int i = 1; i <= m; ++i)
{
int l, r, s;
scanf("%d%d%d", &l, &r, &s); --l;
a[l].push_back(MP(r, s));
a[r].push_back(MP(l, s));
}
solve();
}
return 0;
}
/*
【题意】
有n(40)个数a[],每个数都未知,不过对应着一个选数的上下界bot[],top[],就是a[i]的选数范围为[bot[i],top[i]]
然后有m(C(n,2))个限制关系,对于每个限制关系,我们告诉你——
{a[l]+...+a[r]}的和为偶数还是奇数。
让你判定在满足所有限制关系的条件下,{a[]}有多少种可行方案。
输出方案数和满足要求的最小字典序的解。
【类型】
前缀和思想 DP
【分析】
这道题的解决,首先要引入前缀和思想。
就是,我们知道了a[l]+a[r]的奇偶性,其实相应知道的是——sum[r]-sum[l-1]的奇偶性。
于是,我们所抽象出的每个点,都表示一个前缀和。
然后,我们可以预处理出——
如果一个点的状态确定了,哪些点的状态也是确定的。
这个可以通过端点并查集或者简单的暴力bfs(dfs)实现。
我们除了知道,"如果一个点的状态确定了,哪些点的状态也是确定的。"之外,
我们还可以知道,如果一个点的状态确定了,哪些点的状态是奇数;哪些点的状态是偶数。
(当然,如果存在矛盾和冲突,也是可以判定出来的)
这个的实现,我们可以——
枚举这个点是奇数还是偶数,然后就知道搜到其他点的状态是奇数还是偶数了。
如果搜到的点要求为奇数,我们就把这个状态押进[起点为奇数or偶数]里。
本程序的写法是一个比较机缘巧合的精妙写法——
我们使得起点为奇数,
如果搜到的目标点为奇数,那么[起点][1(目标点为奇数)] |= 目标点状态
其实也恰好为[起点][起点为奇数] 记录着哪些目标点为奇数
如果搜到的目标点为偶数,那么[起点][1(目标点为偶数)] |= 目标点状态
这个对称一下,就是——[起点][起点为偶数] 记录着哪些目标点为奇数
这个是我们程序的预处理过程。
(其实还预处理出了,每个位置选择为奇数或偶数的方案数)
=============================================================================
之后我们要展开DP了(其实这个DP,本质也可以称作为搜索)
一开始,我们只有0状态是确定的,这个前缀和显然只能是0,
这个点可能捆绑确定了一些状态。
我们通过vis(哪些点状态是确定的)和sta(这些确定点具体是奇数还是偶数)来表示状态。
其实当然,我们可以用三进制数来表示我们的具体状态(用UL刚好存得下),并没有什么区别。
我们做这个dfs的时候(相比于DP,我还是更愿意称其为dfs),
我们实际是按照依次枚举这41个点的状态展开的。
这41个点,对应着41个前缀和。也对应着40个需要填充的位置。
过程中会出现这么一些状况——
1,该点的状态已经确定了,我们不需要枚举了。
2,该点的状态未确定,我们需要枚举该点的状态。
具体的写法可以是这样子——
在递归搜索的过程中,我们始终用vis 和 sta,记录"哪些节点的状态确定,具体是什么"
然后,对于当前处理的前缀和(我们按照从前向后的顺序枚举,使得所有前缀和的状态都变得确定化)。
如果其状态是确定的,那么我们可以直接算出该位置的选择方案,并进入下层递归
如果其状态是不确定的,那么我们要枚举该位置是奇数还是偶数,
并积累答案贡献,同时传递下在该点位置确定的条件下,新的vis和sta状态。
等等!
这里有一个重要细节。
如果当前点是一个孤立点,就是与其它状态不发生关系,
那对于其搜索树的展开,我们是不分叉的。
因为:之后的状态如果有要求,我们依然可以从合法的前驱状态中获得继承。
之后的状态如果没要求,我们会枚举之后的状态,依然能够得到其合法前驱。
这样子,我们看到,一个节点只有在——
之前状态对其没有要求,其与之后的点产生关联性的时候,会产开分叉的状态搜索。
显然,这样的分叉最多只会产开n/2次。
于是,复杂度控制在O(2^20)以内,我们可以轻松AC。
【时间复杂度&&优化】
O(2 ^ 20)
*/
相关文章推荐
- 【HDU5565 BestCoder Round 62 (div1)C】【STL or 二分答案 or 计数排序】Clarke and baton n个人减肥m次求最后异或值
- 【HDU5564 BestCoder Round 62 (div1)B】【DP转矩阵快速幂】Clarke and digits 长度在[l,r]范围内7倍数数个数要求相邻两位不为K
- 【FOJ2207 11月月赛C】【DFS栈性质应用 离线处理】以撒的结合 从x到y路径上的第k个点 询问众多
- 【Educational Codeforces Round 1D】【DFS 联通块打标记法】Igor In the Museum 联通块内墙的面数
- 【Educational Codeforces Round 1E】【动态规划-多维DP】Chocolate Bar 矩形巧克力掰开吃的最小成本
- 【HDU5544 2015CCPC 南阳国赛E】【树上dfs找本质不同环 高斯消元 时间戳优化】Ba Gua Zhen 连通图上最大异或环
- 【HDU5222 2015赛码冠军杯I】【并查集找双连通 + tarjan求强连通】Exploration 双向边只能走一边是否图上存在环
- 【HDU5188 BestCoder Round 33C】【贪心排序+DP】zhx and contest 考试不被怀疑作弊条件下达到至少m分的最少时间
- 【HDU4560 2013西山居复赛D】【二分答案+网络流拆点】我是歌手 安排演唱会_每人歌不同_每场歌不同_人歌匹配一次
- 【HDU5411 2015 Multi-University Training Contest 10F】【矩阵快速幂 加一行构造法】CRB and Puzzle 矩阵的1次方到n次方的数值和
- 【HDU5570 BestCoder Round 63 (div1)C】【期望DP 公式化简】balls n种求m种颜色,同颜色球数为x贡献为x方 求期望
- 【HDU5569 BestCoder Round 63 (div1)B】【DP】matrix 向右走向下走最大乘积和
- 【HDU5568 BestCoder Round 63 (div1)A】【DP java高精度】sequence2 长度恰好为m的LIS数
- 【Codeforces Round 333 (Div 2)E】【期望DP概率做法 树状数组转前缀和】Kleofáš and the n-thlon n场比赛m个人获得总名次的期望
- 【HDU5579 2015上海赛区G】【超级大讨论】Game of Arrays a[]+b[]+c[]有些位置可以减一,状态是否可能达成
- 【HDU5583 2015上海赛区L】【找规律 正难则反】LCM Walk 目标状态(x,y)哪些点走公倍数能走到它
- 【HDU5573 2015上海赛区B】【构造 二进制思想】Binary Tree 二叉树上走m层加减数使得最后权值恰为n
- 【HDU5583 2015上海赛区K】【暴力合并】Kingdom of Black and White 连续01串权值贡献为len^2最多改变一次最大权值
- 【HDU5578 2015上海赛区F】【水题】Friendship of Frog 最近的两个相同字符的距离是多少
- 【HDU5586 BestCoder Round 64 (div1)A】【贪心 最大连续子串】Sum 区间函数值变换使得数列权值和最大