1. 程式人生 > >【CF Gym100228】Graph of Inversions

【CF Gym100228】Graph of Inversions

des int esc 數據 continue swa name define clas

Portal --> qwq(貌似是CodeForces Gym 100228 (ECNA2003) - I)

Description

  對於長度為 \(n\) 的序列 \(A\) ,定義其逆序圖 \(G\) 如下:無向圖 \(G\)\(n\) 個節點,編號為 \(0..n-1\) ;對於任意的$ 0≤i<j≤n?1$ ,如果有 \(a[i]>a[j]\),那麽 \(G\)中存在一條 \(i\)\(j\)之間的邊。例如:\(A=\{1,3,4,0,2\}, G=\{(0,3),(1,3),(1,4),(2,3),(2,4)\}\)
?  定義獨立集 \(S\):對於\(?x∈S,y∈S\)

,都不存在一條邊$ (x,y)$
?  定義覆蓋集 \(S\) :對於\(?x?S\),至少存在一條邊$ (x,y)$,使得 \(y∈S\)
?  現在給你一個逆序圖 \(G\)(保證合法),求$ G$ 有多少個點集既是獨立集又是覆蓋集。

?  數據範圍:\(1<=n<=1000,0<=m<=n*(n-1)/2\)

  

Solution

?  首先。。圖的獨立集是。。一個np問題==那所以直接在圖上面搞什麽的顯然是不理智的qwq

  那所以。。要好好利用逆序圖這個條件

  把獨立集和覆蓋集放在回原來的序列裏面來看,其實就是:\(S\)中的元素無法構成逆序對(也就是說。。必須遞增),並且任意非\(S\)

元素均能與\(S\)中至少一個元素構成逆序對

?  所以我們其實是要找有多少個遞增的子序列滿足第二個條件

?  這個要怎麽找呢。。考慮dp,記\(f[i]\)表示以\(i\)結尾的滿足條件的子序列有多少個,那麽考慮轉移,\(f[i]\)能夠轉移到\(f[j]\),當且僅當滿足\(a[i]<a[j]\)並且\(i\)\(j\)中間的這段都要能和子序列中的至少一個元素構成逆序對,也就是要麽小於\(a[i]\)要麽大於\(a[j]\),然後因為如果小於\(a[i]\)的話不滿足第一個轉移條件,所以\(i\)\(j\)之間的,除了之前能夠轉移的位置,其他肯定都是小於\(a[i]\)的不用管,我們只要看\(>a[i]\)

中最大的那個是不是\(>a[j]\)就好了,具體實現其實很簡單,因為這些需要單獨考慮的位置肯定是之前遇到的能夠轉移的位置,所以我們開多一個\(tmp\)記錄一下最大值即可

?  至於這個序列要怎麽還原,因為只有大於和小於關系,所以。。我們欽定一下這個序列是一個\(1\)\(n\)的排列,然後我們可以通過逆序對得到每個數前面比它大的有多少個,後面比它大的有多少個,那就可以得到每個數的具體值了(為了方便統計答案我們可以將\(a[n+1]\)欽定成一個很大的數然後計算到\(n+1\)位,答案就是\(f[n+1]\)

  

?  代碼大概長這個樣子

#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
using namespace std;
const int N=1010;
struct Rec{
    int x,y;
}rec[N*(N-1)/2];
int a[N],cnt[N];
ll f[N];
int n,m,ans;
void dp(){
    int tmp;
    f[0]=1;
    for (int i=0;i<=n;++i){
        tmp=n+2;
        for (int j=i+1;j<=n+1;++j){
            if (a[j]<a[i]||a[j]>=tmp) continue;
            f[j]+=f[i];
            tmp=a[j];
        }
    }
}

int main(){
#ifndef ONLINE_JUDGE
    freopen("a.in","r",stdin);
#endif
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;++i) cnt[i]=n-i;
    for (int i=1;i<=m;++i){
        scanf("%d%d",&rec[i].x,&rec[i].y);
        ++rec[i].x; ++rec[i].y;
        if (rec[i].x>rec[i].y) swap(rec[i].x,rec[i].y);
        ++cnt[rec[i].y]; --cnt[rec[i].x];
    }
    for (int i=1;i<=n;++i) a[i]=n-cnt[i];
    a[n+1]=n+1;
    dp();
    printf("%d\n",f[n+1]);
}

【CF Gym100228】Graph of Inversions