~_~

查询

Description

给出一个长度为 n n n的序列 a a a给出 q q q组询问,每组询问形如 ( x , y ) (x,y) (x,y),求 a a a序列的所有区间中,数字 x x x的出现次数与数字 y y y的出现次数相同的区间有多少个。

Data Constraint
1 < = n < = 8 ∗ 1 0 3 , 11 < = x , y , a i < = 1 0 9 1<=n<=8∗10^3,11<=x,y,a_i<=10^9 1<=n<=810311<=x,y,ai<=109

题解

记录每个数字出现的位置,(由于 a [ i ] a[i] a[i]的范围是 1 0 9 10^9 109,所以我们先将数列进行离散化),对于某个询问 ( x , y ) (x,y) (x,y),我们可以将 ( x , y ) (x,y) (x,y)出现的位置序列合并并按照从小到大排序,假设合成的位置序列为 p 1 , p 2 , p 3 . . . . p_1, p_2, p_3.... p1,p2,p3....,对于答案 a n s ans ans,我们可以将答案分成 i i i类(对于x和y数量都为0的情况单独算),表示为 a n s 1 , a n s 2 . . . . . ans_1,ans_2..... ans1,ans2..... a n s i ans_i ansi a n s i ans_i ansi代表区间内包含的位置序列的最大值为 p i p_i pi的区间数量,那么对于每个位置 p i p_i pi,我们可以向前枚举 p i − 1 … … p 1 p_{i - 1}……p_1 pi1p1,只要发现 x x x y y y的数量相等,我们就可以用乘法原理包含此序列的区间数量加到 a n s i ans_i ansi上,最后加上 x , y x,y x,y都不存在的区间的数量,但是这样枚举复杂度是平方级别,会被卡掉,所以我们需要进行一些优化,我们可以将x出现的每个位置设置为1,y出现的每个位置设置为-1,我们按照位置序列开始模拟,我们发现对于我们所走到的位置pos,若想要某个区间 x , y x,y xy的数量相等,那么这个区间左端点也一定是从这里出发的,所以我们可以将信息保存在该数组上,进而将答案累加起来。

#include <iostream>
#include <cstring>
#include <map>
#include <algorithm>
#include <unordered_map>
#include <vector>
#include <set>

using namespace std;

const int N = 8010, P = 131, mod = 1e9 + 7; //  -_-   风气真的是差!
const double PI = 3.1415926535;

typedef unsigned long long ULL;
typedef long long LL;
typedef pair <int, int> PII;
typedef pair <double, double> PDD;
typedef pair <LL, LL> PLL;


int a[N], ans[N][N], n, q, res[N][N];
vector <int> t, pos[N];
PII temp[(int)5e5 + 10];


int find (int x)
{
    int l = 0, r = n - 1;
    while (l < r)
    {
        int mid = l + r >> 1;
        if (t[mid] >= x) r = mid;
        else l = mid + 1;
    }
    return l;
}


int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0); 
    
    cin >> n >> q;
    for (int i = 1; i <= n; i ++) cin >> a[i];
    
    for (int i = 1; i <= n; i ++) t.push_back (a[i]);

    sort (t.begin(), t.end());
    t.erase (unique (t.begin(), t.end()), t.end());
    
    int len = t.size();
    for (int i = 1; i <= n; i ++)
    {
        int x = find (a[i]);
        pos[x].push_back (i);
    }
    
    //for (int i = 0; i < t.size(); i ++) pos[i].push_back (n + 1);
    
    
    for (int i = 1; i <= q; i ++)
    {
        int x, y; cin >> x >> y;
        int t1 = find (x), t2 = find (y);
        temp[i] = {t1, t2};
        
       // cout << t1 << ' ' << t2 << endl;
        ans[t1][t2] = true;
    }
    
    for (int i = 0; i < n; i ++)
        for (int j = 0; j < n; j ++)
            if (ans[i][j])
            {
                int l = 0, r = 0, idx = 8010, pre = 0;
                int c[16020] = {0};
                vector <PII> ts;
                ts.push_back ({0, 0});
                while (l < pos[i].size() || r < pos[j].size())
                {
                    if (l >= pos[i].size()) ts.push_back ({pos[j][r ++], 1});
                    else if (r >= pos[j].size()) ts.push_back ({pos[i][l ++], -1});
                    else if (pos[i][l] < pos[j][r]) ts.push_back ({pos[i][l ++], -1});
                    else ts.push_back ({pos[j][r ++], 1});
                }
                ts.push_back ({n + 1, 0});
                
                for (int k = 0; k < ts.size(); k ++) cout << ts[k].first << ' ';
                
                cout << endl;
                for (int k = 1; k < ts.size(); k ++) 
                {
                    int d = ts[k].first - ts[k - 1].first;
                    
                    
                    res[i][j] += (d - 1) * (d - 2) / 2;
                    
                    cout << (d - 1) * (d - 2) / 2 << ' ';
                    if (k == ts.size() - 1) break;
                    c[idx] += d, idx += ts[k].second;
                    int d1 = ts[k + 1].first - ts[k].first;
                    
                    
                    res[i][j] += d1 * c[idx]; //cout << d1* c[idx] << ' ';
                }
                cout << endl;
            }
    
    
    for (int i = 1; i <= q; i ++)  printf ("%d\n", res[temp[i].first][temp[i].second]);

    return 0; //652
}
    
    
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值