P5072 [Ynoi2015] 盼君勿忘

文章讨论了解决一个关于数对贡献的问题,利用双向链表存储出现次数,unordered_set存储特定次数的数,采用光速幂优化查询时间复杂度,实现区间内特定数出现次数的高效计算。
摘要由CSDN通过智能技术生成

如果有来世希望还能与你相遇

分析一下询问

考虑计算每个数对答案所作的贡献

假设存在一个x,它在[l,r]区间出现了k次,我们想计算它的贡献

正向考虑似乎很困难

反向考虑一下,我们知道子字串的数量,没有x的子字串的数量

因此贡献应该是(2^{r-l+1}-2^{r-l+1-k})*x

因此我们只需要维护每个数的贡献即可->普通莫队

出现的次数可以用c[N]计数,出现该次数的数可以用sum[N]记录,有几种k可以用双向链表记录或者set,(选择unordered_set常数比较小)

感觉完事了,pow(2,i)快速幂不就完了吗

分析一下

k最多有sqrt(n)个 如果用快速幂预处理的话时间复杂度是O(sqrt(n)*log(sn(sqrt(2e5))))

sn是等差求和 k=1,k=2,k=3...理论上来说能过?

但是这是ynoi的题

考虑用光速幂 预处理O(sqrt(n)),查询O(1)

a^{b}=a^{\left \lfloor b/sqrt(n) \right\rfloor*sqrt(n)}*a^{bmodsqrt(n)}

因此我们只需要处理a^{sqrt(n)*i}以及a^{i}即可

inline void init(int mod){//处理sqrt(n)
    t1[0]=1;
    for(int i=1;i<=mi;i++){
        t1[i]=t1[i-1]*2%mod;
    }
    t2[0]=1;
    for(int i=1;i<=mi;i++){
        t2[i]=t2[i-1]*t1[mi]%mod;
    }
}
inline ll cqmi(int x,int mod){//O(1)询问
    return t1[x%mi]*t2[x/mi]%mod;
}

完整代码 unordered_set

#include<iostream>
#include<algorithm>
#include<cmath>
#include<unordered_set>
#include<vector>
#define INF (1ll<<60)
using namespace std;
typedef long long ll;
namespace Lan {
    inline string sread() {
        string s=" ";char e=getchar();
        while(e==' '||e=='\n')e=getchar();
        while(e!=' '&&e!='\n')s+=e,e=getchar();
        return s;
    }
    inline void swrite(string s){
        for(char e:s)putchar(e);
        printf("\n");
    }
    inline ll read() {
        ll x=0,y=1;char c=getchar();
        while(!isdigit(c)){if(c=='-')y=-1;c=getchar();}
        while(isdigit(c)){x=(x<<3)+(x<<1)+(c^48);c=getchar();}
        return x*=y;
    }
    inline void write(ll x) {
        if(x<0){x=-x,putchar('-');}ll sta[35],top=0;
        do sta[top++]=x%10,x/=10;while(x);
        while(top)putchar(sta[--top]+'0');
    }
}using namespace Lan;
const int N=1e5+9;
const int B=1e3+9;
int a[N],c[N],sum[N];//数,数量,出现k次的和
int L[B],R[B],pos[N];
ll t1[N],t2[N];
ll ans[N];
int cur,mi;
unordered_set<int> st;
struct Q{
    int l,r,p,id;
    friend bool operator < (const Q &a,const Q &b){
        return pos[a.l]^pos[b.l]?pos[a.l]<pos[b.l]:pos[a.l]&1?a.r<b.r:a.r>b.r;
    }
}que[N];
inline void init(int mod){//处理sqrt(n)
    t1[0]=1;
    for(int i=1;i<=mi;i++){
        t1[i]=t1[i-1]*2%mod;
    }
    t2[0]=1;
    for(int i=1;i<=mi;i++){
        t2[i]=t2[i-1]*t1[mi]%mod;
    }
}
inline ll cqmi(int x,int mod){//O(1)询问
    return t1[x%mi]*t2[x/mi]%mod;
}
inline ll query(int l,int r,int p){
    ll res=0;
    for(auto &i : st){
        res+=sum[i]*(cqmi(r-l+1,p)-cqmi(r-l+1-i,p)+p)%p;
    }
    res%=p;
    return res;
}
inline void add(int pos){
    sum[c[a[pos]]]-=a[pos];
    if(!sum[c[a[pos]]]){
        st.erase(c[a[pos]]);
    }
    c[a[pos]]++;
    if(!sum[c[a[pos]]]){
        st.insert(c[a[pos]]);
    }
    sum[c[a[pos]]]+=a[pos];
}
inline void del(int pos){
    sum[c[a[pos]]]-=a[pos];
    if(!sum[c[a[pos]]]){
        st.erase(c[a[pos]]);
    }
    c[a[pos]]--;
    if(!sum[c[a[pos]]]){
        st.insert(c[a[pos]]);
    }
    sum[c[a[pos]]]+=a[pos]; 
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    int n,m;
    n=read();
    m=read();
    for(int i=1;i<=n;i++){
        a[i]=read();
    }
    for(int i=1;i<=m;i++){
        que[i].l=read(),que[i].r=read(),que[i].p=read();
        que[i].id=i;
    }
    mi=sqrt(n)+1;
    int blo=n/sqrt(m);
    int t=ceil(1.0*n/blo);
    for(int i=1;i<=t;i++){
        L[i]=(i-1)*blo+1;
        R[i]=i*blo;
    }
    R[t]=n;
    for(int i=1;i<=t;i++){
        for(int j=L[i];j<=R[i];j++){
            pos[j]=i;
        }
    }
    sort(que+1,que+1+m);
    int l=1,r=0;
    for(int i=1;i<=m;i++){
        while(que[i].l<l){
            add(--l);
        }
        while(que[i].l>l){
            del(l++);
        }
        while(que[i].r>r){
            add(++r);
        }
        while(que[i].r<r){
            del(r--);
        }
        init(que[i].p);
        ans[que[i].id]=query(que[i].l,que[i].r,que[i].p);
    }
    for(int i=1;i<=m;i++){
        cout<<ans[i]<<'\n';
    }
    return 0;
}

双向链表

#include<iostream>
#include<algorithm>
#include<cmath>
#include<vector>
#define INF (1ll<<60)
using namespace std;
typedef long long ll;
namespace Lan {
    inline string sread() {
        string s=" ";char e=getchar();
        while(e==' '||e=='\n')e=getchar();
        while(e!=' '&&e!='\n')s+=e,e=getchar();
        return s;
    }
    inline void swrite(string s){
        for(char e:s)putchar(e);
        printf("\n");
    }
    inline ll read() {
        ll x=0,y=1;char c=getchar();
        while(!isdigit(c)){if(c=='-')y=-1;c=getchar();}
        while(isdigit(c)){x=(x<<3)+(x<<1)+(c^48);c=getchar();}
        return x*=y;
    }
    inline void write(ll x) {
        if(x<0){x=-x,putchar('-');}ll sta[35],top=0;
        do sta[top++]=x%10,x/=10;while(x);
        while(top)putchar(sta[--top]+'0');
    }
}using namespace Lan;
const int N=1e6+9;
const int B=1e3+9;
int a[N],c[N],sum[N];//数,数量,出现k次的和
int L[B],R[B],pos[N];
int pre[N],nxt[N];//双向链表
ll t1[N],t2[N];
ll ans[N];
int t,cur,mi;
struct Q{
    int l,r,p,id;
    friend bool operator < (const Q &a,const Q &b){
        return pos[a.l]^pos[b.l]?pos[a.l]<pos[b.l]:pos[a.l]&1?a.r<b.r:a.r>b.r;
    }
}que[N];
inline void init(int mod){//处理sqrt(n)
    t1[0]=1;
    for(int i=1;i<=mi;i++){
        t1[i]=t1[i-1]*2%mod;
    }
    t2[0]=1;
    for(int i=1;i<=mi;i++){
    	t2[i]=t2[i-1]*t1[mi]%mod;
    }
}
inline ll cqmi(int x,int mod){//O(1)询问
    return (t1[x%mi]*t2[x/mi])%mod;
}
inline void insert(int k){
    nxt[cur]=k;//cur的后面是k
    pre[k]=cur;//k的前面是cur
    cur=k;//现在最后一个是k
}
inline void erase(int k){
    if(cur==k){//在最后一个直接删
        nxt[pre[k]]=0;
        cur=pre[k];	
    }else{
        nxt[pre[k]]=nxt[k];//中间断开
        pre[nxt[k]]=pre[k];
    }
    pre[k]=0;
    nxt[k]=0;
}
inline ll query(int l,int r,int p){
    ll res=0;
    for(int i=nxt[0];i;i=nxt[i]){
        res+=sum[i]*(cqmi(r-l+1,p)-cqmi(r-l+1-i,p)+p)%p;
    }
    res%=p;
    return res;
}
inline void add(int pos){
    sum[c[a[pos]]]-=a[pos];
    if(!sum[c[a[pos]]]){
        erase(c[a[pos]]);
    }
    c[a[pos]]++;
    if(!sum[c[a[pos]]]){
        insert(c[a[pos]]);
    }
    sum[c[a[pos]]]+=a[pos];
}
inline void del(int pos){
    sum[c[a[pos]]]-=a[pos];
    if(!sum[c[a[pos]]]){
        erase(c[a[pos]]);
    }
    c[a[pos]]--;
    if(!sum[c[a[pos]]]){
        insert(c[a[pos]]);
    }
    sum[c[a[pos]]]+=a[pos]; 
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    int n,m;
    n=read();
    m=read();
    for(int i=1;i<=n;i++){
        a[i]=read();
    }
    for(int i=1;i<=m;i++){
        que[i].l=read(),que[i].r=read(),que[i].p=read();
        que[i].id=i;
    }
    mi=sqrt(n)+1;
    int blo=n/sqrt(m);
    t=ceil(1.0*n/blo);
    for(int i=1;i<=t;i++){
        L[i]=(i-1)*blo+1;
        R[i]=i*blo;
    }
    R[t]=n;
    for(int i=1;i<=t;i++){
        for(int j=L[i];j<=R[i];j++){
            pos[j]=i;
        }
    }
    sort(que+1,que+1+m);
    int l=1,r=0;
    for(int i=1;i<=m;i++){
        while(que[i].l<l){
            add(--l);
        }
        while(que[i].l>l){
            del(l++);
        }
        while(que[i].r>r){
            add(++r);
        }
        while(que[i].r<r){
            del(r--);
        }
        init(que[i].p);
        ans[que[i].id]=query(que[i].l,que[i].r,que[i].p);
    }
    for(int i=1;i<=m;i++){
        cout<<ans[i]<<'\n';
    }
    return 0;
}

学了一下双向链表,unordered_set, 光速幂感觉比做思维题有收获

我是数据结构魔怔人

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值