Atcoder CODE FESTIVAL 2016 qual C 的E题 Encyclopedia of Permutations

题意:

对于一个长度为n的排列P,如果P在所有长度为n的排列中,按照字典序排列后,在第s位,则P的value为s

现在给出一个长度为n的排列P,P有一些位置确定了,另外一些位置为0,表示不确定。

现在问,P的所有可能的排列的value之和

n <= 500000

思路:

对于一个可能的排列,它的value为所有小于它的排列的个数 + 1反过来,对于一个排列a,如果P的可能的排列中有sum个排列大于a,则a对答案的贡献为sum

那我们就可以枚举位数,

一位一位的考虑:

对于2个排列P,b,我们假设它在第i位分出大小,即[1,i-1]的位置2个排列相同,并且第i位有P[i]  > b[i]

并且P就是满足条件的可能的排列,那我们算出这个时候有x个可能的P,y个可能的b,则对答案的贡献

为x * y

为了方便,我们从后面往前面枚举

那对于位数i,我们只需要分2 * 2种情况考虑:

自由的数表示没有被固定位置的数

suf表示i后面有多少个没有被确定的位置

sum表示P一共有多少个没有被确定的位置

1. Pi的位置确定了

1.1 b在i处放的数为[1,P[i]-1]中被固定在[i+1,n]中某一位的数,设有x个,则贡献:

   x * sum! * (n - i)!

  求x的话用一个树状数组bit记录就可以了,遇见固定的数就扔进bit里面

1.2   b在i处放的数为[1,P[i]-1]中自由的数,设有y个,则贡献:

  y * suf * (sum - 1)! * (n - i)!

  提前把所有自由的数放到一个树状数组bit2中,就可以快速得到y了

2 P[i]处的数没有确定

2.1 b在i处放的数也是自由的数,则贡献:

  C(sum,2) * (sum - 2)! * suf * (n - i)!

2.2 b在i处放的数是[1,P[i]-1]中被固定在i后面某一位的数

  假设P[i]放的是x,则b[i]放的应该是[1,x-1]中被固定在i后面的数,设[1,x-1]中被固定在i后面的数

  有y个,则贡献:

  y * (sum - 1)! * (n - i)!

  所以这部分的贡献需要我们枚举x,对于每一个x求有多少个y?

  不用,用线段树可以维护,总贡献为:

  seg[1].s * (sum - 1)! * (n - i)!

所以这道题目就搞定了,接下来说说线段树维护的是什么

Seg{int n;int ly;LL s}

线段树只需要维护自由的那一些数,因为枚举的x是自由的数

对于叶子节点i

s表示表示目前被固定的数中,比i小的有多少个

n表示这个叶子节点是不是自由的数

则对于所有节点:

n表示这个区间有多少个自由的数

s表示这个区间的自由的数的s之和

代码:

                                            
  //File Name: E.cpp
  //Author: long
  //Mail: 736726758@qq.com
  //Created Time: 2016年10月26日 星期三 10时29分59秒
                                   
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <iostream>
#include <map>
#include <set>
#include <math.h>
#include <vector>
#define LL long long
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
using namespace std;
const int MAXN  = 500000 + 5;
const int MOD = (int)1e9 + 7;
int p[MAXN],bit1[MAXN],bit2[MAXN],n;
bool use[MAXN];
LL jie[MAXN];
void update(int x,int add,int *bit){
    for(int i=x;i<=n;i+=i&-i)
        bit[i] += add;
}
int query(int x,int *bit){
    int res = 0;
    for(int i=x;i>0;i-=i&-i)
        res += bit[i];
    return res;
}
struct Seg{
    int n,ly;
    LL s;
}seg[MAXN << 2];
void pushup(int rt){
    seg[rt].s = seg[rt<<1].s + seg[rt<<1|1].s;
    seg[rt].n = seg[rt<<1].n + seg[rt<<1|1].n;
}
void pushdown(int rt){
    if(seg[rt].ly){
        int &ly = seg[rt].ly;
        int L = rt<<1,R = rt<<1|1;
        seg[L].ly += ly,seg[R].ly += ly;
        seg[L].s += (LL)seg[L].n * ly;
        seg[R].s += (LL)seg[R].n * ly;
        ly = 0;
    }
}
void build(int l,int r,int rt){
    seg[rt].s = seg[rt].ly = seg[rt].n = 0;
    if(l == r){
        if(!use[l]) seg[rt].n = 1;
        return ;
    }
    int m = l + r >> 1;
    build(lson);
    build(rson);
    pushup(rt);
}
void update(int L,int R,int add,int l,int r,int rt){
    if(L <= l && R >= r){
        seg[rt].ly += add;
        seg[rt].s += (LL)add * seg[rt].n;
        return ;
    }
    pushdown(rt);
    int m = l + r >> 1;
    if(L <= m) update(L,R,add,lson);
    if(R > m) update(L,R,add,rson);
    pushup(rt);
}
int init(){
    build(1,n,1);
    jie[0] = 1;
    for(int i=1;i<=n;i++)
        jie[i] = jie[i-1] * i % MOD;
    int sum = 0;
    for(int i=1;i<=n;i++){
        if(!use[i]){
            update(i,1,bit2);
            sum++;
        }
    }
    return sum;
}
LL solve(){
    int sum = init();
    LL ans = 0,tmp1,tmp2;
    int suf = 0;
    for(int i=n;i>0;i--){
        tmp1 = tmp2 = 0;
//        ans = 0;
        if(p[i]){
            int x = query(p[i] - 1,bit1);
            tmp1 = x * jie[sum] % MOD * jie[n - i] % MOD;
            int y = query(p[i],bit2);
            if(sum >= 1)
                tmp2 = (LL)y*suf % MOD * jie[sum-1] % MOD * jie[n-i] % MOD;
            ans = (ans + tmp1 + tmp2) % MOD;
            update(p[i],1,bit1);
            if(p[i] < n) update(p[i]+1,n,1,1,n,1);
        }
        else{
            if(sum >= 2)
                tmp1 = ((LL)sum * (sum - 1) / 2) % MOD * jie[sum-2] % MOD * suf % MOD * jie[n-i] % MOD;
            if(sum >= 1)
                tmp2 = seg[1].s % MOD * jie[sum-1] % MOD * jie[n-i] % MOD;
//            printf("tmp1 = %lld
tmp2 = %lld
",tmp1,tmp2);
            ans = (ans + tmp1 + tmp2) % MOD;
            suf++;
        }
//        printf("i = %d ans = %lld
",i,ans);
    }
//    cout << jie[sum] << endl;
    ans = (ans + jie[sum]) % MOD;
    return ans;
}
int main(){
    scanf("%d",&n);
    memset(use,false,sizeof(use));
    for(int i=1;i<=n;i++){
        scanf("%d",p + i);
        use[p[i]] = true;
    }
    printf("%d
",(int)solve());
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/-maybe/p/6000257.html