原文链接www.cnblogs.com/zhouzhendong/p/UOJ373.html

前言

  真是一道毒瘤题。UOJ卡常毒瘤++。我卡了1.5h的常数才过QAQ

  Orzjry

  标算居然是指数做法。

题解

1. 感受一下线图上点的含义

1.1 一阶线图

  L(G) 上的一个点对应 G 中的一条边。

1.2 二阶线图

  $L^2(G)$ 上一个点对应 G 中的一条包含 3 个节点的路径。

1.3 三阶线图

  $L^3(G)$ 上一个点对应 G 中的一朵4个节点的菊花或者一条包含 4 个节点的路径,在特殊情况下,这条路径首尾相连,形成三元环。

1.4 k 阶线图

  $L^k(G)$ 上一个点对应 G 中的一个节点数不超过 k+1 的连通块。

1.5 性质

  在原图中同构的两个联通块在 $L^k(G)$ 中对应的节点数相同。

  那么如果对于每一种不超过 k+1 个节点的连通块 g,我们都能求出它在 $L^k(G)$ 中对应的节点个数,而且我们能统计 G 中与 g 同构的连通块个数,那么我们就可以解决这个问题。

2. 找到所有不超过 k+1 个点的联通块 g

  我们发现 G 是一棵树,所以 g 也是一棵树。

  为了方便,我们先搜出所有不同构的不多于 10 个节点的有根树。

  搜的方法很多吧,树哈希、括号序列等等,不展开介绍。

  搜出来了!居然只有 1205 个。

3. 手推 1~4 阶线图节点数公式

  设 e 和 V 分别为 G 的边集和点集。

3.1  一阶线图

  n - 1

3.2  二阶线图

  设 $d[x]$ 为节点 x 的度数,则答案为

$$\sum_{x\in V} \binom {d[x]} 2 $$

3.3  三阶线图

  容易发现答案为

$$\sum_{(x,y)\in e} (d[x]-1)(d[y]-1) + \sum_{x\in V} 3\binom {d[x]} 3$$

3.4  四阶线图

  这个东西非常不好算啊。

  我们把它转化成算 $L^3(L(G))$ 的节点个数。

  设 $d[x,y]$ 表示 $G$ 中的边 $(x,y)$ 在 $L(G)$ 中对应的点的度数。则

$$d[x,y] = d[x]+d[y]-2$$

  则答案为

$$\sum_{(x,y),(y,z)\in e} (d[x,y]-1)(d[y,z]-1) + \sum_{(x,y)\in e} 3\binom {d[x,y]} 3$$

  考虑如何优化这个式子:

  设

$$S[x] = \sum_{(x,y) \in G} d[x,y] $$

  预处理处 S[x] ,剩下的式子就由读者自行推导。

4.对于所有不超过 k+1 个节点的图 g,都算出它在 k 次线图中的对应点个数(设为 v1[g])

  注意,在 k 次线图中的对应点个数,不是 g 的 k 次线图的点数。

  但是我们可以考虑先求出 g 的 k 次线图的点数,然后再减掉 g 的所有子图的贡献。由于 g 的子图的贡献也是一个和原问题模型类似的问题,所以不加展开介绍。

  那么如何求 g 的 k 次线图的点数?

  我们先暴力展开 k-4 层,最后 4 层用之前提到的式子来计算。

  时间复杂度 O(玄) 。

5. 求一个树上有多少个子图与另一个树同构

  这是为了求出 G 中有多少个图 g ,只要求出了这个东西,那么乘上 g 在 k 次线图中的对应点数,就可以得到 g 对答案的贡献。

  考虑 dp 。

  设 dp[i][j] 表示以 G 的节点 i 为根,放上有根树 j 的方案数。

  考虑如何直接合并子树的贡献。

  枚举一下当前节点 i 的 j,把 j 的相同子树缩到一起,搞一个状压 dp。由于子树 size 之和很小以及节点个数少的子树种类很少,所以状压出来的状态很少,可以当做一个常数。于是只要枚举一下 i 的子树,然后用对应的状态进行转移即可。

  时间复杂度 $O(nS\cdot C)$ 。其中 S=1205 表示子图种类数,C 表示刚才提到的那个常数。

6. 其他

  到此为止这题的做法以及讲完了。

  但是实际做这题的时候还可能会被卡常数,在使用各种卡常技巧之外,这里提供另一种卡常思路:

  算 v1[g] 的时候,我们只算所有不同构的无根树的 v1[g] ,因为不同构的无根树的种类很少,比不同构的有根树要少一个数量级,大约在 400 种以下。

  这里需要用到判定无根树是否同构。

  我们只要转化成有根树就好了:以直径的中点/重心为根。(直径的中点/重心可能在一条边上)

代码

#pragma GCC optimize("Ofast","inline")
#include <bits/stdc++.h>
#define clr(x) memset(x,0,sizeof (x))
#define For(i,a,b) for (int i=a;i<=b;i++)
#define Fod(i,b,a) for (int i=b;i>=a;i--)
#define pb(x) push_back(x)
#define mp(x,y) make_pair(x,y)
#define fi first
#define se second
#define _SEED_ ('C'+'L'+'Y'+'A'+'K'+'I'+'O'+'I')
#define outval(x) printf(#x" = %d\n",x)
#define outvec(x) printf("vec "#x" = ");for (auto _v : x)printf("%d ",_v);puts("")
#define outtag(x) puts("----------"#x"----------")
#define outarr(a,L,R) printf(#a"[%d...%d] = ",L,R);\
For(_v2,L,R)printf("%d ",a[_v2]);puts("");
using namespace std;
typedef long long LL;
typedef vector <int> vi;
LL read(){
LL x=0,f=0;
char ch=getchar();
while (!isdigit(ch))
f|=ch=='-',ch=getchar();
while (isdigit(ch))
x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
return f?-x:x;
}
const int N=5010,mod=998244353;
void Add(int &x,int y){
if ((x+=y)>=mod)
x-=mod;
}
void Del(int &x,int y){
if ((x-=y)<0)
x+=mod;
}
int Pow(int x,int y){
int ans=1;
for (;y;y>>=1,x=(LL)x*x%mod)
if (y&1)
ans=(LL)ans*x%mod;
return ans;
}
int inv2=Pow(2,mod-2);
int inv3=Pow(3,mod-2);
int inv6=Pow(6,mod-2);
int inv24=Pow(24,mod-2);
int n,k;
vi e[N];
namespace so1{
int in[100005];
int preval[N];
void prework(){
For(i,0,N-1)
preval[i]=(LL)i*(i-1)*(i-2)/2%mod;
}
int Get_ans1(int n,vi *e){
int ans=0;
For(i,1,n)
Add(ans,in[i]);
return (LL)ans*inv2%mod;
}
int Get_ans2(int n,vi *e){
int ans=0;
For(i,1,n)
Add(ans,(LL)in[i]*(in[i]-1)/2%mod);
return ans;
}
int Get_ans3(int n,vi *e){
int ans=0;
For(x,1,n)
for (auto y : e[x])
Add(ans,(LL)(in[x]-1)*(in[y]-1)%mod);
ans=(LL)ans*inv2%mod;
For(x,1,n)
Add(ans,(LL)in[x]*(in[x]-1)%mod*(in[x]-2)/2%mod);
return ans;
}
int Get_ans4(int n,vi *e){
static int s[100005];
For(x,1,n){
s[x]=0;
for (auto y : e[x])
Add(s[x],in[x]+in[y]-3);
}
int ans=0;
For(x,1,n)
for (auto y : e[x])
if (x<y){
int tmp=in[x]+in[y]-2,tx=s[x],ty=s[y];
Add(ans,preval[tmp]);
Del(tx,tmp-1),Del(ty,tmp-1);
tmp=(tmp&1)?(tmp-1)>>1:(tmp+mod-1)>>1;
Add(ans,(LL)tmp*(tx+ty)%mod);
}
return ans;
}
int solve(int n,vi *e,int k){
For(i,1,n)
in[i]=e[i].size();
if (k==1)
return Get_ans1(n,e);
else if (k==2)
return Get_ans2(n,e);
else if (k==3)
return Get_ans3(n,e);
else if (k==4)
return Get_ans4(n,e);
return -1;
}
}
namespace Unique_tree{
const int S=1230;
vi son[S],tmp;
int size[S],cnt=0,Size;
map <vi,int> Map;
void search(int k){
if (k==0){
Map[tmp]=++cnt;
size[cnt]=Size;
son[cnt]=tmp;
return;
}
search(k-1);
if (Size+size[k]<=10){
Size+=size[k];
tmp.pb(k);
search(k);
tmp.pop_back();
Size-=size[k];
}
}
void Get_Tree(){
Map.clear();
Map[son[++cnt]]=1;
size[1]=1;
For(i,1,cnt){
if (size[i]==10)
continue;
Size=size[i]+1;
tmp.clear();
tmp.pb(i);
search(i);
}
}
vi e[N];
int Add_Edge(int x,int cnt){
int id=cnt;
cnt++;
for (auto y : son[x]){
e[id].pb(cnt),e[cnt].pb(id);
cnt=Add_Edge(y,cnt);
}
return cnt;
}
int dis[N],fa[N];
int far;
void find_far(int x,int pre,int d){
fa[x]=pre;
if (!far||d>dis[far])
far=x;
dis[x]=d;
for (auto y : e[x])
if (y!=pre)
find_far(y,x,d+1);
}
int dfs_get(int x,int pre){
vector <int> v;
for (auto y : e[x])
if (y!=pre)
v.pb(dfs_get(y,x));
sort(v.begin(),v.end());
reverse(v.begin(),v.end());
return Map[v];
}
int Get_Hash(int x){
For(i,1,size[x])
e[i].clear();
Add_Edge(x,1);
int a,b;
far=0,find_far(1,0,0),a=far;
far=0,find_far(a,0,0),b=far;
int mid=b;
Fod(i,dis[b]/2,1)
mid=fa[mid];
if (dis[b]&1){
int va=dfs_get(mid,fa[mid]);
int vb=dfs_get(fa[mid],mid);
if (va>vb)
swap(va,vb);
return (va<<14)|vb;
}
else
return dfs_get(mid,0);
}
}
using Unique_tree::S;
using Unique_tree::son;
using Unique_tree::size;
using Unique_tree::cnt;
//int mxtmp=0;
namespace line_graph_calc{
const int mx=1e5+5;
int e[mx][100],ec[mx];
int e2[mx][100],ec2[mx];
int ed[2][10000005],ed2[2][10000005],cnt1,cnt2;
int n;
void Trans(){
int _n=cnt1;
cnt2=0;
For(i,1,_n)
ec2[i]=0;
For(i,1,n)
for (int j=1;j<=ec[i];j++)
for (int k=j+1;k<=ec[i];k++){
int x=e[i][j],y=e[i][k];
ed2[0][++cnt2]=x;
ed2[1][cnt2]=y;
e2[x][++ec2[x]]=cnt2;
e2[y][++ec2[y]]=cnt2;
}
For(i,1,cnt2)
ed[0][i]=ed2[0][i],ed[1][i]=ed2[1][i];
cnt1=cnt2;
n=_n;
For(i,1,n){
ec[i]=ec2[i];
For(j,1,ec[i])
e[i][j]=e2[i][j];
}
}
vi ee[mx];
int calc(int _n,vi *E,int k){
static int g[15][15];
n=_n;
clr(g);
For(i,1,n)
for (auto j : E[i])
g[i][j]=1;
For(i,1,n)
ec[i]=0;
cnt1=0;
For(i,1,n)
For(j,i+1,n)
if (g[i][j]){
ed[0][++cnt1]=i;
ed[1][cnt1]=j;
e[i][++ec[i]]=cnt1;
e[j][++ec[j]]=cnt1;
}
while (k>4)
Trans(),k--;
For(i,1,n){
ee[i].clear();
For(x,1,ec[i]){
int j=e[i][x];
ee[i].pb(ed[0][j]==i?ed[1][j]:ed[0][j]);
}
}
return so1::solve(n,ee,k);
}
}
int v1[S],v2[S];
int Add_Edge(int x,vi *e,int cnt){
int id=cnt;
cnt++;
for (auto y : son[x]){
e[id].pb(cnt),e[cnt].pb(id);
cnt=Add_Edge(y,e,cnt);
}
return cnt;
}
void Get_v1(int x){
static vi e[20];
For(i,0,19)
e[i].clear();
int cnt=Add_Edge(x,e,1);
assert(cnt==size[x]+1);
v1[x]=line_graph_calc::calc(size[x],e,k);
}
int dp[N][S];
void Update(vi *e,int x,int pre,int i,vector <pair <int,int> > &v){
static int f[S],wei[S],nxtwei[S];
int s=1;
for (auto j : v)
wei[j.fi]=s,s*=j.se+1,nxtwei[j.fi]=s;
f[0]=1;
For(i,1,s-1)
f[i]=0;
for (auto y : e[x]){
if (y==pre)
continue;
Fod(j,s-1,0)
if (f[j])
for (auto k : v)
if (dp[y][k.fi]&&j%nxtwei[k.fi]+wei[k.fi]<nxtwei[k.fi])
Add(f[j+wei[k.fi]],(LL)f[j]*dp[y][k.fi]%mod);
}
dp[x][i]=f[s-1];
}
vector <pair <int,int> > vec;
void solve(vi *e,int x,int pre){
for (auto y : e[x])
if (y!=pre)
solve(e,y,x);
For(i,1,cnt){
vec.clear();
for (auto j : son[i])
if (vec.empty()||j!=vec.back().fi)
vec.pb(mp(j,1));
else
vec.back().se++;
Update(e,x,pre,i,vec);
}
}
void dfs_del(int x,int i){
For(j,1,cnt)
if (size[j]<size[i])
Del(v1[i],(LL)v1[j]*dp[x][j]%mod);
for (auto y : son[x])
dfs_del(y,i);
}
int Hash[S];
int main(){
so1::prework();
n=read(),k=read();
For(i,1,n-1){
int x=read(),y=read();
e[x].pb(y),e[y].pb(x);
}
if (k<=4)
return printf("%d\n",so1::solve(n,e,k)),0;
Unique_tree::Get_Tree();
For(i,1,cnt)
Hash[i]=Unique_tree::Get_Hash(i);
int equal_cnt=0;
For(i,1,cnt){
int flag=0;
Fod(j,i-1,1)
if (Hash[i]==Hash[j]){
if (size[i]>8)
equal_cnt++;
flag=1,v1[i]=v1[j];
break;
}
if (!flag)
Get_v1(i);
}
For(i,1,cnt)
solve(son,i,0);
For(i,1,cnt)
dfs_del(i,i);
solve(e,1,0);
For(i,1,cnt){
v2[i]=0;
For(j,1,n)
Add(v2[i],dp[j][i]);
}
int ans=0;
For(i,1,cnt)
Add(ans,(LL)v1[i]*v2[i]%mod);
cout<<ans<<endl;
return 0;
}

  

UOJ#373. 【ZJOI2018】线图 搜索,树哈希,动态规划的更多相关文章

  1. 【BZOJ5211】[ZJOI2018]线图(树哈希,动态规划)

    [BZOJ5211][ZJOI2018]线图(树哈希,动态规划) 题面 BZOJ 洛谷 题解 吉老师的题目是真的神仙啊. 去年去现场这题似乎骗了\(20\)分就滚粗了? 首先\(k=2\)直接算\(k ...

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

    线图 题目描述 九条可怜是一个热爱出题的女孩子. 今天可怜想要出一道和图论相关的题.在一张无向图 $G$ 上,我们可以对它进行一些非常有趣的变换,比如说对偶,又或者说取补.这样的操作往往可以赋予一些传 ...

  3. 洛谷P4337 [ZJOI2018]线图(状压+搜索+乱搞)

    题面 传送门 题解 妈呀调了我整整一天-- 题解太长了不写了可以去看\(shadowice\)巨巨的 //minamoto #include<bits/stdc++.h> #define ...

  4. UOJ#196. 【ZJOI2016】线段树 概率期望,动态规划

    原文链接www.cnblogs.com/zhouzhendong/p/UOJ196.html 题解 先离散化,设离散化后的值域为 $[0,m]$ . 首先把问题转化一下,变成:对于每一个位置 $i$ ...

  5. Tableau绘制K线图、布林线、圆环图、雷达图

    Tableau绘制K线图.布林线.圆环图.雷达图 本文首发于博客冰山一树Sankey,去博客浏览效果更好.直接右上角搜索该标题即可 一. K线图 1.1 导入数据源 1.2 拖拽字段 将[日期]托到列 ...

  6. 点线图中的A*算法

    A*简介 A*(A-Star)算法是一种启发式算法,是静态路网中求解最短路最有效的方法.公式表示为:f(n)=g(n)+h(n), 其中f(n) 是节点n从初始点到目标点的估价函数, g(n) 是在状 ...

  7. C#下如何用NPlot绘制期货股票K线图(3):设计要显示的股票价格图表窗口并定义相应类的成员及函数

    [内容简介] 上一篇介绍了要显示K线图所需要的数据结构,及要动态显示K线图,需要动态读取数据文件必需的几个功能函数.本篇介绍要显示蜡烛图所用到的窗口界面设计及对应类定义.下面分述如下: [窗口界面] ...

  8. 利用JFreeChart绘制股票K线图完整解决方案

    http://blog.sina.com.cn/s/blog_4ad042e50100q7d9.html 利用JFreeChart绘制股票K线图完整解决方案 (2011-04-30 13:27:17) ...

  9. IOS 股票K线图、分时图

    IOS 股票K线图.分时图,网上开源项目很少,质量也是参差不齐:偶尔搜索到看似有希望的文章,点进去,还是个标题党:深受毒害.经过一段时间的探索,终于在开源基础上完成了自己的股票K线图.分时图: 先放出 ...

随机推荐

  1. C语言博客作业06——结构体&文件

    C语言博客作业06--结构体&文件 1.本章学习总结 1.1思维导图 1.2.本章学习体会 在本周的学习中,我们学习了关于结构体和文件的内容.结构体的本身并不难,但以结构体为基础的链表还是让我 ...

  2. Linux服务与进程状态

    linux进程的几个状态 Linux进程状态:R (TASK_RUNNING),可执行状态&运行状态(在run_queue队列里的状态) Linux进程状态:S (TASK_INTERRUPT ...

  3. Hadoop记录-queue使用率

    #!/bin/sh ip=xxx port=8088 export HADOOP_HOME=/app/hadoop/bin rmstate1=$($HADOOP_HOME/yarn rmadmin - ...

  4. gantt project 使用

    市场上有不少项目计划类系统, 很多都是收费的, 还有很多都是web版, 这些都自然被排除了. 免费好用的还真不多, 今天简单介绍一下 gantt project 这个软件, 开源并且免费, 基于 ja ...

  5. ArcGIS Editor for Open Street Map 10.X for Desktop下载地址

    ArcGIS Editor for Open Street Map可用于导入从OSM下载的地图,但并不是ArcGIS自带的工具,需要从官网下载,虽然文件很小,但下载速度较慢,易断开. 在此为找不到或不 ...

  6. 3、设置jsp上的类容自动更新

    1.run->edit configurations进入下面的界面,并修改 On ‘Update’ action  为  Redeploy. On frame deactivation  为   ...

  7. Debian Security Advisory DSA-4419-1 twig security update

    Package        : twigCVE ID         : CVE-2019-9942 Fabien Potencier discovered that twig, a templat ...

  8. eclipse添加jar包进jar源码debug调试

    1.点击调试配置 2.选中源码 3.点击添加 4.点击java路径变量 5.点击环境变量配置 6.输入名称及jar包复制限定名

  9. L2-004 这是二叉搜索树吗? (25 分) (树)

    链接:https://pintia.cn/problem-sets/994805046380707840/problems/994805070971912192 题目: 一棵二叉搜索树可被递归地定义为 ...

  10. Beta 冲刺(6/7)

    目录 摘要 团队部分 个人部分 摘要 队名:小白吃 组长博客:hjj 作业博客:beta冲刺(6/7) 团队部分 后敬甲(组长) 过去两天完成了哪些任务 ppt制作 视频拍摄 接下来的计划 准备答辩 ...