【2018.6.29NOIP模拟】T1区间interval 【神秘技巧】*

【2018.6.29NOIP模拟】T1区间interval


【问题描述】

给出一个长度为 n 的序列 a[1]…a[n]。
给出 q 组询问,每组询问形如

【输入格式】

第一行两个数 n 和 q。
第二行 n 个数 a[i]。
接下来 q 行,每行两个数 x,y 表示一组询问。

【输出格式】

输出 q 行,每行一个数表示对应询问的答案。

【样例输入】

3 2
1 2 1
1 2
4 5

【样例输出】

2
6

【数据规模与约定】

对于 30%的数据:1≤n≤100;1≤q≤1000。
对于另外 30%的数据:序列中只有最多 50 种不同数字且 1≤n≤1000。
对于 100%的数据:1≤n≤8000;1≤q≤500000;1≤x,y,a[i]≤10^9。


反正考试的时候写了两个subtask总计60分然后爆零

我也不知道发生了什么

然后说说正解:
对于两个数x和y,只有出现x和y的位置对答案有影响,所以我们把每个数出现的位置存下来,然后对于x和y,我们考虑怎么求解:

首先我们如果定义一个数res,当出现x时res++,出现y时res–,我么可以发现如果两个端点的res值相等,那么这是一个可行区间
然后我们就在所有出现过x和y的节点处更新res就好了,我们用cnt数组统计在last前出现的相同的res次数,然后当前这段区间的长度和cnt[res+n]来更新答案,最后更新一下cnt数组和last值和当前的res就好了

我也不知道为什么,cout比printf更慢。。。


#include<bits/stdc++.h>
using namespace std;
int read(){
    int ans=0,w=1;char c=getchar();
    while(!isdigit(c)&&c!='-')c=getchar();
    if(c=='-')c=getchar(),w=-1;
    while(isdigit(c))ans=ans*10+c-'0',c=getchar();
    return ans*w;
}
#define N 8010
int n,q,tot=0,a[N],b[N],cnt[N<<2];
int ans[110][110];
vector<int> G[N],p;
map<int,int> mp;
int main(){
    int n=read(),q=read();
    for(int i=1;i<=n;i++)b[i]=a[i]=read();
    sort(b+1,b+n+1);
    for(int i=1;i<=n;i++)
        if(b[i]!=b[tot])b[++tot]=b[i],mp[b[i]]=tot;
    for(int i=1;i<=n;i++)G[mp[a[i]]].push_back(i);
    for(int i=0;i<=tot;i++)G[i].push_back(n+1);
    while(q--){
        int x=mp[read()],y=mp[read()];
        int tx=0,ty=0,res=0,last=0,sum=0;
        cnt[n]++;
        while(last<n){
            int tmp=sum+n,len;
            if(G[x][tx]<G[y][ty])len=G[x][tx++]-last-1,sum++;
            else len=G[y][ty++]-last-1,sum--;
            res+=len*cnt[tmp]+len*(len-1)/2;
            cnt[tmp]+=len;
            last+=len;
            p.push_back(tmp);
        }
        while(!p.empty())cnt[p.back()]=0,p.pop_back();
        cout<<res<<"\n";
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/dream_maker_yk/article/details/80857448