HDU 5789 Permutation

来源:互联网 发布:python调用caffe测试 编辑:程序博客网 时间:2024/06/11 04:50

给定一棵大小为n的有根树,点的标号为1,…,n。现在要把树转化成一个序列,要求一个点的后代在序列中先出现,求所有满足条件的序列的逆序对数之和。
n≤50

dp[i][j][k]:i子树中j在第k个位置的合法排列情况数

定义子树对应的序列为副序列,已合并的子树对应的序列为主序列
主序列的长度为len,副序列长度为 len
每次把主序列和副序列合并得到新的主序列

首先两个序列内部的逆序对数之和随着合并后总方案数增多而增多
ans[i]表示内部的逆序对数之和,cas[i]表示序列的方案数,x表示主序列,son表示副序列,x表示新的主序列

ans[x]=Clenlen+len×(ans[x]×cas[son]+ans[son]×cas[x])

方便起见,我们可以只枚举副序列中的元素i、其所在位置k、插入到主序列中的第j个数后面,于是只要预处理前缀和O(1)得到主序列中,大小i,位置>j的合法排列情况数总和与大小>i,位置j的合法排列情况数总和即可。

sum[i][j]表示主序列中,大小i,位置j的合法排列情况数总和

sum[i][j]=piqjdp[x][i][j]

a表示把副序列中的第k个数插入到主序列第j个数后面的方案数

a=Cjk1+jClenjlenk+lenj

b表示大小i,位置>j的合法排列情况数总和
b=sum[i][len]sum[i][j]

c表示大小>i,位置j的合法排列情况数总和
c=sum[n][j]sum[i][j]

ans[x]+=dp[son][i][k]×a×(b+c)

计算贡献的复杂度为O(n4)

合并也是类似的,不过不同的是在计算贡献的时候两边的序列相当于确定了,所以组合数乘一下就好了,合并的时候只是把一个序列安置到一个新序列中,所以还要乘上另一个序列总的方案数

枚举元素、原位置、新位置、共n次。
合并的复杂度为O(n4)

每次合并之后重新预处理一下这个二维前缀和。
处理前缀和的复杂度为O(n3)

所以我该怎么努力一下降到O(n3)呢==
是我的做法太朴素了先天不足吗==欢迎大家提供O(n3)的做法

树形dp

#include <set>#include <ctime>#include <queue>#include <cstdio>#include <bitset>#include <cctype>#include <bitset>#include <cstdlib>#include <cassert>#include <cstring>#include <iostream>#include <algorithm>#define inf (1<<30)#define INF (1ll<<62)#define fi first#define se second#define rep(x,s,t) for(register int x=s,t_=t;x<t_;++x)#define per(x,s,t) for(register int x=t-1,s_=s;x>=s_;--x)#define travel(x) for(int I=last[x],to;~I&&(to=e[I].to);I=e[I].nxt)#define prt(x) cout<<#x<<":"<<x<<" "#define prtn(x) cout<<#x<<":"<<x<<endl#define pb(x) push_back(x)#define hash asfmaljkg#define rank asfjhgskjf#define y1 asggnja#define y2 slfvmusing namespace std;typedef long long ll;typedef pair<int,int> ii;template<class T>void sc(T &x){    int f=1;char c;x=0;    while(c=getchar(),c<48)if(c=='-')f=-1;    do x=x*10+(c^48);    while(c=getchar(),c>47);    x*=f;}template<class T>void nt(T x){    if(!x)return;    nt(x/10);    putchar(x%10+'0');}template<class T>void pt(T x){    if(x<0)putchar('-'),x=-x;    if(!x)putchar('0');    else nt(x);}template<class T>void ptn(T x){    pt(x);putchar('\n');}template<class T>void pts(T x){    pt(x);putchar(' ');}template<class T>inline void Max(T &x,T y){if(x<y)x=y;}template<class T>inline void Min(T &x,T y){if(x>y)x=y;}const int maxn=55;const int mod=1e9+7;int n,rt;int last[maxn],ecnt;struct Edge{    int to,nxt;}e[maxn<<1];void ins(int u,int v){    e[ecnt]=(Edge){v,last[u]};    last[u]=ecnt++;}ll cnk[maxn][maxn];ll ans[maxn],ss[maxn];int sz[maxn];int a[maxn],lx[maxn],rx[maxn],dfs_clock;//用来枚举子树元素 void dfs(int x,int f){    lx[x]=++dfs_clock;a[dfs_clock]=x;    travel(x)if(to!=f)        dfs(to,x);    rx[x]=dfs_clock;    sz[x]=rx[x]-lx[x]+1;}ll sum[maxn][maxn];ll dp[maxn][maxn][maxn];void deal(int x,int f){    ans[x]=0;    travel(x)if(to!=f)        deal(to,x);    int p=0;ss[x]=1;    travel(x)if(to!=f){        ans[x]=(ans[x]*cnk[p+sz[to]][sz[to]]%mod*ss[to]%mod              +ans[to]*cnk[p+sz[to]][sz[to]]%mod*ss[x]%mod)%mod;//        ans[x]%=mod;        rep(i,lx[to],rx[to]+1){//枚举元素             rep(j,0,p+1){//枚举插入的位置                rep(k,1,sz[to]+1){                     ll a=cnk[k-1+j][j]*cnk[sz[to]-k+p-j][p-j]%mod;                    ll b=(mod+sum[n][j]-sum[::a[i]][j])%mod;                    ll c=(mod+sum[::a[i]][p]-sum[::a[i]][j])%mod;                    ans[x]+=dp[to][::a[i]][k]*a%mod*(b+c)%mod;///                    ans[x]%=mod;                }            }        }        rep(i,1,n+1)rep(j,1,n+1)sum[i][j]=0;        int np=p+sz[to];        rep(k,1,np+1){//枚举在新排列中的位置             rep(i,lx[x]+1,lx[x]+p+1){//枚举主排列的元素                 rep(j,max(k-sz[to],1),min(k,p)+1){//枚举在主排列中的位置                     sum[a[i]][k]+=dp[x][a[i]][j]*cnk[k-1][j-1]%mod*cnk[np-k][p-j]%mod*ss[to]%mod;//ss                    sum[a[i]][k]%=mod;                }            }            rep(i,lx[to],rx[to]+1){//枚举副排列的元素                rep(j,max(k-p,1),min(k,sz[to])+1){//枚举在副排列中的位置                    sum[a[i]][k]+=dp[to][a[i]][j]*cnk[k-1][j-1]%mod*cnk[np-k][sz[to]-j]%mod*ss[x]%mod;                    sum[a[i]][k]%=mod;                }            }        }        ss[x]=ss[x]*cnk[np][p]%mod*ss[to]%mod;        p=np;        rep(i,1,n+1)rep(j,1,n+1){            dp[x][i][j]=sum[i][j];            sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+dp[x][i][j];        }    }    dp[x][x][sz[x]]=ss[x];    ans[x]+=sum[n][p]-sum[x][p];    ans[x]%=mod;}void solve(){    memset(last,-1,sizeof last);    memset(dp,0,sizeof dp);    memset(sum,0,sizeof sum);    ecnt=0;    rep(i,1,n){        int u,v;        sc(u);sc(v);        ins(u,v);ins(v,u);    }    dfs_clock=0;dfs(rt,0);    deal(rt,0);    ptn(ans[rt]);}int main(){//  freopen("pro.in","r",stdin);//  freopen("chk.out","w",stdout);//  int cas;sc(cas);    rep(i,0,maxn){        rep(j,1,i) cnk[i][j]=(cnk[i-1][j-1]+cnk[i-1][j])%mod;        cnk[i][0]=cnk[i][i]=1;    }    while(~scanf("%d%d",&n,&rt))solve();    return 0;}

蒟蒻:
开始ans[]合并的不好,从开始写到AC中间隔了几道题,改掉原来错误的合并花了不少时间,封装起来应该会更好看一些,以后写题最好一次写一题(但昨天中途写的那题确实蛮有趣的==)

依然有笔误:ss写成了sz,x和to写混了
变量名应该写的好看一些是吧==以后表示方案数的变量就叫…cas[]?

诈尸灵魂OIer Tony整理的变量名:
mx to sum tmp num tar now cur top col flag data memo step rank host block delta factor rx ry res cnt pos val str vec tot dir mark tree perm from list spot Q/que mn/mi dp fa ans len dis nxt par cas org sub used left root comb heap edge prime matrix perm

欢迎提供新的变量名:)

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 汽车导航触摸屏失灵怎么办 爱丽舍导航仪触屏失灵怎么办 手机要开机很久怎么办 小米手机屏失灵怎么办 5s手机屏幕松动怎么办 手机按钮不灵了怎么办 手机触摸屏不灵了怎么办 mac触摸板失灵怎么办 苹果屏幕触摸失灵怎么办 三星手机屏幕没反应怎么办 手机开机定屏怎么办 手机触摸局部失灵怎么办 苹果手机屏幕按键失灵怎么办 5s锁屏键坏了怎么办 平板版本太低怎么办 手机屏局部失灵怎么办 iphone8触屏不灵敏怎么办 苹果机8屏幕失灵怎么办 车钥匙丢车上怎么办 指纹锁华盖坏了怎么办 非法入了户口怎么办 司考成绩单丢了怎么办 小饭桌转让手续怎么办 两个领导不和你怎么办 两个领导意见不一致怎么办 两个领导对立我怎么办 投诉申通没用怎么办 领导作风有问题怎么办 做完火疗受风了难受怎么办 鼻子做的不好看怎么办 埋线双眼皮出血怎么办 割完双眼皮出血怎么办 全切双眼皮出血怎么办 割双眼皮出血了怎么办 割双眼皮后出血怎么办 双眼皮手术后出血怎么办 缝双眼皮开了怎么办 朋友网没了怎么办 压疮发生后怎么办 学籍账号已登录怎么办 护士学分不达标怎么办