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

来源:互联网 发布:淘宝reebonz海外旗舰店 编辑:程序博客网 时间:2024/06/05 14:26

题意:给你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-1002-1003-100.......100-100)       (2-1003-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[N],y[N],n;int l[N],r[N],f[27][N];int tot;bool vis[N];int head[N],go[N],next[N],val[N];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);}