植树方案
题目描述
T国打算种一批树。所谓树,就是由 N 个结点与 N-1 条边连接而成的连通无向图。T国的国王对于这些树有下列要求:
1、树没有根,但它的形态是给定的(即这 N-1 条边是给出的);
2、树的每条边上可以放置一朵花(当然也可以不放置);
3、共 Q 条约束,第 i 组约束规定:标号 ui 的结点到标号 vi 的结点的简单路径上,花的数量为奇数或偶数。
现在,国王想事先知道他最多能种多少棵不一样的树(两棵树被视为不一样当且仅当一棵树中某条边的放花情况与另一棵树不相同)。
输入格式
第 1 行两个整数 N, Q ;
接下来 N-1 行,每行两个整数 u、v,表示 u、v 间存在一条边;
接下来 Q 行,每行三个整数 ui、vi、ki,若 ki 为 0,表示 ui 到 vi 的简单路径上花的个数为偶数,否则为奇数。
输出格式
一行一个整数,表示能种的不一样的树的种数,对 998244353 取模。
样例数据 1
输入
5 2
1 2
1 3
1 5
4 5
1 5 0
2 4 1
输出
4
备注
【数据范围】
对于 20% 的数据,N,Q≤20;
对于 40% 的数据,N,Q≤100;
对于 70% 的数据,N,Q≤5000;
对于 100% 的数据,N,Q≤100000、0≤ki≤1。
解析:
好题!
事实证明题目里面的原树是没什么用的。。。
考虑用约束建树,若AB两点之间花的数量为偶数,则AB之间建一条权值为0的无向边,奇数同理,这样整棵树就被分成了若干个连通块。对于每个连通块,从一点出发,每个点的点权代表到根节点的边权抑或和(根节点只能为0),根据抑或边的权值确定对用端点的位置,如果有冲突则ans=0,否则ans*2,注意根节点所在连通块只有一种情况。
如果仍然不清楚可以借鉴zwk大佬的博客, [ASDFZ-NOIP2016模拟]植树方案。
代码:
#include <bits/stdc++.h>
using namespace std;
const int Max=1000100;
const int mod=998244353;
int n,m,s,ans;
int first[Max],num[Max],father[Max],way[Max],vis[Max];
struct shu{int to,next,len;};
shu edge[Max<<1];
inline int get_int()
{
int x=0,f=1;
char c;
for(c=getchar();(!isdigit(c))&&(c!='-');c=getchar());
if(c=='-') {f=-1;c=getchar();}
for(;isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+c-'0';
return x*f;
}
inline void build(int x,int y,int z)
{
edge[++s].next=first[x];
first[x]=s;
edge[s].to=y;
edge[s].len=z;
}
inline int getfather(int v)
{
return father[v] == v ? v : father[v] = getfather(father[v]);
}
inline bool check(int x,int y,int z)
{
return num[x] ^ z == num[y] ? 1 : 0;
}
inline bool dfs(int point)
{
//cout<<point<<"\n";
vis[point] = 1;
for(int u=first[point];u;u=edge[u].next)
{
int to=edge[u].to;
if(vis[to])
{
if(!check(point,to,edge[u].len)) return 0;
continue;
}
num[to] = num[point] ^ edge[u].len;
return dfs(to);
}
return 1;
}
int main()
{
n=get_int();
m=get_int();
for(int i=1;i<=n-1;i++) int x=get_int(),y=get_int();
for(int i=1;i<=n;i++) father[i]=i;
for(int i=1;i<=m;i++)
{
int x=get_int(),y=get_int(),z=get_int();
build(x,y,z);
build(y,x,z);
int fax = getfather(x),fay = getfather(y);
if(fax != fay) father[fay] = fax;
}
ans=1;
for(int i=1;i<=n;i++)
{
int fa=getfather(i);
if(!way[fa])
{
way[fa] = 1;
num[i] = 0;
if(dfs(i)) ans = i==1 ? ans : (ans * 2) %mod;
else {ans=0;break;}
}
}
cout<<ans<<"\n";
return 0;
}