题目传送门 然而大部分人基本不能看……
题目大意: 好像也不方便写……
题解
据说正解好像是 n 2 n^2 n2 的,但是勉强把我这个 n 3 n^3 n3 的给放过去了qwq。
考虑统计以每个节点为好链的最后一个节点
的好链数量。先考虑树的情况,令
f
i
f_i
fi 表示以
i
i
i 为最后一个节点的好链数量,那么转移的时候暴力枚举子树内的点,假如
j
<
i
j<i
j<i 那么令
f
i
f_i
fi 加上
f
j
f_j
fj,最后
f
i
f_i
fi 加一 也就是新建一条好链。
然后考虑仙人掌,先建出圆方树便于处理环,那么圆点就不需要考虑转移了,考虑他所有儿子(儿子一定是方点,根据圆方树的性质)的转移即可。
对于一个方点,考虑这个方点对应的环,将这个环提取出来,将他的父亲节点放在最后一位。令 d i d_i di 表示环上顺时针看第 i i i 个点,对于 i , j i,j i,j,枚举 i i i 的子树内的节点 k k k,若 k k k 对 j j j 能产生贡献,那么令 g 1 ( j ) g_1(j) g1(j) 加上 f k f_k fk,假如 k k k 恰好是 i i i,那么 g 1 ( j ) g_1(j) g1(j) 还要加上 g 1 ( k ) g_1(k) g1(k)。
逆时针也是这样贡献一次,但是要注意,方点的父亲依然要放在最后,所以实际上就是将 d d d 的除了最后一位的前面的位reverse一下,然后同样递推出一个 g 2 ( i ) g_2(i) g2(i)。
这样子我们就不需要担心环上的一个点走重复的边来回贡献这种极其离谱的情况,比如
i
,
j
,
k
i,j,k
i,j,k 在环上的位置顺时针就是
i
,
j
,
k
i,j,k
i,j,k,但是满足
i
<
k
<
j
i<k<j
i<k<j,如果不钦定一个顺序贡献的话,就会出现
i
i
i 贡献
k
k
k,
k
k
k 贡献
j
j
j,这样
j
,
k
j,k
j,k 之间的边就反复走了,是不符合简单路径
这个条件的。
最后令 f d i + = g 1 ( d i ) + g 2 ( d i ) f_{d_i}+=g_1(d_i)+g_2(d_i) fdi+=g1(di)+g2(di) 即可,但是注意,对于方点的父亲 f a fa fa, ∀ i , d i < f a \forall i,d_i<fa ∀i,di<fa, f d i f_{d_i} fdi 是对 g 1 ( f a ) , g 2 ( f a ) g_1(fa),g_2(fa) g1(fa),g2(fa) 分别贡献了一次的,所以 f f a f_{fa} ffa 需要减去一个 f d i f_{d_i} fdi。不难发现这种问题对于其他 d i d_i di 不会出现,因为一对 d i , d j d_i,d_j di,dj 只可能在一个方向产生一次贡献。
然后一个点 i i i 至多对一个点 j j j 产生 1 1 1 次贡献,特别的,在环上可能产生两次(即上面提到的重复贡献),所以单次dp复杂度是 n 2 n^2 n2 的,总时间复杂度就是 O ( n 3 ) O(n^3) O(n3)。
代码如下:
#include <bits/stdc++.h>
using namespace std;
#define maxn 1010
#define mod 998244353
#define pb push_back
int n,m,n_;
struct edge{int y,next;}E[10010];
int first[maxn],len=1;
void buildroad(int x,int y){E[++len]=(edge){y,first[x]};first[x]=len;}
int dfn[maxn],low[maxn],id=0;
int sta[maxn],t=0;
vector<int> e[maxn];
void dfs(int x,int from){
dfn[x]=low[x]=++id;sta[++t]=x;
for(int i=first[x];i;i=E[i].next){
int y=E[i].y;if(i==from)continue;
if(!dfn[y]){
dfs(y,i^1);
low[x]=min(low[x],low[y]);
if(low[y]>=dfn[x]){
int xx;n_++;do{
xx=sta[t--];
e[xx].pb(n_);e[n_].pb(xx);
}while(xx!=y);
e[x].pb(n_);e[n_].pb(x);
}
}else low[x]=min(low[x],dfn[y]);
}
}
void reduce(int &x){x+=x>>31&mod;}
int add(int x){return x>=mod?x-mod:x;}
int f[maxn],g1[maxn],g2[maxn],ans=0;
void go(int x,int fa,vector<int> &c){
if(x<=n)c.pb(x);
for(int y:e[x])if(y!=fa)go(y,x,c);
}
vector<int> c[maxn];
void solve(int x,int fa){
f[x]=1;
for(int y:e[x])if(y!=fa)solve(y,x);
if(x>n){
vector<int> d;
for(int i=0;i<e[x].size();i++)if(e[x][i]==fa){
for(int j=(i+1)%(int)(e[x].size());j!=i;j=(j+1)%(int)(e[x].size()))
d.push_back(e[x][j]),g1[e[x][j]]=g2[e[x][j]]=0;
break;
}
d.push_back(fa);g1[fa]=g2[fa]=0;
for(int i=0;i<d.size()-1;i++)
c[i].clear(),go(d[i],x,c[i]);
for(int i=0;i<d.size();i++)
for(int j=i+1;j<d.size();j++)
for(int k:c[i])if(k<d[j]){
reduce(g1[d[j]]+=f[k]-mod);
if(k==d[i])reduce(g1[d[j]]+=g1[k]-mod);
}
reverse(d.begin(),--d.end());
reverse(c,c+d.size()-1);
for(int i=0;i<d.size();i++)
for(int j=i+1;j<d.size();j++)
for(int k:c[i])if(k<d[j]){
reduce(g2[d[j]]+=f[k]-mod);
if(k==d[i])reduce(g2[d[j]]+=g2[k]-mod);
}
for(int i=0;i<d.size()-1;i++)
for(int j:c[i])if(j<fa)reduce(f[fa]-=f[j]);
for(int i=0;i<d.size();i++)reduce(f[d[i]]+=add(g1[d[i]]+g2[d[i]])-mod);
}
}
int main()
{
scanf("%d %d",&n,&m);n_=n;
for(int i=1,x,y;i<=m;i++)
scanf("%d %d",&x,&y),buildroad(x,y),buildroad(y,x);
dfs(1,0);
for(int i=1;i<=n;i++){
memset(f,0,sizeof(f));
solve(i,0);reduce(ans+=f[i]-mod);
}
printf("%d",ans);
}