Codeforces 652C Foe Pairs【二分+RMQ】好像这题Dp做法很多啊

来源:互联网 发布:sql连表update 编辑:程序博客网 时间:2024/05/23 19:19

C. Foe Pairs
time limit per test
1 second
memory limit per test
256 megabytes
input
standard input
output
standard output

You are given a permutation p of lengthn. Also you are given m foe pairs (ai, bi) (1 ≤ ai, bi ≤ n, ai ≠ bi).

Your task is to count the number of different intervals (x, y) (1 ≤ x ≤ y ≤ n) that do not contain any foe pairs. So you shouldn't count intervals(x, y) that contain at least one foe pair in it (the positions and order of the values from the foe pair are not important).

Consider some example: p = [1, 3, 2, 4] and foe pairs are{(3, 2), (4, 2)}. The interval (1, 3) is incorrect because it contains a foe pair (3, 2). The interval (1, 4) is also incorrect because it contains two foe pairs(3, 2) and (4, 2). But the interval(1, 2) is correct because it doesn't contain any foe pair.

Input

The first line contains two integers n andm (1 ≤ n, m ≤ 3·105) — the length of the permutationp and the number of foe pairs.

The second line contains n distinct integerspi (1 ≤ pi ≤ n) — the elements of the permutationp.

Each of the next m lines contains two integers(ai, bi) (1 ≤ ai, bi ≤ n, ai ≠ bi) — the i-th foe pair. Note a foe pair can appear multiple times in the given list.

Output

Print the only integer c — the number of different intervals(x, y) that does not contain any foe pairs.

Note that the answer can be too large, so you should use 64-bit integer type to store it. In C++ you can use thelong long integer type and in Java you can use long integer type.

Examples
Input
4 21 3 2 43 22 4
Output
5
Input
9 59 7 2 3 1 4 6 5 81 64 52 77 22 7
Output
20
Note

In the first example the intervals from the answer are (1, 1), (1, 2), (2, 2),(3, 3) and (4, 4).


题目大意:


给你长度为N的一个序列(保证从1~N);

然后在给你M对序列(x,y),让你统计一共有多少个区间[L,R],使得区间内任意两个数ai,aj(L<=i,j<=R)在M个序列中,没有(ai,aj)存在,而且也没有(aj,ai)存在;


思路:


1、很个人化的思路,直接O(n)枚举一个起点然后考虑枚举终点j,随着j的增大,使得可能出现(x,y)的概率增大,那么这里就包含一个单调性,那么我们可以二分终点,对于区间【i,end】之内,肯定所有子区间【i,j】(1<=j<=end)都是可行区间。


2、那么接下来考虑如何判定,如果O(nlogn)的时间复杂度去尝试枚举,那么对应如果判定时间复杂度大于等于O(n)都是肯定不行的,那么此时考虑整个问题具有的特性。

N=9 A【】={9,7,2,3,1,4,6,5,8};

那么我们不妨设定一个数组sum【i】=x表示以i为起点,后边不存在(ai,aj)属于M个序列的任一序列的最远位子x;

那么如果此时m==0.那么我们可以初始化:

sum【1~9】=10;

然后在计数的时候,sum【i】--即可。

假设我们此时m==2,(x,y)==(1,6),(x,y)==(2,6);

那么sum【】:{10,10,7,10,7,10,10,10,10};

sum【i】--后的结果:

sum【】:{9,9,6,9,6,9,9,9,9};

那么很明显,对应如果我们以1为区间起点的话,其最远可以到6这个位子,都是合法的区间,如果到7的话,区间内出现了(1,6)(2,6);

我们又要如何解决这个问题呢?

其实很简单,我们只要判定起点i到当前二分的终点之内的sum【】最小值是否小于当前这个二分的终点即可,如果有这样的情况存在,就要减小终点,否则就可以增大终点。

那么解决这个判定问题我们套个ST表解决RMQ问题就行了。


3、思路比较偏向个人化,自己解这个题的时候还是比较顺的............说起来有点复杂,大家不妨参考代码结合上述语言描述去理解一下。


Ac代码:

#include<stdio.h>#include<string.h>#include<algorithm>#include<math.h>using namespace std;int pos[308000];int a[308000];int sum[308000];int minn[300505][25];int n,m;void ST(){    int len=floor(log10(double(n))/log10(double(2)));    for(int j=1;j<=len;j++)    {        for(int i=1;i<=n+1-(1<<j);i++)        {            minn[i][j]=min(minn[i][j-1],minn[i+(1<<(j-1))][j-1]);        }    }}int getminn(int a,int b){    int len= floor(log10(double(b-a+1))/log10(double(2)));    return min(minn[a][len], minn[b-(1<<len)+1][len]);}int main(){    while(~scanf("%d%d",&n,&m))    {        for(int i=1;i<=n;i++)sum[i]=n+1;        for(int i=1;i<=n;i++)scanf("%d",&a[i]),pos[a[i]]=i;        for(int i=0;i<m;i++)        {            int x,y;            scanf("%d%d",&x,&y);            int posx=pos[x];            int posy=pos[y];            if(posx>posy)swap(posx,posy);            sum[posx]=min(sum[posx],posy);        }        for(int i=1;i<=n;i++)sum[i]--;        for(int i=1;i<=n;i++)minn[i][0]=sum[i];        ST();        long long int output=0;        for(int i=1;i<=n;i++)        {            int ans=-1;            int l=i;            int r=n;            while(r-l>=0)            {                int mid=(l+r)/2;                if(getminn(i,mid)>=mid)                {                    ans=mid;                    l=mid+1;                }                else r=mid-1;            }            //printf("%d\n",ans);            output+=(long long int)(ans-i+1);        }        printf("%lld\n",output);    }}










0 0