D - Online games【离散化差分】

题目链接:https://atcoder.jp/contests/abc221/tasks/abc221_d

题目大意:

给定 nnn 个区间 Ai,BiA_i,B_iAi,Bi ,输出 nnn 个数,分别代表被 12...,n1,2,...,n12...,n 段区间覆盖的点有多少个。

1n2×105,1Ai,Bi1091 \le n \le 2\times 10^5,1 \le A_i,B_i \le 10^91n2×105,1Ai,Bi109

eg:共3个区间 [1,2],[2,4],[3,3][1,2],[2,4],[3,3][1,2],[2,4],[3,3]

每个点被覆盖次数如下:

1 2 3 4
1 2 2 1

输出 :2 2 0(被1段区间覆盖的点为1和4,2段覆盖的点为2和3,没有被三段区间覆盖的点)

思路:

<mtext>        </mtext>~~~~~~~~        正常看就是差分板子题,但是由于 Ai,BiA_i,B_iAi,Bi 最大到 10910^9109 ,直接差分数组开不下,所以加上离散化。离散化的主要操作在于把所有的数放进一个vector,然后进行去重操作。

vector<int>a;
sort(a.begin(),a.end());
a.erase(unique(a.begin(),a.end()),a.end());

获得对应原数组第i个数的下标

int idx = lower_bound(v.begin(),v.end(),a[i])-v.begin();

<mtext>        </mtext>~~~~~~~~        用离散化后的下标做差分,差分数组就只需要开到 4e5 就可以正常操作了,之后就用差分数组还原真实数组累加计算答案即可。

AC代码:

#include <bits/stdc++.h>
typedef long long ll ;
#define int ll
const int N=1e6+10;
const int mod=1e9+7;
const int INF=0x3f3f3f3f3f3f;
#define endl '\n'
#define cwkr     freopen("D:\\workplace\\CLion\\untitled\\in.in","r",stdin)
#define cwkw     freopen("D:\\workplace\\CLion\\untitled\\out.out","w",stdout)
using namespace std;
typedef pair<int, int> PII;
int n,m,k,a[N],b[N],c[N],num[N];
vector<int>v;
void solve(){
    cin>>n;
    for(int i=0;i<n;i++){
        cin>>a[i]>>b[i];
        b[i]+=a[i];
        v.push_back(a[i]);
        v.push_back(b[i]);
    }
    sort(v.begin(),v.end());
    v.erase(unique(v.begin(),v.end()),v.end());
    for(int i=0;i<n;i++){
        int idx=lower_bound(v.begin(),v.end(),a[i])-v.begin();
        c[idx]++;
        idx=lower_bound(v.begin(),v.end(),b[i])-v.begin();
        c[idx]--;
    }
    for(int i=1;i<v.size();i++) c[i]+=c[i-1];
    for(int i=0;i<v.size()-1;i++){
        int x=c[i];
        int y=v[i+1]-v[i];
        num[x]+=y;
    }
    for(int i=1;i<=n;i++) cout<<num[i]<<" ";
}
signed main() {
    ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
    //cwkr;
    //cwkw;
    int _=1;
    //cin>>_;
    while(_--){
        solve();
    }
    return 0;
}

E - LEQ【树状数组】

题目链接:https://atcoder.jp/contests/abc221/tasks/abc221_e)

Tips:最好先掌握树状数组求逆序对

题目大意:

给定一个长度为 nnn 的序列 AAA ,找出一个长度为 k(k2)k(k\geq 2)k(k2) 的序列 AA'A 使得A1AkA'_1 \le A'_kA1Ak

2n3×1052 \le n \le 3\times 10^52n3×1051Ai1091 \le A_i \le 10^91Ai109

思路:

假设 AxAy(x<y)A_x\le A_y(x < y)AxAy(x<y)AxA_xAxA1A'_1A1AyA_yAyAkA'_kAk 。那么 xyxyxy 中间的 yx1y-x-1yx1 个数可选可不选,即以 AxA_xAx 开头 AyA_yAy 结尾的序列有 2yx12^{y-x-1}2yx1 种。此时问题就变成了求 x=1ny=x+1n[AxAy]2yx+1\sum\limits_{x=1}^n\sum\limits_{y=x+1}^n [A_x \le A_y] 2^{y-x+1}x=1ny=x+1n[AxAy]2yx+1 的值。

最朴素的想法就是两层循环暴力计算,但是复杂度肯定过不去。已知树状数组求逆序对可以解决 x=1ny=x+1n[AxAy]\sum\limits_{x=1}^n\sum\limits_{y=x+1}^n [A_x \geq A_y] x=1ny=x+1n[AxAy] 的问题,那么就只需要在此基础上维护 AxAyA_x \le A_yAxAy 的情况和次方信息即可。

更改时:对于 AxA_xAx ,在树状数组的对应位置上加上 2nx12^{n-x-1}2nx1 次方。

计算时:枚举 xxx ,用树状数组求 y=x+1n[AxAy]2y\sum\limits_{y=x+1}^n[A_x\le A_y] 2^yy=x+1n[AxAy]2y 的值再乘 2x+12^{-x+1}2x+1

AC代码:

#include <bits/stdc++.h>
#define int long long
#define lowbit(x) (x&(-x))
using namespace std;
const int MAX=3e5+10;
const int mod = 998244353;
int n,tree[MAX],a[MAX],c[MAX],di[MAX];
int sum(int x){
    int ans=0;
    while(x){
        ans+=tree[x];
        ans%=mod;
        x-= lowbit(x);
    }
    return ans;
}
void add(int x,int d){
    while(x<=n){
        tree[x]+=d;
        tree[x]%=mod;
        x+= lowbit(x);
    }
}
int inv(int a){
    if(a == 1)return 1;
    return inv(mod%a)*(mod - mod/a)%mod;
}
vector<int> v;
void solve(){
    cin>>n;
    c[0]=1;
    for(int i=1;i<=n;i++){
        c[i]=(c[i-1]*2)%mod;
    }
    for(int i=0;i<n;i++){
        cin>>a[i];
        v.push_back(a[i]);
    }
    //离散化
    sort(v.begin(),v.end());
    v.erase(unique(v.begin(),v.end()),v.end());
    int shu=0;
    for(int i=0;i<n;i++){
        int idx=lower_bound(v.begin(),v.end(),a[i])-v.begin()+1;
        int ans=sum(idx);
        ans=(ans*inv(c[n-i]))%mod;
        shu+=ans;
        shu%=mod;
        add(idx,c[n-i-1]);
    }
    cout<<shu;
}

signed main() {
    int _=1;
    //cin>>_;
    while(_--){
        solve();
    }
    return 0;
}