[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<bits/stdc++.h>
#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<n;i++)
{
cnt*=-1,cn=1;
for(int j=1;j<=m;j++)
cn*=c(n-i+a[j]-1,n-i-1),cn%=mod;
ans+=cn*c(n,i)%mod*cnt%mod,ans=(ans+mod)%mod;
}
cout<<ans;
return 0;
}
[JSOI2011]棒棒糖
这道题就是用主席树来维护这一个序列的权值,每次询问从\([1,max{c[i]}]\)开始,每次递归\([l,mid]\)和\([mid,r]\)查找在这个权值区间内的元素数量,如果数量乘2大于询问区间长度,就在这个权值区间内递归,知道找到一个符合要求的数或者找不到,然后剩下的就是主席树的基本操作
#include<bits/stdc++.h>
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;
}