您的位置:首页 > 其它

【GDOI2018模拟7.7】暴力大神hxx 树形dp

2017-07-07 20:55 323 查看
题意:给你n个嵌套for语句,然后从第二个开始每一个循环的起点或者是终点是变量,问你会循环多少次。

这是一道好题。

手玩一下可以很容易发现,上下之间有可以递推的关系,但是直接递推会炸,所以需要dp。

假设语句是这样的:

for i 1 100

for j i 100

for k j 100

我们可以手玩一下发现:

只有一条语句:

1-100

两条语句:

(1-100,2-100,3-100…….100-100) 相邻之间差为1.

三条语句(这里对齐一下让大家更好理解):
(1-100,2-100,3-100.......100-100)
(2-100,3-100.......100-100)
(3-100.......100-100)
....
注意从这里开始差变得不规则了,我把这种不规则的差称之为量级。
可以发现的是,量级的大小明显和嵌套语句的个数有一个公式可以互相转换,那么用相关的数学知识应该是可以推出的(我不会QAQ),但是对于一个OIER,做到这一步就够了。


我们并不需要直接计算出量级,只要无脑往上用乘法原理乘乘乘就好了。

可以发现,第n条语句和第n-1条直接相关,可以通过他推出来,但是他也和第n-2…1条语句间接有关,所以,可以联想到树形dp(比较关键的一步,其实挺好想,模型很明显了)

然后,设f[x][i]以x为根的子树的答案,或者说:把变量x取值为i时的计算结果

那么根据乘法原理直接把所有子树乘起来就好了,但是还是会T,用前缀和优化一下(第二个比较关键的地方),具体看代码。

转化模型和处理方法都十分经典。

#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--);
using namespace std;
typedef long long ll;
const int mo=12015858;
const int N=1e5+5;
char x
,y
,n;
int l
,r
,f[27]
;
int tot;
bool vis
;
int head
,go
,next
,val
;
inline void add(int x,int y)
{
go[++tot]=y;
next[tot]=head[x];
head[x]=tot;
}
inline void dfs(int x)
{
for(int i=head[x];i;i=next[i])
{
int v=go[i];
dfs(v);
}
fo(i,1,100000)
{
f[x][i]=1;
for(int j=head[x];j;j=next[j])
{
int v=go[j];
if (!l[v]&&i>r[v]||!r[v]&&i<l[v])
{
f[x][i]=0;
break;
}
if (!r[v])f[x][i]=1ll*f[x][i]*(f[v][i]-f[v][l[v]-1])%mo;
else f[x][i]=1ll*f[x][i]*(f[v][r[v]]-f[v][i-1])%mo;
}
f[x][i]=(f[x][i]+mo)%mo;
}
fo(i,1,100000)f[x][i]=(f[x][i]+f[x][i-1])%mo;
}
int main()
{

scanf("%d",&n);
fo(i,1,n)
{
char ch[7];
scanf("%s",ch);
int len=strlen(ch);
if (ch[0]<='z'&&ch[0]>='a')
{
add(ch[0]-'a'+1,i);
vis[i]=1;
}
else
fo(j,0,len-1)l[i]=l[i]*10+ch[j]-'0';

scanf("%s",ch);
len=strlen(ch);
if (ch[0]<='z'&&ch[0]>='a')
{
add(ch[0]-'a'+1,i);
vis[i]=1;
}
else
fo(j,0,len-1)r[i]=r[i]*10+ch[j]-'0';
}
ll ans=1;
fo(i,1,n)if (!vis[i])
{
dfs(i);
ans=ans*1ll*(f[i][r[i]]-f[i][l[i]-1])%mo;
}
printf("%lld\n",(ans+mo)%mo);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: