• [jdoj1258]野生动物园(change by panxf)_权值线段树_组合数


    人品计算

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

        注释:每组询问保证区间只相交,不包含。$1le n le 10^5$,$1le 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
    ",ans[i]);
    	}
    	return 0;
    }
    

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

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

  • 相关阅读:
    Mysql 三大特性详解
    MySQL Innodb日志机制深入分析
    Bootstrap学习地址
    Java【锁】
    Java【tomcat】配置文件
    nginx配置文件详解【nginx.conf】
    Sqlserver2008[索引]
    网络知识
    RestClient火狐接口测试地址
    java基础1JDK各大版本下载地址
  • 原文地址:https://www.cnblogs.com/ShuraK/p/8893376.html
Copyright © 2020-2023  润新知