您的位置:首页 > 其它

【ZJOI 2018】线图(树的枚举,hash,dp)

2018-04-01 13:46 369 查看

线图

题目描述

九条可怜是一个热爱出题的女孩子。

今天可怜想要出一道和图论相关的题。在一张无向图 $G$ 上,我们可以对它进行一些非常有趣的变换,比如说对偶,又或者说取补。这样的操作往往可以赋予一些传统的问题新的活力。例如求补图的连通性、补图的最短路等等,都是非常有趣的问题。

最近可怜知道了一种新的变换:求原图的线图 (line graph)。对于无向图 $G = ⟨V, E⟩$,它的 线图 $L(G)$ 也是一个无向图:

  • 它的点集大小为 $|E|$,每个点唯一对应着原图的一条边。
  • 两个点之间有边当且仅当这两个点对应的边在原图上有公共点(注意不会有自环)。 下图是一个简单的例子,左图是原图,右图是它对应的线图。其中点 $1$ 对应原图的边 $(1, 2)$,点 $2$ 对应 $(1, 4)$,点 $3$ 对应 $(1, 3)$,点 $4$ 对应 $(3, 4)$。

#include <cstdio>
#include <map>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;

typedef long long LL;
typedef pair<int, int> Pair;
#define fir first
#define sec second
#define Mp make_pair
typedef vector<int>::iterator Vecit;
typedef vector<Pair>::iterator Vecpa;

const int N = 5005, MOD = 998244353, M = 11, NM = 110005;

int Ans;
int n, K;
int fa
, A[1<<23], pd[M][M], C
[23];
int dg[NM], dgr[NM], dgs[NM];
int dp
[M], gp[2][1<<M];

vector<Pair> E, B[NM];
vector<int> G
, T
;
inline void Add(int a, int b) {
G[a].push_back(b);
}

inline void Ad(int &a, int b) {
a+=b;
(a>=MOD)? (a-=MOD):(0);
}
inline int Last_pos(int x, int res=0) {
for (x&=(-x); x; x>>=1) ++res; return res;
}
inline int Count(int x, int res=0) {
for (; x; x>>=1) res+=x&1; return res;
}
#define Sqr(x) ((LL)(x)*(x))
#define _c2(x) ((LL)(x)*((x)-1)/2)

inline int Calc(int n, int m, int K, int res=0) {
if (K==1) return m;
memset(dg, 0, sizeof(int)*(n+1));
memset(dgr, 0, sizeof(int)*(n+1));
memset(dgs, 0, sizeof(int)*(n+1));
for (Vecpa p=E.begin(); p!=E.end(); ++p) ++dg[(*p).fir], ++dg[(*p).sec];
if (K==2) {
for (int *i=dg+1; i<=dg+n; ++i) res=(res+_c2(*i))%MOD;
return res;
}
if (K==3) {
for (Vecpa p=E.begin(); p!=E.end(); ++p)
res=(res+_c2(dg[(*p).fir]+dg[(*p).sec]-2))%MOD;
return res;
}
if (K==4) {
for (Vecpa p=E.begin(); p!=E.end(); ++p) {
int u=(*p).fir, v=(*p).sec, X=dg[u]+dg[v]-2;
dgr[u]=(dgr[u]+(LL)X*X)%MOD;
dgr[v]=(dgr[v]+(LL)X*X)%MOD;
Ad(dgs[u], X);
Ad(dgs[v], X);
}
for (int i=1; i<=n; ++i) {
int sqr=dgr[i], sum=dgs[i], deg=dg[i];
res=(res+(LL)sum*sum-sqr+(LL)sqr*(deg-1)-(LL)5*(deg-1)*sum+(LL)6*_c2(deg))%MOD;
}
return (LL)res*(MOD+1)/2%MOD;
}
int cnt=0;
for (int i=0; i<=n; ++i) B[i].clear();
for (Vecpa p=E.begin(); p!=E.end(); ++p) {
B[(*p).fir].push_back(Mp((*p).sec, ++cnt));
B[(*p).sec].push_back(Mp((*p).fir, cnt));
}
E.clear();
for (int i=1; i<=n; ++i)
for (int j=0; j<(int)B[i].size(); ++j)
for (int k=0; k<j; ++k)
E.push_back(Mp(B[i][j].sec, B[i][k].sec));
return Calc(m, (int)E.size(), K-1);
}

inline void Get_dp(int x, int Fa, int K) {
for (Vecit p=G[x].begin(); p!=G[x].end(); ++p) if (*p!=Fa) {
Get_dp(*p, x, K);
}
for (int i=1; i<=K; ++i) {
int flg=0;
for (int j=1; j<i; ++j) if (pd[i][j]) {
dp[x][i]=dp[x][j]; flg=1; break;
}
if (flg) continue;
int cnt=0;
vector<int> L;
for (Vecit p=T[i].begin(); p!=T[i].end(); ++p) if (*p!=fa[i]) {
if ((int)T[*p].size()==1) ++cnt;
else L.push_back(*p);
}
if (cnt+(int)L.size() > (int)G[x].size()-(x!=1)) continue;
int ST=1<<(int)L.size(), ls=0, no=1;
memset(gp[0], 0, sizeof(int)*(ST));
memset(gp[1], 0, sizeof(int)*(ST));
gp[no][0]=1;
for (Vecit v=G[x].begin(); v!=G[x].end(); ++v) if (*v!=Fa) {
int ns=0;
for (int j=0; j<(int)L.size(); ++j, ns=0) if (dp[*v][L[j]]){
for (int k=0; k<j; ++k) if (pd[L[j]][L[k]]) ns|=1<<k;
int U=(ST-1)^ns^(1<<j);
for (int sub=U; sub; sub=(sub-1)&U) {
Ad(gp[ls][sub|ns|(1<<j)], (LL)dp[*v][L[j]]*gp[no][sub|ns]%MOD);
}
Ad(gp[ls][ns|(1<<j)], (LL)dp[*v][L[j]]*gp[no][ns]%MOD);
}
for (int st=0; st<ST; ++st) {
Ad(gp[ls][st], gp[no][st]), gp[no][st]=0;
}
no^=1; ls^=1;
}
dp[x][i]=(LL)gp[no][ST-1]*C[(int)G[x].size()-(int)L.size()-(x!=1)][cnt]%MOD;
}
}

string Hash_tree(int x, int st, int Fa) {
vector<string> v;
for (Vecit p=T[x].begin(); p!=T[x].end(); ++p) {
if (*p!=Fa && (st>>(*p-1))&1) v.push_back(Hash_tree(*p, st, x));
}
sort(v.begin(), v.end());
string res="";
for (int i=0; i<(int)v.size(); ++i) res+='1'+v[i]+'0';
return res;
}

inline int Hash_to_int(string s) {
int res=0, le=s.length();
for (int i=0; i<le; ++i) res=(res<<1)|(s[i]=='1');
return res;
}

inline int Build_tree(string tree, int K) {
int x=1, cnt=1, le=tree.length();
for (int i=0; i<le; ++i)
if (tree[i]=='0') {
x=fa[x];
if (!x) return -1;
} else {
fa[++cnt]=x;
T[x].push_back(cnt);
T[cnt].push_back(x);
E.push_back(Mp(x, cnt));
x=cnt;
if (x>K) return -1;
}
if (x!=1) {
cerr << tree << endl;
exit(13);
}
return cnt;
}

typedef unsigned long long ULL;
map<ULL, int> Map;
ULL get_hash(int n) {
ULL ret=0;
vector<int> v;
for (int i=1; i<=n; ++i) {
v.push_back(Hash_to_int(Hash_tree(i, (1<<n)-1, 0)));
}
sort(v.begin(),v.end());
for (int i=0; i<n; ++i) ret=ret*19260817+v[i];
return ret;
}

void DFS(string tree, int K) {
if ((int)tree.length() < 2*(K-1)) {
DFS(tree+'1', K);
DFS(tree+'0', K);
return;
}
E.clear();
for (int i=0; i<=K; ++i) T[i].clear();
if (Build_tree(tree, K) != K) return;
int ss=Hash_to_int(Hash_tree(1, (1<<K)-1, 0));
if (~A[ss]) return;
ULL haha=get_hash(K);
A[ss]=(Map.count(haha)? Map[haha] : Map[haha]=Calc(K, K-1, ::K));

for (int st=0; st<(1<<K)-1; ++st) {
string gt=Hash_tree(Last_pos(st), st, 0);
if ((int)gt.length() != 2*(Count(st)-1)) continue;
A[ss]=(A[ss]-A[Hash_to_int(gt)]+MOD)%MOD;
}
memset(pd, 0, sizeof pd);
for (int i=1; i<=K; ++i)
for (int j=i+1; j<=K; ++j)
pd[i][j]=pd[j][i]=(Hash_tree(i, (1<<K)-1, fa[i]) == Hash_tree(j, (1<<K)-1, fa[j]));
for (int i=1; i<=n; ++i)
memset(dp[i], 0, sizeof(int)*(K+1));;
Get_dp(1, 0, K);
int res=0;
for (int i=1; i<=n; ++i) Ad(res, dp[i][1]);
Ad(Ans, (LL)A[ss]*res%MOD);
}

int main() {
memset(A, -1, sizeof A);
scanf("%d%d", &n, &K);
for (int i=1, x, y; i<n; ++i) {
scanf("%d%d", &x, &y); Add(x, y); Add(y, x);
}

for (int i=0; i<=n; ++i) {
C[i][0]=1;
for (int j=1; j<=i && j<=20; ++j) C[i][j]=(C[i-1][j]+C[i-1][j-1])%MOD;
}

for (int i=1; i<=K+1; ++i) DFS("", i);
printf("%d\n", Ans);

return 0;
}
View Code  

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: