hdu 6044 hdu 6305 笛卡尔树

hdu6044
题意:给出每个点是某段区间的最大值,问构造一个1到n的排列,有多少种符合要求的。
做法:用笛卡尔树,如果对于一个序列,最大值可以把序列分成两段,左边一段和右边一段,然后在对这两段进行分割,,,最大值连接左右子段的最大值,就可以得到一颗树,这棵树有两个性质,左边的点比根的下标小,有点的点比跟的下标大。跟的值是子树中最大的,这就刚好符合笛卡尔树的性质,然后就可以利用题目中给出的信息构造笛卡尔树,如果没有合法的树,那么答案就是0,否者1:可以用排列的方法求出答案,对于一个子树,跟部肯定要选择最大的树,然后把剩下的点选出一点放到左边,选出一些放到右边,方案树就是comb(区间长度-1,左区间长度),然后乘以左右子树的构造方案树,就是这个子树的方案了,递归o(n)的。
2:如果题目中的数据可以构造出一颗笛卡尔树,那么答案就是n!/(每个节点的儿子个数),n个数可以构造n!个不同序列,那么对于某个子树来说,合法的方案概率是这个子树的节点数的倒数,那么每个节点都符合的概率就是所有节点它儿子个数的乘积的倒数。

#include<bits/stdc++.h>
using namespace std;
const int N = 1e6+100;
int l[N],r[N];
int ls[N],rs[N],root;
int n;
int f[N],inv[N],invf[N];
int que[N];
const int mod = 1e9+7;
bool judge(int x,int y){
    return r[y]-l[y]-r[x]+l[x] > 0;
}

namespace IO {
    const int MX = 4e7; //1e7 占用内存 11000kb
    char buf[MX]; int c, sz;
    void begin() {
        c = 0;
        sz = fread(buf, 1, MX, stdin);//一次性全部读入
        /*int tot = 1;
        char now;
        while((now = getchar()) != EOF){
            buf[tot++] = now;
        }
        sz = tot;*/
    }
    inline bool read(int &t) {
        while (c < sz && buf[c] != '-' && (buf[c] < '0' || buf[c] > '9')) c++;
        if (c >= sz) return false;//若读完整个缓冲块则退出
        bool flag = 0; if(buf[c] == '-') flag = 1, c++;
        for(t = 0; c < sz && '0' <= buf[c] && buf[c] <= '9'; c++) t = t * 10 + buf[c] - '0';
        if(flag) t = -t;
        return true;
    }
}

void build(){
    int tp = 0;
    root =0;
    for(int i = 1;i <= n;i ++){
        ls[i] = rs[i] = 0;
        while(tp && judge(que[tp],i)) tp --;
        if(tp == 0){
            ls[i] = root;
            root = i;
        }
        else{
            ls[i] = rs[que[tp]];
            rs[que[tp]] = i;
            rs[i] =0;
        }
        que[++tp] = i;
    }
}
void init(){
    inv[1] = 1;
    for(int i= 2;i < N;i ++){
        inv[i] = (mod-mod/i)*1LL*inv[mod%i]%mod;
    }
    f[0] = invf[0] = 1;
    for(int i= 1;i < N;i ++){
        f[i] = f[i-1]*1LL*i%mod;
        invf[i] = invf[i-1]*1LL*inv[i]%mod;
    }
}
int comb(int x,int y){
    return 1LL*f[x]*invf[y]%mod*invf[x-y]%mod;
}

long long dfs(int x,int nl,int nr){
    if(x == 0){
        return 1;
    }

    if(l[x] != nl ||r[x] != nr) return 0;
    long long op = comb(nr-nl+1,r[x]-l[x]+1);
    op = op*comb(nr-nl,x-nl)%mod;
    op = op*dfs(ls[x],nl,x-1)%mod;
    op = op*dfs(rs[x],x+1,nr)%mod;
    //cout << x<< ' '<<nl << ' '<<nr << ' '<<op << endl;
    return op;
}

void print(int x){
    if(x == 0) return ;
    printf("%d %d %d\n",x,ls[x],rs[x]);
    print(ls[x]);
    print(rs[x]);
}

int main(){
    init();
    IO::begin();
    int kase = 1;
    //cout <<comb(3,3)<<endl;
    while(IO::read(n)){
        for(int i = 1;i <= n;i ++) IO::read(l[i]);
        for(int i = 1;i <= n;i ++) IO::read(r[i]);
        build();
        //print(root);
        //cout<<"!!!!!" << endl;
        long long ans = dfs(root,1,n);
        printf("Case #%d: %lld\n",kase++,ans);
    }

    return 0;
}

hdu 6305
题意:给出一个数组a,b数组的元素是0到1任意的,问a,b笛卡尔树同构的方案的期望。
根据a数组构造笛卡尔树,可以知道同构的概率是所有节点的儿子树乘积的倒数,可以发现对于任意一种序列,b数组为这个序列的期望是一样的,因为总期望是n/2,所以同构的期望是上面那个概率乘以n/2,注意会爆栈。

#include<bits/stdc++.h>
using namespace std;
const int N = 1e6+100;
int num[N];
int ls[N],rs[N],root;
int que[N];
int son[N];
int inv[N];
int ss[N];
int fa[N];
int n;
const int mod = 1e9+7;
bool judge(int x,int y){
    if(num[x] == num[y]) return x > y;
    return num[x] < num[y];
}

void build(){
    int tp = 0;
    root = 0;
    for(int i = 1;i <= n;i ++){
        ls[i] = rs[i] = 0;
        while(tp && judge(que[tp],i)) tp --;
        //cout << que[tp] << ' ' << i << ' '<<root << endl;
        if(tp == 0){
            ls[i] = root;
            root = i;
        }
        else{
            ls[i] = rs[que[tp]];
            rs[que[tp]] = i;
        }
        que[++tp] = i;
    }
}

/*void dfs(int x){
    //cout <<x << ' ' << ls[x] << ' ' <<rs[x] << endl;
    son[x] = 0;
    if(x == 0) return ;
    son[x] = 1;
    dfs(ls[x]);
    dfs(rs[x]);
    son[x] += son[ls[x]];
    son[x] += son[rs[x]];
}*/
void init(){
    inv[1] = 1;
    for(int i = 2;i < N;i ++){
        inv[i] = (mod-mod/i)*1LL*inv[mod%i]%mod;
    }
}

int main(){
    int T;
    init();
    cin >> T;
    while(T--){
        scanf("%d",&n);
        for(int i = 1;i <= n;i ++) scanf("%d",&num[i]);
        //for(int i=  1;i <= n;i ++) num[i] = i;
        build();
        //memset(ss,0,sizeof(ss));
        for(int i = 1;i <= n;i ++){
            ss[i] = 0;
            if(ls[i]) ss[i]++,fa[ls[i]] = i;
            if(rs[i]) ss[i]++,fa[rs[i]] = i;
        }
        fa[root] = 0;
        queue<int> que;
        for(int i = 1;i <= n;i ++){
            son[i] =1;
            if(ss[i] == 0) que.push(i);
        }
        while(!que.empty()){
            int now = que.front();
        //cout <<now << ' '<<fa[now] << ' '<<son[now] << endl;
            que.pop();
            if(fa[now] == 0) continue;
            ss[fa[now]] --;
            son[fa[now]] += son[now];
            if(ss[fa[now]] == 0) que.push(fa[now]);
        }
        //cout<<"!!" << endl;
        long long ret = n;
        for(int i = 1;i <= n;i ++){
            //cout <<i << ' '<<son[i] << ' '<< inv[son[i]] << endl;
            ret= ret*inv[son[i]]%mod;
        }
        ret = ret*inv[2]%mod;
        printf("%lld\n",ret);
    }


    return 0;
}

猜你喜欢

转载自blog.csdn.net/zstu_zy/article/details/81908147
hdu
今日推荐