1. 程式人生 > >【ZOJ3899】State Reversing 解題報告

【ZOJ3899】State Reversing 解題報告

【ZOJ3899】State Reversing

Description

\(N\)不同的怪獸,編號從\(1\)\(N\)。Yukari有\(M\)相同的房間,編號為\(1\)\(M\)。每個房間是無限大的,每個房間有兩種狀態:可以住或者不可以住(一開始所有的房間都可以住)。Yukari每天都會選定一個區間 \([l,r]\),把編號在這個區間的房間狀態反轉(可以住改成不可以住,不可以住改成可以住)。怪獸只能住在“可以住”的房間。每間“可以住”的房間至少有一個怪獸。

Yukari想知道,每次反轉後,有多少種方法為這些怪獸分配房間。兩種方法(方法\(A\)和方法\(B\))認為是同種方法當且僅當對於方案\(A\)

中任意一個房間\(x\),在方案\(B\)中存在一個房間\(y\)使得房間\(x\)和房間\(y\)住的怪獸的編號集合是一樣的。換句話說,房間是相同的。

Input

第一行一個整數 \(T(T≤5)\) ,表示資料組數。

接下來三個整數 \(N,M,Q(1≤M≤N≤10^5,Q≤10^5)\) ,分別表示怪獸數、房間數、操作次數。

接下來\(Q\)行,每行兩個整數 \(l,r(1≤l≤r≤M)\) ,表示Yukari會反轉區間 \([l,r]\) 的狀態。

方案數對\(880803841\)取模

Output

對於每次操作,輸出進行反轉操作後為怪獸分配房間的方案數。


\(n\)個有標號的球求放到\(m\)

個無標號的非空盒子裡,發現就是第二類斯特林數,因為\(m\)會變,所以等價於求斯特林行。

先考慮求出\(n\)個有標號的球出放到\(m\)個有標號的非空盒子裡,即為\({n \brace m}m!\)


\[ f_i={n \brace i}i! \]
考慮進行容斥,設\(g_i\)代表\(n\)個有標號的球放到\(i\)個有標號的盒子裡的方案數,則有
\[ g_i=i^n \]
顯然有
\[ g_n=\sum_{i=0}^n\binom{n}{i}f_i \]
二項式反演
\[ \begin{aligned} f_n&=\sum_{i=0}^n(-1)^i\binom{n}{i}g_{n-i}\\ &=\sum_{i=0}^n(-1)^i\frac{n!}{(n-i)!i!}(n-i)^n\\ &=n!\sum_{i=0}^n\frac{(-1)^i}{i!}\frac{(n-i)^n}{(n-i)!} \end{aligned} \]


所以
\[ {n\brace k}=\sum_{i=0}^k\frac{(-1)^i}{i!}\frac{(k-i)^n}{(n-i)!} \]
然後卷一下子就行了,維護用個線段樹就好


Code:

#include <cstdio>
#include <algorithm>
const int mod=880803841,G=26,Gi=711418487;
const int N=1e5+10;
#define add(a,b) ((a+b)%mod)
#define mul(a,b) (1ll*(a)*(b)%mod)
int qp(int d,int k){int f=1;while(k){if(k&1)f=mul(f,d);d=mul(d,d),k>>=1;}return f;}
int n,m,q,a[N<<3],b[N<<3],turn[N<<3],fac[N<<3],len,L;
void NTT(int *a,int typ)
{
    for(int i=1;i<len;i++) if(i<turn[i]) std::swap(a[i],a[turn[i]]);
    for(int le=1;le<len;le<<=1)
    {
        int wn=qp(typ?G:Gi,(mod-1)/(le<<1));
        for(int p=0;p<len;p+=le<<1)
        {
            int w=1;
            for(int i=p;i<p+le;i++,w=mul(w,wn))
            {
                int tx=a[i],ty=mul(w,a[i+le]);
                a[i]=add(tx,ty);
                a[i+le]=add(tx,mod-ty);
            }
        }
    }
    if(!typ)
    {
        int inv=qp(len,mod-2);
        for(int i=0;i<len;i++) a[i]=mul(a[i],inv);
    }
}
int sum[N<<2],tag[N<<2];
#define ls id<<1
#define rs id<<1|1
void build(int id,int l,int r)
{
    tag[id]=0,sum[id]=r+1-l;
    if(l==r) return;
    int mid=l+r>>1;
    build(ls,l,mid),build(rs,mid+1,r);
}
void pushdown(int id,int l,int r)
{
    if(tag[id])
    {
        int mid=l+r>>1;
        sum[ls]=mid+1-l-sum[ls];
        sum[rs]=r-mid-sum[rs];
        tag[ls]^=1,tag[rs]^=1,tag[id]^=1;
    }
}
void change(int id,int L,int R,int l,int r)
{
    if(L==l&&R==r)
    {
        sum[id]=R+1-L-sum[id];
        tag[id]^=1;
        return;
    }
    pushdown(id,L,R);
    int Mid=L+R>>1;
    if(r<=Mid) change(ls,L,Mid,l,r);
    else if(l>Mid) change(rs,Mid+1,R,l,r);
    else change(ls,L,Mid,l,Mid),change(rs,Mid+1,R,Mid+1,r);
    sum[id]=sum[ls]+sum[rs];
}
int main()
{
    int T;scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d%d",&m,&n,&q);
        len=1,L=-1;
        while(len<=m<<1) len<<=1,++L;
        for(int i=0;i<len;i++) a[i]=b[i]=0,turn[i]=turn[i>>1]>>1|(i&1)<<L;
        fac[0]=1;for(int i=1;i<=m;i++) fac[i]=mul(fac[i-1],i);
        a[m]=b[m]=qp(fac[m],mod-2);
        for(int i=m-1;~i;i--) b[i]=a[i]=mul(a[i+1],i+1);
        for(int i=1;i<=m;i+=2) a[i]=mod-a[i];
        for(int i=0;i<=m;i++) b[i]=mul(b[i],qp(i,m));
        NTT(a,1),NTT(b,1);
        for(int i=0;i<len;i++) a[i]=mul(a[i],b[i]);
        NTT(a,0);
        build(1,1,n);
        for(int l,r,i=1;i<=q;i++)
        {
            scanf("%d%d",&l,&r);
            change(1,1,n,l,r);
            printf("%d\n",a[sum[1]]);
        }
    }
    return 0;
}

2018.12.22