「HNOI2018」毒瘤

解题思路

先考虑只有一棵树的情况,经典独立集计数。

\[dp[u][0]=\prod (dp[v][0]+dp[v][1]) \\
dp[u][1]=\prod dp[v][0]
\]

然后考虑将所有非树边的端点建一棵虚树,那么虚树以外的节点的 \(\text{dp}\) 值是不会改变的,那么就可以推出虚树上一个节点对它父亲贡献的系数。

然后枚举一下所有非树边能选取的合法状态,再在虚树上计算一遍贡献,令 \(k = m-n+1\),这样复杂度是 \(\mathcal O(k3^k+m)\) 。

事实上只需要枚举每一条非树边的左端点是否选,当左端点选的时候,右端点只能不选,否则右端点可选可不选,这样涵盖了所有三种合法情况,复杂度 \(\mathcal O(k2^k+m)\) 。

code

/*program by mangoyang*/
#include <bits/stdc++.h>
#define inf (0x7f7f7f7f)
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
typedef long long ll;
using namespace std;
template <class T>
inline void read(T &x){
int ch = 0, f = 0; x = 0;
for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1;
for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
if(f) x = -x;
}
#define int ll
const int N = 300005, mod = 998244353;
vector<pair<int, int> > ed;
vector<int> g[N], e[N], vec;
int dfn[N], dep[N], fa[N], s[N][2], dp[N][2], dp2[N][2], ali[N][2];
int tr[N][2][2], a[N], b[N], in[N], pa[N], st[N], n, m, ans;
inline int ask(int x){
if(x == fa[x]) return x; else return fa[x] = ask(fa[x]);
}
namespace PR{
int Log[N], f[N][22], tot;
inline int chkmin(int x, int y){ return dep[x] < dep[y] ? x : y; }
inline void dfs(int u, int fa){
dep[u] = dep[fa] + 1, dfn[u] = ++tot, f[tot][0] = u;
dp[u][0] = dp[u][1] = 1;
for(int i = 0; i < (int) g[u].size(); i++){
int v = g[u][i];
if(v == fa) continue;
dfs(v, u), f[++tot][0] = u;
(dp[u][1] *= dp[v][0]) %= mod;
(dp[u][0] *= (dp[v][1] + dp[v][0]) % mod) %= mod;
}
}
inline void solve(){
dfs(1, 0);
for(int i = 2; i <= tot; i++) Log[i] = Log[i>>1] + 1;
for(int j = 1; j <= 21; j++)
for(int i = 1; i + (1 << j) - 1 <= tot; i++)
f[i][j] = chkmin(f[i][j-1], f[i+(1<<(j-1))][j-1]);
}
inline int Lca(int u, int v){
int x = dfn[u], y = dfn[v];
if(x > y) swap(x, y); int g = Log[y-x+1];
return chkmin(f[x][g], f[y-(1<<g)+1][g]);
}
}
inline bool cmp(int x, int y){ return dfn[x] < dfn[y]; }
inline void buildtree(int a[], int len){
sort(a + 1, a + len + 1, cmp); int top = 0, tot = 0;
for(int i = 1; i <= len; i++){
int u = a[i];
if(!top){ st[++top] = b[++tot] = u; continue; }
int ca = PR::Lca(u, st[top]);
for(; top > 1 && dep[st[top]] > dep[ca]; top--)
if(dep[st[top-1]] < dep[ca]) pa[st[top]] = ca;
if(st[top] != ca)
pa[ca] = st[top], st[++top] = b[++tot] = ca;
pa[u] = ca, st[++top] = b[++tot] = u;
}
for(int i = 1; i <= tot; i++){
in[b[i]] = 1, e[pa[b[i]]].push_back(b[i]);
ali[b[i]][0] = ali[b[i]][1] = 1;
}
}
inline int dfs(int u, int fa){
int x = 0, k0 = 1, k1 = 1;
for(int i = 0; i < (int) g[u].size(); i++){
int v = g[u][i];
if(v == fa) continue;
int tmp = dfs(v, u);
if(tmp) x = tmp;
else (k0 *= dp[v][0]) %= mod, (k1 *= (dp[v][0] + dp[v][1]) % mod) %= mod;
}
s[u][0] = k1, s[u][1] = k0;
if(in[u]) return tr[u][0][0] = 1, tr[u][1][1] = 1, u;
if(!x) return 0;
int tmp[2][2];
for(int i = 0; i < 2; i++)
for(int j = 0; j < 2; j++) tmp[i][j] = tr[x][i][j];
tr[x][0][0] = (tmp[0][0] + tmp[0][1]) % mod * k1 % mod;
tr[x][0][1] = tmp[0][0] * k0 % mod;
tr[x][1][0] = (tmp[1][0] + tmp[1][1]) % mod * k1 % mod;
tr[x][1][1] = tmp[1][0] * k0 % mod;
return x;
}
inline void dfs2(int u){
for(int i = 0; i < 2; i++) dp2[u][i] = ali[u][i] * s[u][i];
for(int i = 0; i < (int) e[u].size(); i++){
int v = e[u][i];
dfs2(v);
int k0 = (dp2[v][0] * tr[v][0][0] % mod + dp2[v][1] * tr[v][1][0] % mod) % mod;
int k1 = (dp2[v][0] * tr[v][0][1] % mod + dp2[v][1] * tr[v][1][1] % mod) % mod;
(dp2[u][0] *= (k0 + k1) % mod) %= mod, (dp2[u][1] *= k0) %= mod;
}
}
inline void solve(int mask){
for(int i = 0; i < (int) vec.size(); i++)
ali[vec[i]][0] = ali[vec[i]][1] = 1;
for(int i = 0; i < (int) ed.size(); i++){
int x = ed[i].first, y = ed[i].second;
int tmp = (1 << i) & mask;
if(tmp) ali[vec[x]][0] = ali[vec[y]][1] = 0; else ali[vec[x]][1] = 0;
}
dfs2(1), (ans += dp2[1][0] + dp2[1][1]) %= mod;
}
signed main(){
int len = 0;
read(n), read(m);
for(int i = 1; i <= n; i++) fa[i] = i;
for(int i = 1, x, y; i <= m; i++){
read(x), read(y);
if(ask(x) == ask(y)){
ed.push_back(make_pair(x, y));
vec.push_back(x), vec.push_back(y);
}
else{
fa[ask(x)] = ask(y);
g[x].push_back(y), g[y].push_back(x); }
}
PR::solve();
if(m == n - 1) return cout << (dp[1][0] + dp[1][1]) % mod, 0;
sort(vec.begin(), vec.end());
vector<int>::iterator newend = unique(vec.begin(), vec.end());
vec.erase(newend, vec.end());
for(int i = 0; i < (int) vec.size(); i++) a[++len] = vec[i];
if(!len || vec[0] != 1) a[++len] = 1;
buildtree(a, len), dfs(1, 0);
for(int i = 0; i < (int) ed.size(); i++){
int x = lower_bound(vec.begin(), vec.end(), ed[i].first) - vec.begin();
int y = lower_bound(vec.begin(), vec.end(), ed[i].second) - vec.begin();
ed[i] = make_pair(x, y);
}
for(int i = 0; i < (1 << (m - n + 1)); i++) solve(i);
cout << ans << endl;
return 0;
}

「HNOI2018」毒瘤的更多相关文章

  1. 「HNOI2018」游戏

    「HNOI2018」游戏 解题思路 首先没有锁上的门可以缩点缩掉,然后对于一扇锁上的门,如果钥匙在左边,那么右边就永远不可能到达左边,同理如果钥匙在右边,左边就永远不可能到达右边. 然后考虑一个暴力的 ...

  2. 「HNOI2018」转盘

    「HNOI2018」转盘 现场推出了大部分结论但是只写了 \(40\) 分暴力,被贺指导踩爆,现在还有点怀念 HNOI2018 贺指导对着镜子荒野行动的日子,那几天他云球迷瞎**指点篮球,被送上指导称 ...

  3. @loj - 2496@ 「AHOI / HNOI2018」毒瘤

    目录 @description@ @solution@ @accepted code@ @details@ @description@ 从前有一名毒瘤. 毒瘤最近发现了量产毒瘤题的奥秘.考虑如下类型的 ...

  4. 【LOJ】#2496. 「AHOI / HNOI2018」毒瘤

    题面 还有这么诚实的出题人! 我们最多影响20个点,然后把这20个点的虚树建出来,并且枚举每个点的选举状态,如果一个点选或不选可以通过改\(dp[u][0] = 0\)或\(dp[u][1] = 0\ ...

  5. Loj #2495. 「AHOI / HNOI2018」转盘

    Loj #2495. 「AHOI / HNOI2018」转盘 题目描述 一次小 G 和小 H 原本准备去聚餐,但由于太麻烦了于是题面简化如下: 一个转盘上有摆成一圈的 \(n\) 个物品(编号 \(1 ...

  6. Loj #2494. 「AHOI / HNOI2018」寻宝游戏

    Loj #2494. 「AHOI / HNOI2018」寻宝游戏 题目描述 某大学每年都会有一次 Mystery Hunt 的活动,玩家需要根据设置的线索解谜,找到宝藏的位置,前一年获胜的队伍可以获得 ...

  7. 「HNOI2016」数据结构大毒瘤

    真是 \(6\) 道数据结构毒瘤... 开始口胡各种做法... 「HNOI2016」网络 整体二分+树状数组. 开始想了一个大常数 \(O(n\log^2 n)\) 做法,然后就被卡掉了... 发现直 ...

  8. loj #2510. 「AHOI / HNOI2018」道路

    #2510. 「AHOI / HNOI2018」道路 题目描述 W 国的交通呈一棵树的形状.W 国一共有 n−1 个城市和 nnn 个乡村,其中城市从 111 到 n−1 编号,乡村从 111 到 n ...

  9. loj #2509. 「AHOI / HNOI2018」排列

    #2509. 「AHOI / HNOI2018」排列   题目描述 给定 nnn 个整数 a1,a2,…,an(0≤ai≤n),以及 nnn 个整数 w1,w2,…,wn.称 a1,a2,…,an 的 ...

随机推荐

  1. 基于Django-Cookie的CBV和FBV的用户验证装饰器

    FBV模式 def cookie(func):       def deco(request,*args,**kwargs):             u = request.get_signed_c ...

  2. 【译】第七篇 Replication:合并复制-订阅

    本篇文章是SQL Server Replication系列的第七篇,详细内容请参考原文. 订阅服务器就是复制发布项目的所有变更将传送到的服务器.每一个发布需要至少一个订阅,但是一个发布可以有多个订阅. ...

  3. μC/OS-Ⅱ在C8051F060上的移植及其应用

    嵌入式操作系统是嵌入式应用的基础和核心.随着应用系统的不断复杂化和系统实时性需求的不断提高,对相应软件的逻辑结构.稳定性.实时性也提出了更高的要求,以传统的前后台编程模式编制软件将更加困难,而且容易出 ...

  4. java7与java8中计算两个日期间隔多少年多少月多少天的实现方式

    最近工作中碰到个新需求,计算每个员工入职公司的时长,要求形式为多少年多少月多少天形式,某个值为0就跳过不显示,因为前段时间学习过java8新特性,对于这个需求,java8的新时间日期API可以直接解决 ...

  5. 一个简单的爆破 mysql 远程连接脚本(perl6)

    sub MAIN(Str $host) { use DBIish; my $file = open 'password.txt'; while $file.get -> $line { my $ ...

  6. linux下使用indent整理代码(代码格式化)【转】

    转自:https://blog.csdn.net/jiangjingui2011/article/details/7197069 常用的设置: indent -npro -kr -i8 -ts8 -s ...

  7. 十一、springcloud之链路追踪Sleuth

    一.背景 随着微服务的数量增长,一个业务接口涉及到多个微服务的交互,在出错的情况下怎么能够快速的定位错误 二.简介 Spring Cloud Sleuth 主要功能就是在分布式系统中提供追踪解决方案, ...

  8. VirtualBox上安装CentOS-7(Minimal)

    Windows 10家庭中文版,VirtualBox 5.2.12,CentOS 7(Minimal版), 因为听到大家在谈论CentOS,阿里云上也有CentOS,CentOS还是Red Hat出品 ...

  9. Vue项目实现excel导出

    1.package.json里面安装三个插件 npm install  xlsx  --save npm install  script-loader  –save-dev npm install   ...

  10. JVM性能调优监控工具——jps、jstack、jmap、jhat、jstat、hprof使用详解

    摘要: JDK本身提供了很多方便的JVM性能调优监控工具,除了集成式的VisualVM和jConsole外,还有jps.jstack.jmap.jhat.jstat.hprof等小巧的工具,本博客希望 ...