P4426 [HNOI/AHOI2018]毒瘤
挺不错的一个题。
题意即为求一个图的独立集方案数。
如果原图是一棵树,可以直接大力f[x][0/1]来dp。
由于非树边很少,考虑2^11容斥,强制某些点必选,然后再O(n)dp,这样应该过不了。
发现这个容斥本质上是对一些点进行修改,修改的形式是强制它必须选。
直接xjb上一个ddp就没了。
这里由于有可能会/0,考虑维护一下结尾0的个数就行。
复杂度的话,直接乱写是311*log2的,但是实际上我们可以优化加点和删点的顺序。
考虑按照某一个特定的顺序去容斥。
我的想法是一个简单的贪心,每次找一个新加入的尽可能少的状态,复杂度玄学。
比较优秀的做法是dfs来构造集合,这样的修改总量是2^n的,十分优秀。
#include<bits/stdc++.h>
#define N 220000
#define eps 1e-7
#define inf 1e9+7
#define db double
#define ll long long
#define ldb long double
using namespace std;
inline int read()
{
char ch=0;
int x=0,flag=1;
while(!isdigit(ch)){ch=getchar();if(ch=='-')flag=-1;}
while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return x*flag;
}
const int mo=998244353;
int ksm(int x,int k)
{
int ans=1;
while(k)
{
if(k&1)ans=1ll*ans*x%mo;
k>>=1;x=1ll*x*x%mo;
}
return ans;
}
int inv(int x){return ksm(x,mo-2);}
struct node
{
int x,y;
int w(){return y?0:x;}
};
node operator*(node a,int b)
{
node ans=a;
if(b)ans.x=1ll*ans.x*b%mo;else ans.y++;
return ans;
}
node operator/(node a,int b)
{
node ans=a;
if(b)ans.x=1ll*ans.x*inv(b)%mo;else ans.y--;
return ans;
}
struct matrix
{
int s[2][2];
void clear()
{
memset(s,0,sizeof(s));
}
};
matrix operator*(matrix a,matrix b)
{
matrix ans;ans.clear();
for(int i=0;i<2;i++)
{
for(int j=0;j<2;j++)for(int k=0;k<2;k++)
ans.s[i][j]=(ans.s[i][j]+(1ll*a.s[i][k]*b.s[k][j]%mo))%mo;
}
return ans;
}
struct edge{int to,nxt;}e[N*2];
int num,f[N],head[N];
inline void add(int x,int y){e[++num]={y,head[x]};head[x]=num;}
int find(int x){if(x!=f[x])f[x]=find(f[x]);return f[x];}
bool merge(int x,int y){x=find(x);y=find(y);if(x==y)return false;f[x]=y;return true;}
struct link{int x,y;}q[N];
bool vis[N];
node a[N][2],b[N][2];
int times,p[N],d[N],id[N],fs[N],sz[N],fa[N],son[N],top[N],low[N],flag[N];
struct Segment_Tree
{
#define lson o<<1
#define rson o<<1|1
#define mid ((l+r)>>1)
matrix dp[N*4];
inline void pushup(int o){dp[o]=dp[lson]*dp[rson];}
void build(int o,int l,int r)
{
if(l==r)
{
int x=p[l];
dp[o]={{{b[x][0].w(),b[x][0].w()},{b[x][1].w(),0}}};
return;
}
build(lson,l,mid);build(rson,mid+1,r);pushup(o);
}
void optset(int o,int l,int r,int q)
{
if(l==r)
{
int x=p[l];
dp[o]={{{b[x][0].w(),b[x][0].w()},{b[x][1].w(),0}}};
return;
}
if(q<=mid)optset(lson,l,mid,q);
else optset(rson,mid+1,r,q);
pushup(o);
}
matrix query(int o,int l,int r,int ql,int qr)
{
if(ql<=l&&r<=qr)return dp[o];
if(ql<=mid&&qr>mid)return query(lson,l,mid,ql,qr)*query(rson,mid+1,r,ql,qr);
if(ql<=mid)return query(lson,l,mid,ql,qr);
if(qr>mid)return query(rson,mid+1,r,ql,qr);
}
}T;
matrix get(int x){return T.query(1,1,times,id[x],id[low[top[x]]]);}
void update(int x,int o)
{
b[x][0]=b[x][0]/flag[x];flag[x]=o;b[x][0]=b[x][0]*flag[x];
while(true)
{
matrix A=get(top[x]);
T.optset(1,1,times,id[x]);
matrix B=get(top[x]);
x=fa[top[x]];if(!x)return;
b[x][0]=b[x][0]/((A.s[0][0]+A.s[1][0])%mo);
b[x][0]=b[x][0]*((B.s[0][0]+B.s[1][0])%mo);
b[x][1]=b[x][1]/A.s[0][0];b[x][1]=b[x][1]*B.s[0][0];
}
}
void dfs1(int x)
{
sz[x]=1;
for(int i=head[x];i!=-1;i=e[i].nxt)
{
int to=e[i].to;
if(to==fa[x])continue;
fa[to]=x;dfs1(to);sz[x]+=sz[to];
if(sz[son[x]]<sz[to])son[x]=to;
}
}
void dfs2(int x,int tp)
{
p[++times]=x;id[x]=times;top[x]=tp;
if(son[x])dfs2(son[x],tp);else low[tp]=x;
for(int i=head[x];i!=-1;i=e[i].nxt)
{
int to=e[i].to;
if(top[to])continue;
dfs2(to,to);
}
}
void DP(int x)
{
a[x][0]=a[x][1]=b[x][0]=b[x][1]={1,0};
for(int i=head[x];i!=-1;i=e[i].nxt)
{
int to=e[i].to;
if(to==fa[x])continue;
DP(to);
a[x][0].x=1ll*a[x][0].x*(a[to][0].x+a[to][1].x)%mo;
a[x][1].x=1ll*a[x][1].x*a[to][0].x%mo;
if(to==son[x])continue;
b[x][0].x=1ll*b[x][0].x*(a[to][0].x+a[to][1].x)%mo;
b[x][1].x=1ll*b[x][1].x*a[to][0].x%mo;
}
}
int main()
{
int n=read(),m=read(),cnt=0;
num=-1;memset(head,-1,sizeof(head));
for(int i=1;i<=n;i++)f[i]=i;
for(int i=1;i<=m;i++)
{
int x=read(),y=read();
if(merge(x,y))add(x,y),add(y,x);
else q[++cnt]={x,y};
}
dfs1(1);dfs2(1,1);DP(1);T.build(1,1,times);
for(int i=1;i<=n;i++)flag[i]=1;
for(int i=0;i<(1<<cnt);i++)fs[i]=fs[i>>1]+(i&1);
int ans=0;
for(int o=0,lst=0;o<(1<<cnt);o++)
{
int s=-1;
for(int i=0;i<(1<<cnt);i++)
if(!vis[i])if(s==-1||fs[i^lst]<fs[s^lst])s=i;
for(int i=1;i<=cnt;i++)if((1<<(i-1))&(s^lst))
{
int x=q[i].x,y=q[i].y;
if(1<<(i-1)&s)
{
if(d[x]++==0)update(x,0);
if(d[y]++==0)update(y,0);
}
else
{
if(d[x]--==1)update(x,1);
if(d[y]--==1)update(y,1);
}
}
vis[s]=true;lst=s;
matrix t=get(1);
if(fs[s]%2==0)ans=(ans+((t.s[0][0]+t.s[1][0])%mo))%mo;
else ans=(ans-((t.s[0][0]+t.s[1][0])%mo))%mo;
}
printf("%d",(ans%mo+mo)%mo);
return 0;
}
P4426 [HNOI/AHOI2018]毒瘤的更多相关文章
- Luogu P4426 [HNOI/AHOI2018]毒瘤
题目 神仙题. 首先我们可以把题意转化为图的独立集计数.显然这个东西是个NP-Hard的. 然后我们可以注意到\(m\le n+10\),也就是说最多有\(11\)条非树边. 我们现在先考虑一下,树上 ...
- 洛谷 P4426 - [HNOI/AHOI2018]毒瘤(虚树+dp)
题面传送门 神仙虚树题. 首先考虑最 trival 的情况:\(m=n-1\),也就是一棵树的情况.这个我相信刚学树形 \(dp\) 的都能够秒掉罢(确信).直接设 \(dp_{i,0/1}\) 在表 ...
- [HNOI/AHOI2018]毒瘤
题目描述 https://www.lydsy.com/JudgeOnline/upload/201804/%E6%B9%96%E5%8D%97%E4%B8%80%E8%AF%95%E8%AF%95%E ...
- #10 //I [HNOI/AHOI2018]毒瘤
题解: 80分做法还是听简单的 对于非树边枚举一下端点状态 然而我也不知道为什么就多t了一个点 具体实现上 最暴力的是3^n次 但是我们可以发现对于i不取,j取 i不取,j不取是可以等效成i不取,j没 ...
- 题解 [HNOI/AHOI2018]毒瘤
题目传送门 题目大意 给出一个 \(n\) 个点 \(m\) 条边的无向图,问有多少个点集满足点集中任意两点均不存在边相连. \(n\le 10^5,m-n\le 10\),答案对 \(9982443 ...
- [HNOI/AHOI2018]转盘(线段树优化单调)
gugu bz lei了lei了,事独流体毒瘤题 一句话题意:任选一个点开始,每个时刻向前走一步或者站着不动 问实现每一个点都在$T_i$之后被访问到的最短时间 Step 1 该题可证: 最优方案必 ...
- 【LG4437】[HNOI/AHOI2018]排列
[LG4437][HNOI/AHOI2018]排列 题面 洛谷 题解 题面里这个毒瘤的东西我们转化一下: 对于\(\forall k,j\),若\(p_k=a_{p_j}\),则\(k<j\). ...
- 【题解】Luogu P4436 [HNOI/AHOI2018]游戏
原题传送门 \(n^2\)过百万在HNOI/AHOI2018中真的成功了qwqwq 先将没门分格的地方连起来,枚举每一个块,看向左向右最多能走多远,最坏复杂度\(O(n^2)\),但出题人竟然没卡(建 ...
- [Bzoj5285][洛谷P4424][HNOI/AHOI2018]寻宝游戏(bitset)
P4424 [HNOI/AHOI2018]寻宝游戏 某大学每年都会有一次Mystery Hunt的活动,玩家需要根据设置的线索解谜,找到宝藏的位置,前一年获胜的队伍可以获得这一年出题的机会. 作为新生 ...
随机推荐
- gimp的使用笔记
gimp是德国的开源软件! 跟其他软件一样, 包括file, edit, view, 还有select, color , filter, 和 window. 窗口window就包括所有的dockabl ...
- ZOJ 1602 Multiplication Puzzle(区间DP)题解
题意:n个数字的串,每取出一个数字的代价为该数字和左右的乘积(1.n不能取),问最小代价 思路:dp[i][j]表示把i~j取到只剩 i.j 的最小代价. 代码: #include<set> ...
- 【解决办法】Undefined command/function 'mapminmax'.
原因: 低版本7.0中没有mapminmax这个函数,对应的归一化函数是premnmx和postmnmx,具体请查看着两个函数的用法升级到2009就肯定可以正常使用这个函数了 解释: premnmx. ...
- springboot配置redis
https://www.cnblogs.com/xiaoping1993/p/7761123.html https://www.cnblogs.com/gdpuzxs/p/7222309.html s ...
- JS 事件绑定、事件监听、事件委托详细介绍
原:http://www.jb51.net/article/93752.htm 在JavaScript的学习中,我们经常会遇到JavaScript的事件机制,例如,事件绑定.事件监听.事件委托(事件代 ...
- hihoCoder 1515 分数调查(带权并查集)
http://hihocoder.com/problemset/problem/1515 题意: 思路: 带权并查集的简单题,计算的时候利用向量法则即可. #include<iostream&g ...
- 【bzoj】4538: [Hnoi2016]网络
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=4538 维护一个数据结构支持对于一颗树的操作,需要支持: 1.对于树上的一条路径上的每个点上 ...
- 使用Coding体验小记
https://coding.net/ 写了些小程序后放在github上托管,并用gitpages展示. 今天发现用git pages展示的网页网络特别不稳定,时常会出现网页打不开的现象. 之前一直通 ...
- Intent 类型
Intent 分为两种类型: 显式 Intent:按名称(完全限定类名)指定要启动的组件. 通常,您会在自己的应用中使用显式 Intent 来启动组件,这是因为您知道要启动的 Activity 或服务 ...
- java扫描文件夹下面的所有文件(递归与非递归实现)
java中扫描指定文件夹下面的所有文件扫描一个文件夹下面的所有文件,因为文件夹的层数没有限制可能多达几十层几百层,通常会采用两种方式来遍历指定文件夹下面的所有文件.递归方式非递归方式(采用队列或者栈实 ...