[HEOI2013]SAO ——计数问题
题目大意:
Welcome to SAO ( Strange and Abnormal Online)。这是一个 VR MMORPG, 含有 n 个关卡。但是,挑战不同关卡的顺序是一个很大的问题。
有 n – 1 个对于挑战关卡的限制,诸如第 i 个关卡必须在第 j 个关卡前挑战, 或者完成了第 k 个关卡才能挑战第 l 个关卡。并且,如果不考虑限制的方向性, 那么在这 n – 1 个限制的情况下,任何两个关卡都存在某种程度的关联性。即, 我们不能把所有关卡分成两个非空且不相交的子集,使得这两个子集之间没有任 何限制。
对于每个数据,输出一行一个整数,为攻克关卡的顺序方案个数,mod 1,000,000,007 输出。
题目翻译:
发现最后一句话就是说:这是一棵树形图。
所以我们现在有了一棵树,只是边是有向边,挑战的限制就是边的方向,我们必须把所有指向x0的关卡全部通过,才能通过x0关卡。
其实,所有指向x0的边就是它的度数,所以可以看出来,
这个题是让我们求这个树形图有多少种拓扑序。
分析:
这个题即使看了题解也是理解了半天。网上题解也不是很多,做法类似。
树形计数问题,可以用树形DP,首先我们可以先尝试定义一维,定义f[i]表示以i为根的子树的拓扑序有多少种,现在我们需要考虑怎样将若干个儿子的值转移到父亲上。
发现,如果把两个儿子的拓扑序,看做是两个区间,那么我们做的其实是一个区间合并的操作。
但是由于边其实是有向的,(虽然我们是无向边建树)实际边的方向还决定父亲,该儿子的真正完全的拓扑序谁在前,谁在后。就是说,要先过了父亲,还是先过了儿子。
非常无从下手的感觉。我们需要再定义一维。
于是我们这样定义:
f[i][j]表示,在以i为根的子树中,根节点i排在第j位的拓扑序的种类数。(其实所有的拓扑序就是f[i][1-size])
可以发现,j不同时,方案数一定是独立的。
现在我们要考虑转移:
当我们循环到x的一个儿子y的时候,size[x]记录的是当前x与其前面所有儿子子树的size和,就是还没有包括y
那么前面说了,就是一个区间合并,我们以x的位置作为断点考虑合并。
先分类(因为我们无向边建树,但是实际上是有向边。)
①x<y 即先通过x,再通过y。
这个时候,拓扑序合并后x的排名一定在y的前面。
对于f[x][k],最终x前面有k-1个元素。可以从f[x][i](1<=i<=min(k,size))和 f[y][j](j的范围随后再确定)转移过来。转移之后,区间内共有size[x]+size[y]个数
就是说,我在合并后的拓扑序中,先从之前的f[x][i]中的方案数中拿出若干种,放进大区间里,再从f[y][j]里选择一些方案数,放进大区间里。所以这里i一定小于等于k
前k-1个位置,从之前的数中先挑出i-1个位置,有C(k-1,i-1)种选法,
后size[x]+size[y]-k个位置(不算x), 已经选择了i-1个数,还剩下size[x]-i个数(x自己不算),有C(size[x]+size[y]-k,size[x]-i)种选法。
再乘上每个选上的集合中自己的变化,也就是f[x][i]自己本身(类似多重集合的排列)
剩下的位置就是f[y][j]的了,不需要再乘组合数,只需乘上f[y][j]就好。
现在我们要确定j的取值范围:
对于x<y的情况,x之前的数,我们已经填了i-1个位置,还剩下k-i个位置要填,
为了使得y在x的后面,而y之前还能放j-1个数,所以要使得:j-1>=k-i,当然j<=size[y]
所以,j的循环范围是,k-i+1<=j<=size[y]
所以,对于x<y的情况,我们可以列出状态转移的方程是:
f[x][k]=(1<=i<=min(k,size[x]))(k-i+1<=j<=size[y]) f[x][i]*c[k-1][i-1]*c[size[x]+size[y]-k][size[x]-i]*f[y][j]
这样子,发现每次要循环一遍j,复杂度是O(n^4)的,直接挂掉。。。
又发现,对于同一个y,我们好像加的是同一些树,循环的是同一些j。。。
我们把这个式子用乘法分配律提出来一下:
f[x][k]=(1<=i<=min(k,size[x])) f[x][i]*c[k-1][i-1]*c[size[x]+size[y]-k][size[x]-i]*(f[y][k-i+1]+...f[y][size[y]])
所以,加粗部分是可以通过一个前缀合优化处理的,复杂度变成O(1)。
①x>y 即先通过y,再通过x。
其实是同理的。f[x][i](1<=i<=min(k,size)),i的范围没有变。
但是由于要保证y在x的前面,j-1个元素,必然不能填满k-i个位置
所以,j-1<k-i (注意是小于,不是小于等于,因为还有一个位置是y自己,所以要用j-1个位置填不满k-i个位置)并且j>=1
所以这里的状态转移方程是:
f[x][k]=(1<=i<=min(k,size[x]))(1<=j<=k-i) f[x][i]*c[k-1][i-1]*c[size[x]+size[y]-k][size[x]-i]*f[y][j]
同理可以乘法分配律,前缀和优化。
详见代码:
#include<bits/stdc++.h>
#define ull unsigned long long
#define ll long long
using namespace std;
const int N=+;
const int mod=1e9+;
int n,t;
struct node{
int nxt,to,val;
}bian[*N];
int head[N],cnt;
void add(int x,int y,int z)
{
bian[++cnt].to=y;
bian[cnt].nxt=head[x];
bian[cnt].val=z;
head[x]=cnt;
} ull f[N][N],sumdp[N][N];
ull c[N][N];
int size[N];
bool vis[N];
void dfs(int x)
{
size[x]=;
f[x][]=;
vis[x]=;
for(int o=head[x];o;o=bian[o].nxt)
{
int y=bian[o].to;
if(!vis[y])
{
dfs(y);
if(bian[o].val)//......x...y
{
for(int k=size[x]+size[y];k>=;k--)
{
ull sum=;
for(int i=;i<=min(size[x],k);i++)
{
int l=k-i,r=size[y];
ull del=(sumdp[y][size[y]]+mod-sumdp[y][k-i])%mod;//前缀和差值
if(l<r)
{
ull q=(f[x][i]*del)%mod,p=(c[k-][i-]*c[size[x]+size[y]-k][size[x]-i])%mod;
p*=q;p=p%mod;sum+=p;sum%=mod;//这里,必须四个数分别计算并取模,否则会爆long long
}
}
f[x][k]=sum;
}
}
else//.........y...x
{
for(int k=size[x]+size[y];k>=;k--)
{
ull sum=;
for(int i=;i<=min(size[x],k-);i++)
{
int r=min(size[y],k-i);
ull del=sumdp[y][r];
ull q=(f[x][i]*del)%mod,p=(c[k-][i-]*c[size[x]+size[y]-k][size[x]-i])%mod;
p*=q;p=p%mod;sum+=p;sum%=mod;
}
f[x][k]=sum;
}
}
size[x]+=size[y];
}
}
for(int i=;i<=size[x];i++)//处理完了x,赋值前缀和,以便后续使用
sumdp[x][i]=(sumdp[x][i-]+f[x][i])%mod;
} void clear()//清空
{
cnt=;
for(int i=;i<=n;i++)
{
head[i]=;vis[i]=;
size[i]=;
for(int j=;j<=n;j++)
sumdp[i][j]=,f[i][j]=;
}
}
int main()
{
c[][]=;
for(int i=;i<=;i++)
{
c[i][]=;
for(int j=;j<=i;j++)
c[i][j]=(c[i-][j]+c[i-][j-])%mod;
}//1000的范围,组合数打表
cin>>t;
while(t)
{
scanf("%d",&n);
clear();
int x,y;
char q[];
for(int i=;i<=n-;i++)
{
scanf("%d%s%d",&x,q,&y);
x++,y++;//变成以1开始
if(q[]=='<')
{
add(x,y,);
add(y,x,);
}
else{
add(y,x,);
add(x,y,);
}//建无向边,x,y距离是1,表示x<y 先过x后过 y
}
dfs();
ull ans=;
for(int i=;i<=size[];i++)
{
ans=(ans+f[][i])%mod;
}//方案数
printf("%llu\n",ans);
t--;
}
return ;
}
基本思路和代码参考shadowice1984,https://www.luogu.org/blog/ShadowassIIXVIIIIV/solution-p4099
详细化了很多。
[HEOI2013]SAO ——计数问题的更多相关文章
- 3167: [Heoi2013]Sao [树形DP]
3167: [Heoi2013]Sao 题意: n个点的"有向"树,求拓扑排序方案数 Welcome to Sword Art Online!!! 一开始想错了...没有考虑一个点 ...
- 【BZOJ3167】[HEOI2013]SAO(动态规划)
[BZOJ3167][HEOI2013]SAO(动态规划) 题面 BZOJ 洛谷 题解 显然限制条件是一个\(DAG\)(不考虑边的方向的话就是一棵树了). 那么考虑树型\(dp\),设\(f[i][ ...
- P4099 [HEOI2013]SAO
P4099 [HEOI2013]SAO 贼板子有意思的一个题---我()竟然没看题解 有一张连成树的有向图,球拓扑序数量. 树形dp,设\(f[i][j]\)表示\(i\)在子树中\(i\)拓扑序上排 ...
- BZOJ 3167: [Heoi2013]Sao
3167: [Heoi2013]Sao Time Limit: 30 Sec Memory Limit: 256 MBSubmit: 96 Solved: 36[Submit][Status][D ...
- P4099 [HEOI2013]SAO(树形dp)
P4099 [HEOI2013]SAO 我们设$f[u][k]$表示以拓扑序编号为$k$的点$u$,以$u$为根的子树中的元素所组成的序列方案数 蓝后我们在找一个以$v$为根的子树. 我们的任务就是在 ...
- 【BZOJ3167/4824】[Heoi2013]Sao/[Cqoi2017]老C的键盘
[BZOJ3167][Heoi2013]Sao Description WelcometoSAO(StrangeandAbnormalOnline).这是一个VRMMORPG,含有n个关卡.但是,挑战 ...
- [HEOI2013]SAO(树上dp,计数)
[HEOI2013]SAO (这写了一个晚上QAQ,可能是我太蠢了吧.) 题目说只有\(n-1\)条边,然而每个点又相互联系.说明它的结构是一个类似树的结构,但是是有向边连接的,题目问的是方案个数,那 ...
- 【做题记录】 [HEOI2013]SAO
P4099 [HEOI2013]SAO 类型:树形 \(\text{DP}\) 这里主要补充一下 \(O(n^3)\) 的 \(\text{DP}\) 优化的过程,基础转移方程推导可以参考其他巨佬的博 ...
- [BZOJ3167][P4099][HEOI2013]SAO(树形DP)
题目描述 Welcome to SAO ( Strange and Abnormal Online).这是一个 VR MMORPG, 含有 n 个关卡.但是,挑战不同关卡的顺序是一个很大的问题. 有 ...
随机推荐
- spring boot 实现密码连续输入错误5次,限制十分钟内不能进行登录
我们要实现的就是,密码连续输入错误5次,就限制用户十分钟不能进行登录. 大致的流程图 数据库设计如下 DROP TABLE IF EXISTS `user`; CREATE TABLE `user` ...
- JAVA核心:内存、比较和Final
1.java是如何管理内存的 java的内存管理就是对象的分配和释放问题.(其中包括两部分) 分配:内存的分配是由程序完成的,程序员需要通过关键字new为每个对象申请内存空间(基本类型除外),所有的对 ...
- Java实验报告一:Java开发环境的熟悉
实验要求: 1. 使用JDK编译.运行简单的Java程序 2.使用Eclipse 编辑.编译.运行.调试Java程序 实验内容 (一) 命令行下Java程序开发 (二)Eclipse下Java程序 ...
- 同步手绘板——android端取色
作为绘图软件,颜色的选取必不可少,在刚开始取色时,所选颜色和显示颜色始终不一致,比如选取白色显示绿色,在这个问题上消耗了太多的时间,后来发现是比例问题,通过修改实现恰当的取色.
- 关于本科毕业论文《Laguerre小波在数值积分与微分方程数值解中的应用》存在的问题与小结
本科的毕业设计<Laguerre小波在数值积分与微分方程数值解中的应用>是通过Laguerre小波函数来近似表达某个需要求积分或解微分方程的函数,将原函数很难求得函数用小波函数表达出来,这 ...
- atcoder B - Frog 2 (DP)
B - Frog 2 Time Limit: 2 sec / Memory Limit: 1024 MB Score : 100100 points Problem Statement There a ...
- JavaScript代码-----位置决定结果
刚学JavaScript的时候,即使照着书上的代码敲一遍,运行的时候,得到的结果要么总是和书上的结果不同,要么是没产生效果.学到后面,才明白到其实程序的代码是没错的,错误的是代码的位置! 首先看下面这 ...
- PAT 1021 个位数统计
https://pintia.cn/problem-sets/994805260223102976/problems/994805300404535296 给定一个k位整数N = d~k-1~*10^ ...
- HTML使用button的一个小坑
https://www.w3schools.com/TAGs/att_button_type.asp Definition and Usage The type attribute specifies ...
- Oracle10.2.0.1以及其他版本升级Oracle10.2.0.5的简单步骤
Oracle没有发布 完整版的 Oracle 10.2.0.5 的安装包,只能是通过安装完10.2.0.4 之后再升级10.2.0.5 这一点挺坑的. 建安记录一下步骤. 1. 挂载Oracle10. ...