zoukankan      html  css  js  c++  java
  • P5072 [Ynoi2015] 盼君勿忘

    题目

    P5072 [Ynoi2015] 盼君勿忘

    分析

    莫队。

    首先这道题有区间限制,数据范围也很明显,可以考虑离线莫队。

    然后我们发现可以对于每一个数来算贡献,但是不好直接计算其出现次数,考虑转化。

    发现其实可以转化成 (2^{r-l+1}-2^{r-l-k+1}) 次(全局的减掉不出现的,剩下的就是出现的),其中 (k) 是这个数的出现次数。

    那么我们想一下怎么维护。

    似乎每次直接暴力遍历所有出现过的数并不好,因为这样复杂度直接爆炸。

    但是我们如果对每一个出现次数的所有数的和开一个数组,这样其实也有 (O(n)) 级别个数每次。

    于是考虑平衡,因为这里涉及到了“划分”,也就是说,我们一共有 (O(n)) 级别个“单次出现”,现在这些元素被“划分”到了 (n) 个数里面去(就是把次数分配给了数)

    那么如果我们根号平衡,发现就很好做了。

    对于出现次数 ( esqrt{n}) 的数,这样的数不超过 (sqrt{n}) 个,我们可以直接拿一个 (unordered)_(map) 来存这样的数(也可以链表维护)。

    对于剩下的数,我们发现相当于其值域就在 (sqrt{n}) 以内,我们可以直接拿数组 (Cnt_i) 来存:当前出现 (i) 次的所有数的和。

    然后查询的时候直接遍历第一个的每一个数,和第二个的每一个出现次数即可。

    然后注意这里的求幂次方不能直接快速幂,也不能固定模数,于是可以考虑 (O(sqrt{n})) 单次预处理,(O(1)) 回答的光速幂。

    代码

    #include<bits/stdc++.h>
    using namespace std;
    template <typename T>
    inline void read(T &x){
    	x=0;bool f=false;char ch=getchar();
    	while(!isdigit(ch)) f|=ch=='-',ch=getchar();
    	while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    	x=f?-x:x;
    	return ;
    }
    template <typename T>
    inline void write(T x){
    	if(x<0) putchar('-'),x=-x;
    	if(x>9) write(x/10);
    	putchar(x%10^48);
    	return ;
    }
    const int N=1e5+5;
    #define ll long long 
    int n,m,a[N],bl[N],t;
    struct Query{
    	int l,r,mod,id;
    	inline bool operator < (const Query &B)const{return (bl[l]^bl[B.l])?(l<B.l):((bl[l]&1)?r<B.r:r>B.r);}
    	Query(int l=0,int r=0,int mod=0,int id=0):l(l),r(r),mod(mod),id(id){}
    }Q[N];
    unordered_set<int> S;
    int cnt[N],Cnt[N];
    ll Ans[N];
    inline void Add(int x){
    	cnt[x]++;
    	if(cnt[x]==t) Cnt[cnt[x]-1]-=x,S.insert(x);
    	if(cnt[x]<t) Cnt[cnt[x]-1]-=x,Cnt[cnt[x]]+=x; 
    	return ;
    }
    inline void Del(int x){
    	cnt[x]--;
    	if(cnt[x]==t-1) Cnt[cnt[x]]+=x,S.erase(x);
    	if(cnt[x]<t-1) Cnt[cnt[x]+1]-=x,Cnt[cnt[x]]+=x;
    	return ; 
    }
    #define Inc(x,y,mod) (x+y>=mod?x+y-mod:x+y)
    #define Dec(x,y,mod) (x-y<0?x-y+mod:x-y)
    ll Pow[N],POW[N];
    #define LogPow(x,mod) (POW[(int)floor((x)/t)]*Pow[x-(int)floor((x)/t)*t]%mod)
    signed main(){
    	read(n),read(m);t=sqrt(n)+1;
    	const int block=n/sqrt(m);
    	for(int i=1;i<=n;i++) read(a[i]),bl[i]=(i-1)/block+1;
    	for(int i=1;i<=m;i++){
    		int l,r,mod;
    		read(l),read(r),read(mod);
    		Q[i]=Query(l,r,mod,i);
    	}
    	sort(Q+1,Q+m+1);
    	int l=1,r=0;
    	for(int i=1;i<=m;i++){
    		while(r<Q[i].r) Add(a[++r]);
    		while(l>Q[i].l) Add(a[--l]);
    		while(r>Q[i].r) Del(a[r--]);
    		while(l<Q[i].l) Del(a[l++]);
    		ll res=0;const ll mod=Q[i].mod;
    		Pow[0]=1;POW[0]=1;
    		for(int j=1;j<=t;j++) Pow[j]=Inc(Pow[j-1],Pow[j-1],mod);POW[1]=Pow[t];
    		for(int j=2;j<=t;j++) POW[j]=POW[j-1]*POW[1]%mod;
    		for(int j=1;j<t;j++) res=Inc(res,Dec(LogPow(r-l+1,mod),LogPow(r-l-j+1,mod),mod)*Cnt[j]%mod,mod);
    		unordered_set<int>::iterator it=S.begin();
    		for(;it!=S.end();it++) res=Inc(res,Dec(LogPow(r-l+1,mod),LogPow(r-l-cnt[*it]+1,mod),mod)*(*it)%mod,mod);
    		Ans[Q[i].id]=res;
    	}	
    	for(int i=1;i<=m;i++) write(Ans[i]),putchar('
    ');
    	return 0;
    }
    

    最开始把题给看错了...以为必须是连续子序列...

    光速幂的技巧值得学习,同时这里“全局”减掉“局部”的思想也非常巧妙。

  • 相关阅读:
    作业2(4)求m和n之间的和
    作业2(3)计算x的n次方
    作业3(6)查询水果的单价。有 4 种水果,苹果(apples)、梨(pears)、桔子(oranges)和葡萄(grapes),
    作业3(5)输入五级制成绩(A-E),输出相应的百分制成绩(0-100)区间,要求使用 switch语句。
    作业3(4)循环输入多个年份 year,判断该年是否为闰年。判断闰年的条件是:能被 4 整除但不能被100 整除,或者能被 400 整除。输入-1退出程序执行
    P39-习题2-5
    P39-习题2-7
    计算N户电费
    P39-习题2-4
    P39-习题2-3
  • 原文地址:https://www.cnblogs.com/Akmaey/p/14737496.html
Copyright © 2011-2022 走看看