[JSOI2011]解题报告

[JSOI2011]分特产

一道组合数学+容斥的题目,考虑如何处理掉每个人至少一个这个限制,这时就要容斥一下有多少人没有分到即可,没分到的人数设为\(i\)

每种特产分开算,便转化成了\(n-i-1\)块板子插入\(a[i]\)个中,方案数为\(C(n-i-a[i]-1,n-i-1)\)

那么总的式子便为\(\sum_{i=0}^{n-1}(-1)^iC(n,i)\prod_{j=1}^mC(n-i+a[i]-1,n-i-1)\)

#include
#define int long long
using namespace std;
const int mod=1e9+7;
int jie[10003],cnt=-1,n,m,a[100003],cn,ans;
int poww(int x,int p)
{
    if(p==0)
        return 1;
    int tmp=poww(x,p/2);
    tmp=(tmp*tmp)%mod;
    if(p%2==1)
        tmp=(tmp*x)%mod;
    return tmp;
}
int c(int nn,int mm)
{
    return jie[nn]*poww(jie[mm],mod-2)%mod*poww(jie[nn-mm],mod-2)%mod;
}
signed main()
{
    jie[0]=1;
    for(int i=1; i<=10000; i++)
        jie[i]=jie[i-1]*i%mod;
    scanf("%lld%lld",&n,&m);
    for(int i=1;i<=m;i++)
        scanf("%lld",&a[i]);
    for(int i=0;i

[JSOI2011]棒棒糖

这道题就是用主席树来维护这一个序列的权值,每次询问从\([1,max{c[i]}]\)开始,每次递归\([l,mid]\)\([mid,r]\)查找在这个权值区间内的元素数量,如果数量乘2大于询问区间长度,就在这个权值区间内递归,知道找到一个符合要求的数或者找不到,然后剩下的就是主席树的基本操作

#include
using namespace std;
struct node
{
    int l1,r1,sum;
}tr[5000003];
int n,m,s,t,a[50003],gen[50003],summ,su,ans[50003],mp[50003];
inline int rd()
{
    int x=0,p=1;
    char a=getchar();
    while((a<48||a>57)&&a!='-')
        a=getchar();
    if(a=='-')
        p=-p,a=getchar();
    while(a>47&&a<58)
        x=(x<<1)+(x<<3)+(a&15),a=getchar();
    return x*p;
}
int nbuild(int root)
{
    summ++,tr[summ]=tr[root];
    return summ;
}
int build(int l,int r)
{
    summ++;
    int root=summ,mid=(l+r)/2;
    if(l==r)
    {
        tr[root].sum=0;
        return root;
    }
    tr[root].l1=build(l,mid),tr[root].sum=0,tr[root].r1=build(mid+1,r);
    return root;
}
int add(int root,int l,int r,int x)
{
    root=nbuild(root);
    if(l==r)
    {
        tr[root].sum++;
        return root;
    }
    int mid=(l+r)/2;
    if(mid>=x)
        tr[root].l1=add(tr[root].l1,l,mid,x);
    else
        tr[root].r1=add(tr[root].r1,mid+1,r,x);
    tr[root].sum=tr[tr[root].l1].sum+tr[tr[root].r1].sum;
    return root;
}
int ask(int root1,int root2,int l,int r,int x)
{
    if(l==r)
        return l;
    int mid=(l+r)/2;
    if(2*(tr[tr[root2].l1].sum-tr[tr[root1].l1].sum)>x)
        return ask(tr[root1].l1,tr[root2].l1,l,mid,x);
    else if(2*(tr[tr[root2].r1].sum-tr[tr[root1].r1].sum)>x)
        return ask(tr[root1].r1,tr[root2].r1,mid+1,r,x);
    return 0;
}
int main()
{
    n=rd(),m=rd();
    for(int i=1;i<=n;i++)
    {
        a[i]=rd();
        if(mp[a[i]]==0)
            su++,mp[a[i]]=su,ans[su]=a[i];
        a[i]=mp[a[i]];
    }
    gen[0]=build(1,su);
    for(int i=1;i<=n;i++)
        gen[i]=add(gen[i-1],1,su,a[i]);
    for(int i=1;i<=m;i++)
    {
        s=rd(),t=rd();
        printf("%d\n",ans[ask(gen[s-1],gen[t],1,su,t-s+1)]);
    }
    return 0;
}

你可能感兴趣的:([JSOI2011]解题报告)