【BZOJ3167/4824】[Heoi2013]Sao/[Cqoi2017]老C的键盘
【BZOJ3167】[Heoi2013]Sao
Description
Input
Output
对于每个数据,输出一行一个整数,为攻克关卡的顺序方案个数,mod1,000,000,007输出。
Sample Input
10
5 > 8
5 > 6
0 < 1
9 < 4
2 > 5
5 < 9
8 < 1
9 > 3
1 < 7
Sample Output
题解:憋了一下午想出来的树形DP题~
如何设状态呢?显然应该是个二维的状态。设f[i][j]表示在i的子树中,i位于第j个位置的方案数。那么我们如何将x的当前状态与他的儿子的状态合并呢?
先只考虑x<y的情况,我们设原来的siz[x]=sa,siz[y]=sb,sa+sb=sc,我们想用f[x][a]和f[y][b]来更新f[x][c](c>=a+b),为了区分新的f和旧的f,我们用g[c]表示新的f。
如果x位于位置c,那么它左边有c-1个位置,相当于将y的序列中左边的数与x的序列中左边的数进行了二路归并,并且归并的顺序可以随便确定,那么方案数就是$C_{c-1}^{a-1}$,同理,右面的sc-c个数也可以归并处理,方案数是$C_{sc-c}^{sa-a}$,所以得到方程:
$g[c]=\sum\limits_{a=1}^{c}\sum\limits_{b=1}^{c-a}C_{c-1}^{a-1}C_{sc-c}^{sa-a}f[x][a]*f[y][b]$
可以将f[y][b]提出来
$g[c]=\sum\limits_{b=1}^{c}f[y][b]\sum\limits_{a=1}^{c-b}C_{c-1}^{a-1}C_{sc-c}^{sa-a}f[x][a]$
这个东西就可以用前缀和维护了~
不过值得惭愧的是,我的代码的上界设的不紧,或是循环顺序不太对,复杂度其实应该是O(n^3)的,似乎可以改一改使得复杂度变成树形背包的优雅的O(n^2)。不过还是卡过了,就没有改,求不卡~不过处理4824那题还是很轻松的,因为是完全二叉树嘛~
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
typedef long long ll;
const ll P=1000000007;
int n,cnt;
ll ans;
ll C[1010][1010];
ll f[1010][1010],g[1010],s[1010];
int to[2010],next[2010],head[1010],val[2010],fa[1010],siz[1010];
char str[5];
void dfs(int x)
{
int i,y,a,b,c;
siz[x]=1,f[x][1]=1;
for(i=head[x];i!=-1;i=next[i]) if(to[i]!=fa[x])
{
y=to[i],fa[y]=x,dfs(y);
memset(g,0,sizeof(g[0])*(siz[x]+siz[y]+1));
if(val[i]==1)
{
for(c=2;c<=siz[x]+siz[y];c++)
{
s[max(1,c-siz[y])-1]=0;
for(b=1;b<=min(siz[x],c-1);b++)
s[b]=(s[b-1]+C[c-1][b-1]*C[siz[x]+siz[y]-c][siz[x]-b]%P*f[x][b])%P;
for(a=1;a<=min(siz[y],c-1);a++)
{
if(c-a>min(siz[x],c-1)) s[c-a]=s[min(siz[x],c-1)];
if(c-a<max(1,c-siz[y])) s[c-a]=0;
g[c]=(g[c]+s[c-a]*f[y][a])%P;
}
}
f[x][1]=0;
for(c=2;c<=siz[x]+siz[y];c++) f[x][c]=g[c];
}
else
{
for(c=2;c<=siz[x]+siz[y];c++)
{
s[max(1,c-siz[y])-1]=0;
for(b=1;b<=min(siz[x],c-1);b++)
s[b]=(s[b-1]+C[c-1][b-1]*C[siz[x]+siz[y]-c][siz[x]-b]%P*f[x][siz[x]-b+1])%P;
for(a=1;a<=min(siz[y],c-1);a++)
{
if(c-a>min(siz[x],c-1)) s[c-a]=s[min(siz[x],c-1)];
if(c-a<max(1,c-siz[y])) s[c-a]=0;
g[c]=(g[c]+s[c-a]*f[y][siz[y]-a+1])%P;
}
}
f[x][siz[x]+siz[y]]=0;
for(c=1;c<siz[x]+siz[y];c++) f[x][c]=g[siz[x]+siz[y]-c+1];
}
siz[x]=siz[x]+siz[y];
}
}
inline void add(int a,int b,int c)
{
to[cnt]=b,val[cnt]=c,next[cnt]=head[a],head[a]=cnt++;
}
void work()
{
scanf("%d",&n);
int i,a,b;
memset(head,-1,sizeof(head)),cnt=0;
memset(f,0,sizeof(f)),memset(fa,0,sizeof(fa));
for(i=1;i<n;i++)
{
scanf("%d%s%d",&a,str,&b),a++,b++;
if(str[0]=='>') add(a,b,1),add(b,a,0);
if(str[0]=='<') add(a,b,0),add(b,a,1);
}
dfs(1);
ans=0;
for(i=1;i<=n;i++) ans=(ans+f[1][i])%P;
printf("%lld\n",ans);
}
int main()
{
int T,i,j;
C[0][0]=1;
for(i=1;i<=1000;i++)
{
C[i][0]=1;
for(j=1;j<=i;j++) C[i][j]=(C[i-1][j-1]+C[i-1][j])%P;
}
scanf("%d",&T);
while(T--) work();
return 0;
}//5 10 5 > 8 5 > 6 0 < 1 9 < 4 2 > 5 5 < 9 8 < 1 9 > 3 1 < 7 10 6 > 7 2 > 0 9 < 0 5 > 9 7 > 0 0 > 3 7 < 8 1 < 2 0 < 4 10 2 < 0 1 > 4 0 > 5 9 < 0 9 > 3 1 < 2 4 > 6 9 < 8 7 > 1 10 0 > 9 5 > 6 3 > 6 8 < 7 8 > 4 0 > 6 8 > 5 8 < 2 1 > 8 10 8 < 3 8 < 4 1 > 3 1 < 9 3 < 7 2 < 8 5 > 2 5 < 6 0 < 9
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
typedef long long ll;
const ll P=1000000007;
int n,cnt;
ll ans;
ll C[1010][1010];
ll f[1010][1010],g[1010],s[1010];
int to[2010],next[2010],head[1010],val[2010],fa[1010],siz[1010];
char str[5];
void dfs(int x)
{
int i,y,a,b,c;
siz[x]=1,f[x][1]=1;
for(i=head[x];i!=-1;i=next[i]) if(to[i]!=fa[x])
{
y=to[i],fa[y]=x,dfs(y);
memset(g,0,sizeof(g[0])*(siz[x]+siz[y]+1));
if(val[i]==1)
{
for(c=2;c<=siz[x]+siz[y];c++)
{
s[max(1,c-siz[y])-1]=0;
for(b=1;b<=min(siz[x],c-1);b++)
s[b]=(s[b-1]+C[c-1][b-1]*C[siz[x]+siz[y]-c][siz[x]-b]%P*f[x][b])%P;
for(a=1;a<=min(siz[y],c-1);a++)
{
if(c-a>min(siz[x],c-1)) s[c-a]=s[min(siz[x],c-1)];
if(c-a<max(1,c-siz[y])) s[c-a]=0;
g[c]=(g[c]+s[c-a]*f[y][a])%P;
}
}
f[x][1]=0;
for(c=2;c<=siz[x]+siz[y];c++) f[x][c]=g[c];
}
else
{
for(c=2;c<=siz[x]+siz[y];c++)
{
s[max(1,c-siz[y])-1]=0;
for(b=1;b<=min(siz[x],c-1);b++)
s[b]=(s[b-1]+C[c-1][b-1]*C[siz[x]+siz[y]-c][siz[x]-b]%P*f[x][siz[x]-b+1])%P;
for(a=1;a<=min(siz[y],c-1);a++)
{
if(c-a>min(siz[x],c-1)) s[c-a]=s[min(siz[x],c-1)];
if(c-a<max(1,c-siz[y])) s[c-a]=0;
g[c]=(g[c]+s[c-a]*f[y][siz[y]-a+1])%P;
}
}
f[x][siz[x]+siz[y]]=0;
for(c=1;c<siz[x]+siz[y];c++) f[x][c]=g[siz[x]+siz[y]-c+1];
}
siz[x]=siz[x]+siz[y];
}
}
inline void add(int a,int b,int c)
{
to[cnt]=b,val[cnt]=c,next[cnt]=head[a],head[a]=cnt++;
}
void work()
{
scanf("%d",&n);
int i,a,b;
memset(head,-1,sizeof(head)),cnt=0;
memset(f,0,sizeof(f)),memset(fa,0,sizeof(fa));
for(i=1;i<n;i++)
{
scanf("%d%s%d",&a,str,&b),a++,b++;
if(str[0]=='>') add(a,b,1),add(b,a,0);
if(str[0]=='<') add(a,b,0),add(b,a,1);
}
dfs(1);
ans=0;
for(i=1;i<=n;i++) ans=(ans+f[1][i])%P;
printf("%lld\n",ans);
}
int main()
{
int T,i,j;
C[0][0]=1;
for(i=1;i<=1000;i++)
{
C[i][0]=1;
for(j=1;j<=i;j++) C[i][j]=(C[i-1][j-1]+C[i-1][j])%P;
}
scanf("%d",&T);
while(T--) work();
return 0;
}//5 10 5 > 8 5 > 6 0 < 1 9 < 4 2 > 5 5 < 9 8 < 1 9 > 3 1 < 7 10 6 > 7 2 > 0 9 < 0 5 > 9 7 > 0 0 > 3 7 < 8 1 < 2 0 < 4 10 2 < 0 1 > 4 0 > 5 9 < 0 9 > 3 1 < 2 4 > 6 9 < 8 7 > 1 10 0 > 9 5 > 6 3 > 6 8 < 7 8 > 4 0 > 6 8 > 5 8 < 2 1 > 8 10 8 < 3 8 < 4 1 > 3 1 < 9 3 < 7 2 < 8 5 > 2 5 < 6 0 < 9
【BZOJ3167/4824】[Heoi2013]Sao/[Cqoi2017]老C的键盘的更多相关文章
- 【BZOJ3167】[HEOI2013]SAO(动态规划)
[BZOJ3167][HEOI2013]SAO(动态规划) 题面 BZOJ 洛谷 题解 显然限制条件是一个\(DAG\)(不考虑边的方向的话就是一棵树了). 那么考虑树型\(dp\),设\(f[i][ ...
- [BZOJ4824][CQOI2017]老C的键盘(树形DP)
4824: [Cqoi2017]老C的键盘 Time Limit: 10 Sec Memory Limit: 512 MBSubmit: 193 Solved: 149[Submit][Statu ...
- [BZOJ4824][Cqoi2017]老C的键盘 树形dp+组合数
4824: [Cqoi2017]老C的键盘 Time Limit: 10 Sec Memory Limit: 512 MBSubmit: 218 Solved: 171[Submit][Statu ...
- [CQOI2017]老C的键盘
[CQOI2017]老C的键盘 题目描述 额,网上题解好像都是用的一大堆组合数,然而我懒得推公式. 设\(f[i][j]\)表示以\(i\)为根,且\(i\)的权值为\(j\)的方案数. 转移: \[ ...
- bzoj 4824: [Cqoi2017]老C的键盘
Description 老 C 是个程序员. 作为一个优秀的程序员,老 C 拥有一个别具一格的键盘,据说这样可以大幅提升写程序的速度,还能让写出来的程序 在某种神奇力量的驱使之下跑得非常快.小 ...
- [bzoj4824][Cqoi2017]老C的键盘
来自FallDream的博客,未经允许,请勿转载,谢谢. 老 C 是个程序员. 作为一个优秀的程序员,老 C 拥有一个别具一格的键盘,据说这样可以大幅提升写程序的速度,还能让写出来的程序在某种 ...
- [bzoj4824][洛谷P3757][Cqoi2017]老C的键盘
Description 老 C 是个程序员. 作为一个优秀的程序员,老 C 拥有一个别具一格的键盘,据说这样可以大幅提升写程序的速度,还能让写出来的程序 在某种神奇力量的驱使之下跑得非常快.小 Q 也 ...
- Luogu P3757 [CQOI2017]老C的键盘
题目描述 老C的键盘 题解 显然对于每个数 x 都有唯一对应的 \(x/2\) , 然而对于每个数 x 却可以成为 \(x*2\) 和 \(x*2+1\) 的对应数 根据这一特性想到了啥??? 感谢l ...
- [BZOJ3167][P4099][HEOI2013]SAO(树形DP)
题目描述 Welcome to SAO ( Strange and Abnormal Online).这是一个 VR MMORPG, 含有 n 个关卡.但是,挑战不同关卡的顺序是一个很大的问题. 有 ...
随机推荐
- JAVA中string.replace()和string.replaceAll()的区别及用法
乍一看,字面上理解好像replace只替换第一个出现的字符(受javascript的影响),replaceall替换所有的字符,其实大不然,只是替换的用途不一样. public String r ...
- var result = eval('(' + data + ')');的学习
$.post("url", function(data) { //这里的function(data)这里的data是前端页面获取的后台的返回的数据: var result = ev ...
- x:Class, x:Key
x:Class: 用来创建一个partial的class, 比如默认生成的x:Class="MyTest.MainWindow", 会自动生成一个MainWindow的partia ...
- Js+CSS3实现手风琴效果
效果截图: HTML代码: <div id="container"> <img src="images/photo01.jpg" alt=&q ...
- 数学:乘法逆元-拓展GCD
乘法逆元应用在组合数学取模问题中,这里给出的实现不见得好用 给出拓展GCD算法: 扩展欧几里得算法是指对于两个数a,b 一定能找到x,y(均为整数,但不满足一定是正数) 满足x*a+y*b=gcd(a ...
- bzoj4886 [Lydsy2017年5月月赛]叠塔游戏
传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4886 [题解] 跟bzoj4883:http://www.cnblogs.com/galax ...
- Oop分析方法
为了实现Oop,这个我已经在一个前端的js项目中实现了Oop,过后总结:对于js这种动态语言,可以在运行时动态组件对象的属性和方法这种,解释型的语言来讲,真的是OOP,如果不存在关系数据库,仅仅是从后 ...
- [转]如何整理Linux磁盘碎片,竟与Windows的方式大不同 返回操作系统首页
Linux 系统永远不需要整理磁盘碎片的神话相信很多人都听说过.由于 Linux 采用了优秀的日志文件系统(ext2.ext3.ext4, btrfs等),在绝大多数情况下确实是不需要进行磁盘碎片整理 ...
- DECODE 与CASE WHEN 的比较以及用法
1.DECODE 只有Oracle 才有,其它数据库不支持; 2.CASE WHEN的用法, Oracle.SQL Server. MySQL 都支持; 3.DECODE 只能用做相等判断,但是可以配 ...
- textbox自动提示
AutoCompleteStringCollection myCutomSource = new AutoCompleteStringCollection(); myCutomS ...