人品计算

    题目大意:n个数的a序列,m组询问。每次询问给出T,A,B,K。求在a序列的[A,B]的位置之内的K小值P,的$C_{T}^{P \% T} \% 10111$

    注释:每组询问保证区间只相交,不包含。$1\le n \le 10^5$,$1\le m \le 10^4$。

      想法:卧槽?啥题啊??!get一波新知识点:权值线段树。

        权值线段树,就是在序列的桶里建线段树,维护balabala。修改就是在原序列上修改,等价在桶上进行修改,然后操作和线段树几乎相同,没啥区别。可以支持一些线段树并不能完成的操作:查询全局k最值等。特别地,我们并不用单独开一个桶的数组,只需要在权值线段树的底层修改、维护即可。

      关于这道题,由于题目中说明了区间和区间之间只有相交,没有包含,这就等价于离线,按区间左端点排序后右端点也是递增的,这样我们就可以将两个区间相交的地方保留,左边的全都将桶内元素-1,右边的+1,然后套版子查询k小值即可。

    最后,附上丑陋的代码... ...

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define lson pos<<1
#define rson pos<<1|1
#define mod 10111 
#define N 100010 
using namespace std;
int before[N/100+1],change[N/100+1];
int s[N<<2],a[N];
int ans[N/10];
struct Node
{
	int t,l,r,k;
	int id;
	Node(){t=id=l=r=k=0;}
}f[N];
bool cmp(Node a,Node b)
{
	return a.l<b.l;
}
void fix(int pos,int l,int r,int x,int y)//在区间l到r中将x+y
{
	int mid=(l+r)>>1;
		s[pos]+=y;
	if(l==r)
	{
		return;
	}
	if(x<=mid) fix(lson,l,mid,x,y);
	else fix(rson,mid+1,r,x,y);
	// s[pos]=s[lson]+s[rson];
}
int find(int pos,int l,int r,int k)//查询l到r之内的k小值
{
	int mid=(l+r)>>1;
	if(l==r) return r;
	if(k<=s[lson]) return find(lson,l,mid,k);
	else return find(rson,mid+1,r,k-s[lson]);
}
int quick_power(int x,int y,int z)//快速幂
{
	int ans=1;
	x%=z;
	while(y)
	{
		if(y&1) ans=(ans*x)%z;
		y>>=1;
		x=(x*x)%z;
	}
	return ans;
}
int main()
{
	freopen("rp.in","r",stdin);
	freopen("rp.out","w",stdout);
	int n,m;
	cin >> n >> m;
	before[1]=before[0]=change[0]=change[1]=1;
	for(int i=2;i<=1000;i++)
	{
		before[i]=before[i-1]*i%mod;
		change[i]=quick_power(before[i],mod-2,mod);
	}
	// cout << before[6] << endl ;
	// cout << before[66]*change[66]%mod << endl ;
	int minn=0x7f7f7f7f,maxn=0;//minn和maxn分别是整个线段树基层桶的下界和上界
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		minn=min(minn,a[i]);
		maxn=max(maxn,a[i]);
	}
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d%d%d",&f[i].t,&f[i].l,&f[i].r,&f[i].k);
		f[i].id=i;
	}
	sort(f+1,f+m+1,cmp);
	// for(int i=0;i<=m;i++)
	// {
	// 	cout << f[i].l << " " << f[i].r << endl ;
	// }
	for(int i=1;i<=m;i++)
	{
		// if(f[i-1].r<f[i].l)
		// {
		if(i!=1)
		for(int j=f[i-1].l;j<=(f[i].l>f[i-1].r?f[i-1].r:f[i].l-1);j++)//注意边界
		{
			fix(1,minn,maxn,a[j],-1);
			// cout << "Tiao del : " << j << endl ;
		}
		for(int j=(f[i].l>f[i-1].r?f[i].l:f[i-1].r+1);j<=f[i].r;j++)//注意边界
		{
			fix(1,minn,maxn,a[j],1);
			// cout << "Tiao add : " << j << endl ;
		}
		// }
		// else
		// {
		// }
		int middle=find(1,minn,maxn,f[i].k)%f[i].t;
		// cout << middle << endl ;
		ans[f[i].id]=before[f[i].t]%mod*change[(f[i].t-middle)]%mod*change[middle]%mod;
	}
	for(int i=1;i<=m;i++)
	{
		printf("%d\n",ans[i]);
	}
	return 0;
}

    小结:错误:1.离散排序处理答案之后需要将它们按照读入的顺序输出!!!

          2.before函数记录的是阶乘,然后由于N是100010,所以N/10恰好为1000,导致before[1000]这个值没有取到,直接Gg