[bzoj5287] [HNOI2018]毒瘤
题目描述
从前有一名毒瘤。
毒瘤最近发现了量产毒瘤题的奥秘。考虑如下类型的数据结构题:给出一个数组,要求支持若干种奇奇怪怪的修改操作(比如区间加一个数,或者区间开平方),并支持询问区间和。毒瘤考虑了n个这样的修改操作,并编号为\(1\sim n\)。当毒瘤要出数据结构题的时候,他就将这些修改操作中选若干个出来,然后出成一道题。
当然了,这样出的题有可能不可做。通过精妙的数学推理,毒瘤揭露了这些修改操作的关系:有m对“互相排斥”的修改操作,第i对是第ui个操作和第vi个操作。当一道题同时含有ui和vi这两个操作时,这道题就会变得不可做。另一方面,一道题中不包含任何“互相排斥”的修改操作时,这个题就是可做的。此外,毒瘤还发现了一个规律:m-n是一个很小的数字,且任意两个修改操作都是连通的。两个修改操作a,b是连通的,当且仅当存在若干操作\(t_0,t_1,...,t_l\),使得\(t_0=a,t_l=b\),且对1≤i≤l,\(t_{i-1}\)和\(t_i\)都是“互相排斥”的修改操作。
一堆“互相排斥”的修改操作称为互斥对。现在毒瘤想知道,给定值n和m个互斥对,他共能出出多少道可做的不同的数据结构题。两道数据结构题是不同的,当且仅当有一个修改操作在其中一道题中存在,而在另一道题中不存在。
输入输出格式
输入格式:
第一行为正整数n,m。
接下来m行,每行两个正整数u,v,代表一对“互相排斥”的修改操作。
输出格式:
输出一行一个整数,代表毒瘤可以出的可做的不同的“互相排斥”的修改操作的个数。这个数可能很大,所以只输出模998244353后的值。
Solution
虚树。
先处理出一颗生成树,考虑到非树边很少,考虑暴力枚举非树边两端的状态,复杂度\(O(4^{n-m+1}n)\)。
然后优化一下,对于一条非树边,两端的状态只需要枚举\((1,0)\)和\((0,1)\)就好了,\((1,1)\)显然不合法,\((0,0)\)可以在\(dp\)的时候得到。复杂度\(O(2^{n-m+1}n)\)。
考虑到上面的过程枚举时,树边是不变的,也就是说,可以考虑把非树边所在的点建出一颗虚树,然后对于虚树上的点,\(dp\)方程一定可以表示成:
\]
其中\(k\)为固定的系数,对于虚树上每条边,这个转移都是固定的。
所以可以\(O(n)\)先在原树\(dp\)出系数,然后暴力枚举关键点状态暴力虚树上\(dp\)就好了。
复杂度\(O(n+2^{2(m-n+1)}(n-m+1))\),足以通过此题。
#include<bits/stdc++.h>
using namespace std;
#define int long long
void read(int &x) {
x=0;int f=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f;
}
void print(int x) {
if(x<0) putchar('-'),x=-x;
if(!x) return ;print(x/10),putchar(x%10+48);
}
void write(int x) {if(!x) putchar('0');else print(x);putchar('\n');}
const int mod = 998244353;
const int maxn = 3e5+10;
int n,m,s[100],tt[100],cnt,sz[maxn],dfn[maxn],dep[maxn],use[maxn],val[maxn],vis[maxn],g[maxn][2],pr[maxn];
struct data {int k[2][2];}epsilon;
struct Dsu {
int fa[maxn];
void init() {for(int i=1;i<=n;i++) fa[i]=i;}
int find(int x) {return fa[x]==x?x:fa[x]=find(fa[x]);}
}dsu;
struct Input_Tree {
int head[maxn],tot,f[maxn][20],dfn_cnt;
struct edge{int to,nxt;}e[maxn<<1];
void add(int u,int v) {e[++tot]=(edge){v,head[u]},head[u]=tot;}
void ins(int u,int v) {add(u,v),add(v,u);}
void dfs(int x,int fa) {
sz[x]=1,dfn[x]=++dfn_cnt,dep[x]=dep[fa]+1,f[x][0]=fa;
for(int i=1;i<=19;i++) f[x][i]=f[f[x][i-1]][i-1];
for(int i=head[x];i;i=e[i].nxt)
if(e[i].to!=fa) dfs(e[i].to,x),sz[x]+=sz[e[i].to];
}
int lca(int x,int y) {
if(dep[x]<dep[y]) swap(x,y);
for(int i=19;~i;i--) if(dep[f[x][i]]>=dep[y]) x=f[x][i];
if(x==y) return x;
for(int i=19;~i;i--) if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
return f[x][0];
}
void dp(int x,int fa) {
int bo=1;g[x][0]=g[x][1]=1;
for(int i=head[x];i;i=e[i].nxt)
if(e[i].to!=fa) {
bo=0,dp(e[i].to,x);int v=e[i].to;
g[x][0]=1ll*g[x][0]*(g[v][0]+g[v][1])%mod;
g[x][1]=1ll*g[x][1]*g[v][0]%mod;
}
if(bo) g[x][0]=g[x][1]=1;
}
int ns[maxn][2],nt[maxn][2];
data get(int x,int fa) {
ns[x][0]=1,ns[x][1]=0;
nt[x][0]=0,nt[x][1]=1;
while(x!=fa) {
pr[x]=1;
int pre=x;x=f[x][0];ns[x][0]=ns[x][1]=nt[x][0]=nt[x][1]=1;
ns[x][0]=1ll*ns[x][0]*(ns[pre][0]+ns[pre][1])%mod;
ns[x][1]=1ll*ns[x][1]*ns[pre][0]%mod;
nt[x][0]=1ll*nt[x][0]*(nt[pre][0]+nt[pre][1])%mod;
nt[x][1]=1ll*nt[x][1]*nt[pre][0]%mod;
if(x==fa) break;
for(int i=head[x];i;i=e[i].nxt)
if(e[i].to!=f[x][0]&&e[i].to!=pre) {
int v=e[i].to;
ns[x][0]=1ll*ns[x][0]*(g[v][0]+g[v][1])%mod;
ns[x][1]=1ll*ns[x][1]*g[v][0]%mod;
nt[x][0]=1ll*nt[x][0]*(g[v][0]+g[v][1])%mod;
nt[x][1]=1ll*nt[x][1]*g[v][0]%mod;
}
}
data ans;pr[fa]=1;
ans.k[0][0]=ns[x][0],ans.k[1][0]=ns[x][1];
ans.k[0][1]=nt[x][0],ans.k[1][1]=nt[x][1];
return ans;
}
}T;
int cmp(int x,int y) {return dfn[x]<dfn[y];}
struct Virtual_Tree {
int head[maxn],tot;
struct edge{int to,nxt;data k;}e[maxn<<1];
void add(int u,int v) {e[++tot]=(edge){v,head[u],epsilon},head[u]=tot;}
void ins(int u,int v) {add(u,v),add(v,u);}
int in[maxn],k,use[maxn],used,sta[maxn],top,ban[maxn],f[maxn][2],val[maxn][2];
void build() {
sort(in+1,in+k+1,cmp);k=unique(in+1,in+k+1)-in-1;
sta[++top]=1;
for(int i=1;i<=k;i++) {
if(in[i]==1) continue;
int t=T.lca(in[i],sta[top]),pre=-1;
while(dfn[sta[top]]>dfn[t]&&dfn[sta[top]]<dfn[t]+sz[t]) {
if(pre!=-1) ins(sta[top],pre);
pre=sta[top],top--;
}
if(pre!=-1) ins(t,pre);
if(sta[top]!=t) sta[++top]=t;
sta[++top]=in[i];
}
int pre=-1;
while(top) {
if(pre!=-1) ins(sta[top],pre);
pre=sta[top],top--;
}
}
void dfs(int x,int fa) {
use[x]=1;
for(int i=head[x];i;i=e[i].nxt)
if(e[i].to!=fa) dfs(e[i].to,x);
}
void make(int x,int fa) {
for(int i=head[x];i;i=e[i].nxt)
if(e[i].to!=fa) {
e[i].k=T.get(e[i].to,x);
make(e[i].to,x);
}
}
void dp(int x,int fa) {
f[x][0]=val[x][0],f[x][1]=val[x][1];
for(int i=head[x];i;i=e[i].nxt)
if(e[i].to!=fa) {
dp(e[i].to,x);data u=e[i].k;int v=e[i].to;
f[x][0]=1ll*f[x][0]*(f[v][0]*u.k[0][0]%mod+f[v][1]*u.k[0][1]%mod)%mod;
f[x][1]=1ll*f[x][1]*(f[v][0]*u.k[1][0]%mod+f[v][1]*u.k[1][1]%mod)%mod;
}
if(ban[x]==0) f[x][1]=0;
else if(ban[x]==1) f[x][0]=0;
}
void get_val(int x,int fa) {
val[x][0]=val[x][1]=1;
for(int i=T.head[x],v=T.e[i].to;i;i=T.e[i].nxt,v=T.e[i].to)
if(!pr[v]) {
val[x][0]=1ll*val[x][0]*(g[v][0]+g[v][1])%mod;
val[x][1]=1ll*val[x][1]*g[v][0]%mod;
}
for(int i=head[x];i;i=e[i].nxt)
if(e[i].to!=fa) get_val(e[i].to,x);
}
void solve()
for(int i=1;i<=cnt;i++)
in[++k]=s[i],in[++k]=tt[i],vis[s[i]]=1,vis[tt[i]]=1;
build();make(1,0);int ans=0;
memset(ban,-1,sizeof ban);
get_val(1,0);
for(int st=0;st<(1<<k);st++) {
for(int i=1;i<=k;i++)
if(st&(1<<(i-1))) ban[in[i]]=1;
else ban[in[i]]=0;
for(int i=1;i<=cnt;i++)
if(ban[s[i]]==1&&ban[tt[i]]==1) goto loop;
dp(1,0);
ans=(0ll+ans+f[1][0]+f[1][1])%mod;
loop:;
}
write(ans);
}
}VT;
signed main() {
read(n),read(m);dsu.init();
for(int i=1,x,y;i<=m;i++) {
read(x),read(y);int u=dsu.find(x),v=dsu.find(y);
if(u==v) s[++cnt]=x,tt[cnt]=y;
else dsu.fa[u]=v,T.ins(x,y);
}
T.dfs(1,0),T.dp(1,0);VT.solve();
return 0;
}
[bzoj5287] [HNOI2018]毒瘤的更多相关文章
- BZOJ5287 HNOI2018毒瘤(虚树+树形dp)
显然的做法是暴力枚举非树边所连接两点的选或不选,大力dp.考场上写的是最暴力的O(3n-mn),成功比大众分少10分.容斥或者注意到某些枚举是不必要的就能让底数变成2.但暴力的极限也就到此为止. 每次 ...
- [BZOJ5287][HNOI2018]毒瘤(虚树DP)
暴力枚举非树边取值做DP可得75. 注意到每次枚举出一个容斥状态的时候,都要做大量重复操作. 建立虚树,预处理出虚树上两点间的转移系数.也可动态DP解决. 树上倍增.动态DP.虚树DP似乎是这种问题的 ...
- 【BZOJ5287】[HNOI2018]毒瘤(动态规划,容斥)
[BZOJ5287][HNOI2018]毒瘤(动态规划,容斥) 题面 BZOJ 洛谷 题解 考场上想到的暴力做法是容斥: 因为\(m-n\le 10\),所以最多会多出来\(11\)条非树边. 如果就 ...
- [HNOI2018]毒瘤
Description 从前有一名毒瘤. 毒瘤最近发现了量产毒瘤题的奥秘.考虑如下类型的数据结构题:给出一个数组,要求支持若干种奇奇怪怪的修改操作(比如区间加一个数,或者区间开平方),并支持询问区间和 ...
- bzoj 5287: [Hnoi2018]毒瘤
Description Solution \(dfs\) 出一棵生成树之后,多出来的边就都是反祖边了 把反祖边两个端点都拿出来,就会得到最多 \(k=2*(m-n+1)\) 个关键点 除了关键点以外的 ...
- BZOJ.5287.[AHOI HNOI2018]毒瘤(虚树 树形DP)
BZOJ LOJ 洛谷 设\(f[i][0/1]\)表示到第\(i\)个点,不选/选这个点的方案数.对于一棵树,有:\[f[x][0]=\prod_{v\in son[x]}(f[v][0]+f[v] ...
- HNOI2018毒瘤
题面链接 luogu sol 这篇博是骗访问量的QwQ. 考虑树怎么做,简单容斥.诸如\(f[u][0]=\prod (f[v][0]+f[v][1]),f[u][1]=\prod f[v][0]\) ...
- 【比赛】HNOI2018 毒瘤
虚树+dp 直接看zlttttt的强大题解 zlttttt的题解看这里 #include<bits/stdc++.h> #define ui unsigned int #define ll ...
- BZOJ 5287: [Hnoi2018]毒瘤 动态dp(LCT+矩阵乘法)
自己 yy 了一个动态 dp 做法,应该是全网唯一用 LCT 写的. code: #include <bits/stdc++.h> #define ll long long #define ...
随机推荐
- Maven学习总结(10)
本文通过一个例子来介绍利用maven来构建一个多模块的jave项目.开发工具:intellij idea. 一.项目结构 multi-module-project是主工程,里面包含两个模块(Modul ...
- Integer和int使用==比较的总结
public static void main(String[] args) { int i1 = 128; Integer i2 = 128; Integer i3 = new Integer(12 ...
- WIN10下解决Failed installing tomcat X service
Win10下无法安装Tomcat的service.bat 来看本文的网友想必已经在自己电脑的cmd看了几百遍的Failed installing tomcat X service 字样,也认真检查过自 ...
- 使用 python快速搭建http服务
在 Linux 服务器上或安装了 Python 的机器上,Python自带了一个WEB服务器 SimpleHTTPServer. 我们可以很简单的使用 python -m SimpleHTTPSer ...
- python join() 提示UnicodeDecodeError: 'utf8' codec can't decode byte 0xcb in position 0: unexpected end of的原因及解决办法
问题: 在使用join()将列表元素连接成字符串时出错如下 return split.join(result) UnicodeDecodeError: 'utf8' codec can't decod ...
- 【HTML】placeholder中换行
表示回车 表示换行 案例 <textarea rows="10" placeholder="测试换行 新的一行"></textarea>
- Windows手工创建服务方法
需要将程序设置成Windows服务的情况,可以利用一下windows自带的sc命令来创建服务. 该命令的基本用法如下:打开cmd命令, 输入如下信息:1 创建服务:sc create SecServe ...
- Small Talk Matters【闲谈很重要】
Small Talk Matters We' ve all been there: in a lift, in line at the bank or on an airplane, 我们都有过这样的 ...
- POJ 2084 Catalan
Game of Connections Time Limit: 1000MS Memory Limit: 30000K Total Submissions: 8772 Accepted: 43 ...
- 吴恩达DeepLearning 第一课第四周随笔
第四周 4.1深度神经网络符号约定 L=4______(神经网络层数) 4.2 校正矩阵的维数 校正要点:,, dZ,dA,dW,db都与它们被导数(Z,A,W,b)的维数相同 4.3 为什么使用 ...