【BZOJ3167/4824】[Heoi2013]Sao/[Cqoi2017]老C的键盘
【BZOJ3167】[Heoi2013]Sao
Description
WelcometoSAO(StrangeandAbnormalOnline)。这是一个VRMMORPG,含有n个关卡。但是,挑战不同关卡的顺序是一个很大的问题。有n–1个对于挑战关卡的限制,诸如第i个关卡必须在第j个关卡前挑战,或者完成了第k个关卡才能挑战第l个关卡。并且,如果不考虑限制的方向性,那么在这n–1个限制的情况下,任何两个关卡都存在某种程度的关联性。即,我们不能把所有关卡分成两个非空且不相交的子集,使得这两个子集之间没有任何限制。
Input
第一行,一个整数T,表示数据组数。对于每组数据,第一行一个整数n,表示关卡数。接下来n–1行,每行为“i sign j”,其中0≤i,j≤n–1且i≠j,sign为“<”或者“>”,表示第i个关卡必须在第j个关卡前/后完成。
T≤5,1≤n≤1000
Output
对于每个数据,输出一行一个整数,为攻克关卡的顺序方案个数,mod1,000,000,007输出。
Sample Input
1
10
5 > 8
5 > 6
0 < 1
9 < 4
2 > 5
5 < 9
8 < 1
9 > 3
1 < 7
10
5 > 8
5 > 6
0 < 1
9 < 4
2 > 5
5 < 9
8 < 1
9 > 3
1 < 7
Sample Output
2580
题解:憋了一下午想出来的树形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的序列中左边的数进行了二路归并,并且归并的顺序可以随便确定,那么方案数就是,同理,右面的sc-c个数也可以归并处理,方案数是,所以得到方程:
可以将f[y][b]提出来
这个东西就可以用前缀和维护了~
不过值得惭愧的是,我的代码的上界设的不紧,或是循环顺序不太对,复杂度其实应该是O(n^3)的,似乎可以改一改使得复杂度变成树形背包的优雅的O(n^2)。不过还是卡过了,就没有改,求不卡~不过处理4824那题还是很轻松的,因为是完全二叉树嘛~
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | #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 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | #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 |
| 欢迎来原网站坐坐! >原文链接<
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析