Same Occurrence

原题:Same Occurrence

原题大意

给一个长度为n的序列,和q个询问,每个询问有两个数x,y。问在所有的子序列中x,y出现次数相同的序列个数有多少?

算法分析

首先观察数据范围可以得出一下结论:

  • n的长度最多有8000,可以用离散化缩小单个数据范围
  • 询问最多有500000,如果n小一点的话,肯定会有重复询问,可以有数组记录询问结果

然后考虑具体的询问

  • 如果两个数相同或者都不在序列中,那么答案是n*(n+1)/2
  • 如果只有一个数 x存在于序列 a, b, c, x, e, f, x, g, h, i, x中,我们可以将序列分割成如下形式[a,b,c],[e,f],[g,h,i],然后按上一种情况处理每个区间。
  • 如果两个数都存在于序列中,设前i个数里含x的有c个,含y的有d个,在前j(j < i)处,含x的有a个,含y的有b个,若使得(i,j]可以被选中,那么c-a == d-b,变型一下就是c-d == a-b。那么现在维护前缀就好了。因为序列中存在大量的非x,y的数,我们可以用vector记录x,y出现的位置,然后vector里的后一个减去前一个,就得到了中间的值,并加入到f[c-d]里去。最后结果就是遍历f,对于每一个f的值x,进行x*(x-1)/2,然后求和。

程序代码

#include <bits/stdc++.h>
typedef long long ll;
#define fi first
#define se second
#define mem(a,b) memset(a,b,sizeof(a))
#define pb push_back
using namespace std;
const int maxn = 8005;
int n, q;
map<int, int> dict;
vector<int> adj[maxn];
int dp[maxn][maxn];
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin >> n >> q;
    int tot = 0;
    for(int i = 1; i <= n; i++){
        int x;
        cin >> x;
        if(!dict.count(x)) dict[x] = ++tot;
        adj[dict[x]].pb(i);
    }
    mem(dp,-1);
    while(q--){
        int x, y;
        cin >> x >> y;
        x = dict[x];
        y = dict[y];

        if(dp[x][y] != -1){
            cout << dp[x][y] << "\n";
            continue;            
        }

        if(x == y)
        {
            dp[x][y] = n * (n+1) / 2;
            cout << dp[x][y] << "\n";
            continue;
        }

        map<int, int> mp;
        vector<pair<int, int> > v;
        int i = 0, j = 0;
        while((i < adj[x].size()) && (j < adj[y].size())){
            if(adj[x][i] < adj[y][j]){
                v.pb({adj[x][i], 0});
                i++;
            }
            else{
                v.pb({adj[y][j], 1});
                j++;
            }
        }
        while(i < adj[x].size()){
            v.pb({adj[x][i], 0});
            i++;
        }
        while(j < adj[y].size()){
            v.pb({adj[y][j], 1});
            j++;
        }
        int len = v.size();
        mp[0] = 1;
        if(len >= 1){
            mp[0]+=v[0].fi-1;
        }
        int cnt1 = 0, cnt2 = 0;
        for(i = 0; i < len - 1; i++){
            if(v[i].se == 0){
                cnt1++;
            }
            else cnt2++;
            mp[cnt1-cnt2] += v[i+1].fi - v[i].fi;
        }
        if(len >= 1 && i >= 0 && i < n){
            if(v[i].se == 0) cnt1++;
            else cnt2++;
            mp[cnt1-cnt2] += n+1 -v[i].fi;
        }
        ll ans = 0;
        for(auto p : mp){
            ll temp = p.se;
            ans += temp*(temp-1)/2;
        }
        dp[x][y] = ans;
        cout << dp[x][y] << "\n";
    }
    return 0;
}