「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. javaScript书写规范

    命名规范. 常量名    全部大写并单词间用下划线分隔    如:CSS_BTN_CLOSE.TXT_LOADING对象的属性或方法名    小驼峰式(little camel-case)    如: ...

  2. easyui表单提交验证form

    方式一,不需要考虑jquery.easyui.min.js版本 <script> $(function () { //针对 设置 novalidate:true $('.validateb ...

  3. pre,html转义,abbr缩写,表格table

    <pre></pre>预定义文本标签pre(保留换行和空格) <sdds>对html转义 <abbr title="sddsdsds"&g ...

  4. 随机数生成 && 生成执行锁

    生成随机数列: openssl rand -base64 uuidgen echo $RANDOM | md5sum echo $RANDOM | sha256sum 随机小写10个字母 随机数: [ ...

  5. RabbitMQ--Publish/Subscribe(四)

    先前例子中,我们创建了一个简单的日志系统,广播messages到consumer接收方. 但如果有日志错误级别的,不同的consumer接收不同错误级别的信息.比如consumer1接收info和wa ...

  6. 关于bcb调用动态库,contains invalid OMF record, type 0x21 (possibly COFF)问题

    今天用C++Builder6.0 调用三方lib文件时,编译的时候出现如下错误: “contains invalid OMF record, type 0x21 (possibly COFF)” 才知 ...

  7. 获取SQL Server的版本信息

    微软 SQL Server 版本号 产品名称 发行日期 主版本号 正式版 SP1 SP2 SP3 SP4 SQL Server 2016 2016.06.01 13.00.1601.5 13.00.1 ...

  8. apache代理配置https

    原文:https://mp.weixin.qq.com/s/Tw4UzX73Q7MSw3GJXnpN8A 微信小程序开发https设置 2017-04-06 格里菲斯 互联网工作者 微信官方规定小程序 ...

  9. Ajax请求中的async:false/true

    Ajax请求中的async:false/trueasync. 默认是 true,即为异步方式,$.ajax执行后,会继续执行ajax后面的脚本,直到服务器端返回数据后,触发$.ajax里的succes ...

  10. js中的call,apply,bind区别

    在JavaScript中,call.apply和bind是Function对象自带的三个方法,这三个方法的主要作用是改变函数中的this指向. call.apply.bind方法的共同点和区别:app ...