人品计算
题目大意: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