显然的做法是暴力枚举非树边所连接两点的选或不选,大力dp。考场上写的是最暴力的O(3n-mn),成功比大众分少10分。容斥或者注意到某些枚举是不必要的就能让底数变成2。但暴力的极限也就到此为止。

  每次重新dp做了大量重复的事,考虑从减少重复计算方面优化。先跑一遍没有限制的树形dp。将非树边所连接的点拎出来建一棵虚树。注意到虚树中某点对其父亲的贡献系数(也即由该点到其父亲的链上点的dp值与其关系)是不变的,那么dp出系数,依旧暴力枚举非树边就可以了。一定程度上与noip2018d2t3有相似之处?

  码起来非常长(虽然似乎并没有很难写),可能是我姿势不对。注意暴力枚举非树边时不应标记其是否强制被选,而是标记强制被选的次数,防止回溯时出问题。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
#define N 100050
#define P 998244353
char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<''||c>'')) c=getchar();return c;}
int gcd(int n,int m){return m==?n:gcd(m,n%m);}
int read()
{
int x=,f=;char c=getchar();
while (c<''||c>'') {if (c=='-') f=-;c=getchar();}
while (c>=''&&c<='') x=(x<<)+(x<<)+(c^),c=getchar();
return x*f;
}
int n,m,p[N],f[N][],id[N][],dfn[N],size[N],fa[N][],deep[N],num[N][][],tmp[][],tot,cnt,t,u,ans=;
bool vis[N],flag[N];
struct data{int to,nxt;
}edge[N<<];
void addedge(int x,int y){t++;edge[t].to=y,edge[t].nxt=p[x],p[x]=t;}
int ksm(int a,int k)
{
int s=;
for (;k;k>>=,a=1ll*a*a%P) if (k&) s=1ll*s*a%P;
return s;
}
int inv(int a){return ksm(a,P-);}
void dfs(int k)
{
vis[k]=;f[k][]=f[k][]=;dfn[k]=++tot;size[k]=;
for (int i=p[k];i;i=edge[i].nxt)
if (edge[i].to!=fa[k][])
if (vis[edge[i].to])
{
flag[k]=flag[edge[i].to]=;bool tag=;
for (int j=;j<=cnt;j++) if (id[j][]==edge[i].to&&id[j][]==k) {tag=;break;}
if (tag) cnt++,id[cnt][]=k,id[cnt][]=edge[i].to;
}
else
{
fa[edge[i].to][]=k;
deep[edge[i].to]=deep[k]+;
dfs(edge[i].to);
f[k][]=1ll*f[k][]*(f[edge[i].to][]+f[edge[i].to][])%P,
f[k][]=1ll*f[k][]*f[edge[i].to][]%P;
size[k]+=size[edge[i].to];
}
}
int lca(int x,int y)
{
if (deep[x]<deep[y]) swap(x,y);
for (int j=;~j;j--) if (deep[fa[x][j]]>=deep[y]) x=fa[x][j];
if (x==y) return x;
for (int j=;~j;j--) if (fa[x][j]!=fa[y][j]) x=fa[x][j],y=fa[y][j];
return fa[x][];
}
namespace virtual_tree
{
int m,p[N],t,g[N][],point[N],stk[N],top,cho[N];
struct data{int to,nxt;}edge[N<<];
void addedge(int x,int y){t++;flag[x]=flag[y]=;edge[t].to=y,edge[t].nxt=p[x],p[x]=t;}
bool cmp(const int&a,const int&b){return dfn[a]<dfn[b];}
void build()
{
for (int i=;i<=n;i++) if (flag[i]) point[++m]=i;
sort(point+,point+m+,cmp);
stk[top=]=;
for (int i=(point[]==)+;i<=m;i++)
{
int x=lca(point[i],stk[top]);
if (x==stk[top]) stk[++top]=point[i];
else
{
while (top>&&deep[stk[top-]]>=deep[x])
addedge(stk[top-],stk[top]),top--;
if (stk[top]!=x) addedge(x,stk[top--]),stk[++top]=x;
stk[++top]=point[i];
}
}
while (top>) addedge(stk[top-],stk[top]),top--;
}
void copy(int k)
{
g[k][]=f[k][],g[k][]=f[k][];
for (int i=p[k];i;i=edge[i].nxt)
{
copy(edge[i].to);
g[k][]=1ll*g[k][]*inv((1ll*num[edge[i].to][][]*f[edge[i].to][]+1ll*num[edge[i].to][][]*f[edge[i].to][])%P)%P;
g[k][]=1ll*g[k][]*inv((1ll*num[edge[i].to][][]*f[edge[i].to][]+1ll*num[edge[i].to][][]*f[edge[i].to][])%P)%P;
}
}
void dp(int k)
{
if (cho[k]) g[k][]=;
for (int i=p[k];i;i=edge[i].nxt)
{
dp(edge[i].to);
g[k][]=1ll*g[k][]*((1ll*num[edge[i].to][][]*g[edge[i].to][]+1ll*num[edge[i].to][][]*g[edge[i].to][])%P)%P;
g[k][]=1ll*g[k][]*((1ll*num[edge[i].to][][]*g[edge[i].to][]+1ll*num[edge[i].to][][]*g[edge[i].to][])%P)%P;
}
}
int getans()
{
copy();
dp();
return (g[][]+g[][])%P;
}
void dfs(int k,int s)
{
if (k>cnt) {ans=((ans+s*getans())%P+P)%P;return;}
cho[id[k][]]++,cho[id[k][]]++;dfs(k+,-s);
cho[id[k][]]--,cho[id[k][]]--;dfs(k+,s);
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("bzoj5287.in","r",stdin);
freopen("bzoj5287.out","w",stdout);
const char LL[]="%I64d\n";
#else
const char LL[]="%lld\n";
#endif
n=read(),m=read();
for (int i=;i<=m;i++)
{
int x=read(),y=read();
addedge(x,y),addedge(y,x);
}
fa[][]=;deep[]=;dfs();
for (int j=;j<;j++)
for (int i=;i<=n;i++)
fa[i][j]=fa[fa[i][j-]][j-];
virtual_tree::build();
for (int i=;i<=n;i++) if (flag[i]) virtual_tree::point[++u]=i;
for (int j=;j<=u;j++)
{
int i=virtual_tree::point[j];
num[i][][]=num[i][][]=;
if (i!=)
{
int x=fa[i][],y=i;
do
{
tmp[][]=(num[i][][]+num[i][][])%P,tmp[][]=(num[i][][]+num[i][][])%P,
tmp[][]=num[i][][],tmp[][]=num[i][][];
num[i][][]=tmp[][],num[i][][]=tmp[][],num[i][][]=tmp[][],num[i][][]=tmp[][];
if (!flag[x])
for (int k=p[x];k;k=edge[k].nxt)
if (edge[k].to!=y&&edge[k].to!=fa[x][])
{
num[i][][]=1ll*num[i][][]*(f[edge[k].to][]+f[edge[k].to][])%P;
num[i][][]=1ll*num[i][][]*(f[edge[k].to][]+f[edge[k].to][])%P;
num[i][][]=1ll*num[i][][]*f[edge[k].to][]%P;
num[i][][]=1ll*num[i][][]*f[edge[k].to][]%P;
}
y=x,x=fa[x][];
}while(!flag[y]);
}
}
virtual_tree::dfs(,);
cout<<ans;
return ;
}

最新文章

  1. 【jQuery小实例】---2自定义动画
  2. 学习使用html与css,并尝试写php
  3. php常见问题以及解决方法
  4. Java 随机数
  5. 【AngularJs】---JSONP跨域访问数据传输
  6. 欧拉工程第67题:Maximum path sum II
  7. 字符串[未AC](后缀自动机):HEOI 2016 str
  8. UESTC_秋实大哥与战争 2015 UESTC Training for Data Structures&lt;Problem D&gt;
  9. 搭建Windows SVN服务器及TortoiseSVN使用帮助和下载
  10. jQuery EasyUI API 中文文档 - 分隔按钮(splitbutton)
  11. [译文] SQL JOIN,你想知道的应该都有
  12. Hyper-V虚拟机故障导致数据文件丢失的数据恢复全过程
  13. zTree 3-- jQuery 树插件笔记
  14. 初学Python——装饰器
  15. iptables简单应用
  16. .NET MVC 学习笔记(三)— MVC 数据显示
  17. 【C】——itoa 函数的实现
  18. Redis缓存穿透问题及解决方案
  19. Effective Java部分读书笔记
  20. JavaScript中的一些小技巧

热门文章

  1. 模拟T1数字number
  2. AI Summit(2018.07.19)
  3. 20155210 Exp9 Web安全基础实践
  4. VS编程,WPF单独更改TextBlock中部分文字格式的一种方法
  5. 总是套路留人心, JAVA提供的套路: LinkedHashMap实现LRU缓存; InvocationHandler实现动态代理; fork/join实现窃取算法
  6. [Deep-Learning-with-Python]基于Keras的房价预测
  7. TMS320VC5509片内ADC采集
  8. 搭建 Guacamole 并解决各种坑和创建不了虚拟驱动器导致无法实现文件传输的方法
  9. LOJ#2799. 「CCC 2016」生命之环
  10. Js_图片轮换