gym101964 I.Inversion(拓扑排序,思维,dp)
Posted live4m
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了gym101964 I.Inversion(拓扑排序,思维,dp)相关的知识,希望对你有一定的参考价值。
题意:
解法:
给定的图可以还原每个节点之间的大小关系,
那么根据图可以还原出原排列:
令大的位置指向小的位置,拓扑排序记录时间戳,时间戳小的放大值即可.
这题也可以直接O(n^2)根据大小关系依次暴力找出n,n-1,n-2...
有了原排列,考虑如何计算答案.
如果第一个位置选择了i,那么我们之后只能选择>a[i]的数,
因为我们如果选择了<a[i]的数,会和a[i]组成逆序对产生边,就不是独立集了.
发现我们选出的数一定是递增的.
同时[1,i-1]不选,因此a[1,i-1]中的所有值都必须>a[i],
因为不选择的点必须和选择的点之间有边,
我们选择的数是递增的,a[i]最小,因此与a[i]产生逆序对最容易.
同理,假设我们第二个数选择了a[j],i<j,
设i<k<j,k是没选的数,
那么必须满足a[k]<a[i]或者a[k]>a[j],这样才能产生逆序对,
即不能存在a[k]在(a[i],a[j])范围内.
与第一个数相同,选择的最后一个位置p需要满足:
p后面的数都比p小,因为这样才能产生逆序对,与上面同理.
这样就能dp了,
令d[i]为前i个数中,以i为结尾的合法序列方案数.
初始化:
d[t]=1,其中t满足t左边的所有数都大于a[t],即可以作为第一个数.
dp转移:
d[i]+=d[j],其中j<i,a[j]<a[i],且a[j+1,i-1]不在(a[j],a[i])范围内.
判断a[j+1,i-1]是否在(a[j],a[i])范围内可以直接for循环暴力.
累加答案:
对于可以作为结尾的位置p,ans+=d[p],
p需要满足p右边的所有数都<a[p].
code:
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxm=111+5;
vector<int>g[maxm];
int dfn[maxm];
int idx[maxm];
int deg[maxm];
int a[maxm];
int n,m;
bool cmp(int i,int j){
return dfn[i]>dfn[j];
}
void topo(){
queue<int>q;
for(int i=1;i<=n;i++){
if(!deg[i]){
q.push(i);
}
}
int num=0;
while(q.size()){
int x=q.front();q.pop();
dfn[x]=++num;
for(int v:g[x]){
if(deg[v]){
deg[v]--;
if(!deg[v]){
q.push(v);
}
}
}
}
for(int i=1;i<=n;i++){
idx[i]=i;
}
sort(idx+1,idx+1+n,cmp);
for(int i=1;i<=n;i++){
int x=idx[i];
a[x]=i;
}
}
void add(int i,int j){
g[i].push_back(j);
deg[j]++;
}
int mp[maxm][maxm];
int r[maxm];
int f[maxm];
int cnt[maxm];
void solve(){
cin>>n>>m;
for(int i=1;i<=m;i++){
int x,y;cin>>x>>y;
if(x>y)swap(x,y);
mp[x][y]=1;
}
for(int i=1;i<=n;i++){
for(int j=i+1;j<=n;j++){
if(mp[i][j]){
add(i,j);
}else{
add(j,i);
}
}
}
topo();
r[n]=a[n];
for(int i=n-1;i>=1;i--){
r[i]=max(a[i],r[i+1]);
}
int ans=0;
for(int i=1,mi=1e9;i<=n;i++){
if(mi>a[i]){
cnt[i]=1;
}
mi=min(mi,a[i]);
}
for(int i=1;i<=n;i++){
for(int j=1;j<i;j++){
if(a[j]>a[i])continue;
int ok=1;
for(int k=j+1;k<=i-1;k++){
if(a[k]>a[j]&&a[k]<a[i]){//不能有(a[j],a[i])范围内的数
ok=0;
}
}
if(ok)cnt[i]+=cnt[j];
}
}
for(int j=1;j<=n;j++){
if(j==n||r[j+1]<a[j]){
ans+=cnt[j];
}
}
cout<<ans<<endl;
}
signed main() {
#ifndef ONLINE_JUDGE
freopen("../in.txt","r",stdin);
freopen("../out.txt","w",stdout);
#endif
solve();
return 0;
}
以上是关于gym101964 I.Inversion(拓扑排序,思维,dp)的主要内容,如果未能解决你的问题,请参考以下文章
Gym - 101964E Fishermen(差分区间修改)
gym101964 G.Matrix Queries(思维+线段树)
gym101964 B.Broken Watch(画图+数学,python大数)