[TCO2014]FrozenStandings

題目大意

有n個人,每個人有一個數字xi
現在你可以把某些人的xi 加一。
問一共能造成多少本質不同的排名?
兩個人中數字大的排名靠前,數字一樣編號小的靠前。

做法

雙關鍵字讓我們非常難受。
考慮設一個大數w,令ri=xiw+i ,然後令li=riw
那麼現在就是一個人可以選擇liri ,問排名數。
立刻變成了單關鍵字。
現在我們先按照li 排序,顯然ri 也會有序。
不妨考慮什麼情況兩種不同的選擇方案會使得排名相同。
因爲選擇方案不同,不妨假設一個人i ,在兩種方案中一個選了li 另一個選了ri
如果不存在任意一個人選擇的數字在這之間,顯然這個人的排名沒有改變。
意味着,如果我們認爲i 產生了矛盾,對於所有li<lj<rili<rj<ri 的這些j ,他們的選擇是固定的,才能使得自己不落在區間內。
因爲所有liri 間距相同,這樣的j 顯然是一個區間[Li,Ri]
現在就會有一個簡單的思路,枚舉最小的矛盾位置i ,那麼i 之間的都不產生矛盾,而i 之後的都無所謂。
這個簡單的思路,可以用更簡單的做法表達:
dpk 表示前k 個人的方案數,即我們只確定了前k 個人,要求沒有人能造成矛盾(一個人造成矛盾是確定的,需要他的L與R均登場)。
轉移相當簡單:
dpk=dpk12Ri=kdpLi1
這樣我們就解決了本題。

#include<cstdio> 
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const int maxn=500000+10,mo=1000000007;
class FrozenStandings{
    int X[maxn],L[maxn],R[maxn],dp[maxn];
    int h[maxn],go[maxn],nxt[maxn];
    ll lv[maxn],rv[maxn],w;
    int i,j,k,l,t,n,m,tot;
    public:
    void add(int x,int y){
        go[++tot]=y;
        nxt[tot]=h[x];
        h[x]=tot;
    }
    int countStandings(int N, int A, int seed){
        int x = seed;
        w=10000000;
        n=N;
        fo(i,1,n){
            x = (ll)x * 20142014 % 1000000007;
            X[i] = x % A;
            rv[i]=-(ll)X[i]*w+(ll)i;
        }
        sort(rv+1,rv+n+1);
        fo(i,1,n) lv[i]=rv[i]-(ll)w;
        j=k=1;
        fo(i,1,n){
            while (k<n&&lv[k+1]<=rv[i]) k++;
            while (rv[j]<lv[i]) j++;
            if (j<=k) add(k,j);
        }
        dp[0]=1;
        fo(i,1,n){
            dp[i]=(ll)dp[i-1]*2%mo;
            t=h[i];
            while (t){
                (dp[i]-=dp[go[t]-1])%=mo;
                t=nxt[t];
            }
        }
        (dp[n]+=mo)%=mo;
        return dp[n];
    }
};
發佈了836 篇原創文章 · 獲贊 317 · 訪問量 59萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章