【CF Gym100228】Graph of Inversions

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]);
}

原文地址:https://www.cnblogs.com/yoyoball/p/9745300.html

时间: 2024-08-25 11:44:49

【CF Gym100228】Graph of Inversions的相关文章

【CF 566F】 Clique in the Divisibility Graph

[CF 566F] Clique in the Divisibility Graph 最大团模型的dp 数做点 能约分的一对数间有路 问最大团(最大完全子图) 用最长公共子序列做法 dp出最长路 由于一个数约数的约数也是这个数的约数 所以只要能连起来就是个完全子图 代码如下: #include <iostream> #include <cstdlib> #include <cstdio> #include <cstring> #include <alg

【CF 520D】Cubes

[CF 520D]Cubes 怎么说呢--英语阅读题+超级大模拟-- 最重要的是知道怎么出来的数据...题意好懂 xy坐标内给出几个单位正方形 以正方形左下点坐标给出 y=0为地面 正方形下面或者左右下方至少存在一个正方形他才能稳定.. 正方形按0~m-1标号 每次只能取出不影响整体结构的正方形 甲乙玩一个游戏 交替取正方形 每取下一个按从左到右的顺序排好 得到一个大数 重点来了! 取出的数是m进制 转换为十进制是最终结果 甲希望结果最大 乙希望结果最小 问结果为多少 甲先取 题意明白了模拟就行

【CF 474E】Pillars

[CF 474E]Pillars 离散化+线段树dp 大半夜写出来了...好长好长好长好长好挫--先把高度排序离散化 我又开了个哈希数组用来查某点对应离散后的点 然后遍历每个点时二分出满足题意的区间(1~h-d)(h+d~max) 然后线段树查两个区间当前最大长度的序列 累计到当前点对应的树内点 同时更新他的父亲点们的最大长度 再把之前最大长度的末尾作为当前点的前驱 如果没有就用当前点自己做前驱 最后输出树根存的节点的前驱们(即为树内最长的序列) 各种节点哈希的有点混乱--代码--看乱了就别看了

【CF 460C】Present

[CF 460C]Present 二分+贪心 二分最矮高度的最大值 每二分一个遍历看是否可达 可达low = mid+1不可达high = mid-1 可达的判断用贪心即可 改点长度不足时 在改点设置浇水点 同是在i+w设置断水 之后每个点都继承前一个点的浇水量 到i+w时减少i处浇的水即可 代码如下: #include <iostream> #include <cstdio> #include <cstring> #define sz 100000 #define l

【CF 459D】 Pashmak and Parmida&#39;s problem

[CF 459D] Pashmak and Parmida's problem 预处理+线段树求逆序对 新学了树状数组 很适合这题 来一发 代码如下: #include <iostream> #include <cstdio> #include <cstdlib> #include <map> #include <cstring> #define ll long long using namespace std; map <int,int&

【CF 515D】 Drazil and Tiles

[CF 515D] Drazil and Tiles 拓扑的思想 如果满足条件 '.'未遍历完之前肯定存在度为1的点(上下左右仅有一个'.') 遍历到一个1度点u时 将与他连的点v用对应括号括起 此时v也已匹配 入度归零 同时将与v相连的其余点入度减1 不断遍历知道遍历完所有'.' 若能遍历完 则满足条件否则无解或多解 (吐槽一下 原本用的纯粹拓扑思路 想想也是绝对要T的..后来改换BFS 代码如下: #include <iostream> #include <cstdio> #i

【CF 507E】Breaking Good

[CF 507E]Breaking Good 双条件最短路 每个路有已搭建和未搭建两种状态 需要把经过的路都建起 为经过的路都拆掉 优先经过最少条路 同样少的路走改动(搭建+拆掉)最小的 最短路跑完后把最短的路上的路径标记一下 bfs输出拆除和搭建 在最短路径上的路 未建的搭建 不在的建好的拆掉 通过此题试了一下spfa的一个小优化还有dijkstra的优先队列优化 不过别看spfa加优化快了点 前两天做了个专门卡这个优化的题--想方设法让他多跑就是..HDOJ 4889 有兴趣的可以去瞅瞅 此

【POJ 1419】Graph Coloring

[POJ 1419]Graph Coloring 求图的最大独立集 最大独立集=补图最大团 很适合理解最大团/最大独立集的模板题 建立补图套模板既可 需要输出集合点 原本想用stack 但发现copy比较麻烦 vector用一个iterator指针 循环很便利 代码如下: #include <iostream> #include <cstdlib> #include <cstdio> #include <cstring> #include <vecto

【线段树】【树状数组】【CF 121E】幸运数列

1922. [CF 121E]幸运数列 ★★★ 输入文件:cf121e.in 输出文件:cf121e.out 简单对比 时间限制:3 s 内存限制:256 MB [题目描述] 对于欧洲人来说,"幸运数"是指那些十进制只由4或7组成的数.财务员Petya需要维护一个支持如下操作的整数数列: add l r d - 表示将[l, r]区间内的所有数加上一个正整数d(). count l r - 统计[l, r]区间内有多少个"幸运数".() 请你帮助Petya实现它.