[随机算法+Hash] Codeforces 799F Round #413 F. Beautiful fountains rows

题目梗概

n 个不同的数字,每个数字只出现在[L,R]中。
求所有满足要求的区间的长度和。
要求:出现在该区间的所有数字的个数必须为奇数,且必须有数字存在。

解题思路

考虑一个区间如何才能满足要求。
将每个数字赋上一个 (2631,0] 的随机值。
这个区间所有数字的异或值再异或上这个区间出现过的数字,如果等于0,那么这个区间满足要求。

对于区间所有数字的异或值构造前缀异或和数组 P[] 即可。
考虑 [L,R] 区间出现过的数字的异或值。

一个数字 i [L,R]出现过必须满足

!(y[i]<L||x[i]>R)=(y[i]>=Landx[i]<=R)
,满足条件的所有数字就是满足 x[i]<=R 的数字 XOR 满足 (y[i]<Landx[i]<=R) 的数字。

观察 (y[i]<Landx[i]<=R) ,发现如果前一项满足后一项显然也满足,所以这个条件变为 y[i]<L

我们分别开数组 s[],t[] 分别构造满足 x[i]<=Ry[i]<L 的异或和,数组的构造显然是线性的。
通过以上变换题目初始条件变为满足 P[R] ^ P[L1]==s[R] ^ t[L]
移项得 t[L] ^ P[L1]==s[R] ^ P[R]

把前项压进一个map里就可以了。

Ps:本人脸丑,不得不双hash。

#include<map>
#include<vector>
#include<cstdio>
#include<algorithm>
#define LL long long
using namespace std;
typedef pair<LL,LL> jz;
const int maxn=200005;
int n,m;
inline int _read(){
    int num=0;char ch=getchar();
    while (ch<'0'||ch>'9') ch=getchar();
    while (ch>='0'&&ch<='9') num=num*10+ch-48,ch=getchar();
    return num;
}
map<jz,LL> G,S;
vector<int> L[maxn],R[maxn];
LL a[maxn],b[maxn],p1[maxn],p2[maxn],ans;
int x[maxn],y[maxn],num[maxn];
LL calc(LL x){return (x+1)*x*(x+1)/2-x*(x+1)*(2*x+1)/6;}
int main(){
    n=_read();m=_read();
    for (int i=1;i<=n;i++){
        x[i]=_read(),y[i]=_read();
        L[x[i]].push_back(i);R[y[i]].push_back(i);
        a[i]=(LL)rand()*rand()*rand()*rand();
        b[i]=(LL)rand()*rand()*rand()*rand();
        p1[x[i]]^=a[i];p1[y[i]+1]^=a[i];
        p2[x[i]]^=b[i];p2[y[i]+1]^=b[i];
        num[x[i]]++;num[y[i]+1]--;
    }
    for (int k=0;k<2;k++) for (int i=1;i<=m;i++) p1[i]^=p1[i-1],p2[i]^=p2[i-1];
    G[jz(0,0)]=1;
    LL s1=0,s2=0,t1=0,t2=0;
    for (int i=1;i<=m;i++){
        for (int j=0;j<L[i].size();j++) s1^=a[L[i][j]],s2^=b[L[i][j]];
        ans+=G[jz(s1^p1[i],s2^p2[i])]*i-S[jz(s1^p1[i],s2^p2[i])];
        for (int j=0;j<R[i].size();j++) t1^=a[R[i][j]],t2^=b[R[i][j]];
        G[jz(t1^p1[i],t2^p2[i])]++;S[jz(t1^p1[i],t2^p2[i])]+=i;
    }
    for (int i=1;i<=m;i++) num[i]+=num[i-1];
    int i=1,j;
    while(i<=m){
        j=i;
        while(j<=m&&num[j]==0) j++;
        ans-=calc(j-i);i=j+1;
    }
    printf("%lld\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值