AGC059C Guessing Permutation for as Long as Possible 做题笔记

明日散る運命なら
简单题
我花了 \(\left \lfloor \pi \right \rfloor\) 分钟想到了本题唯一的,也是最关键的核心结论,然后在距离正解一步之遥的时候倒闭了。

题意简述

\(\dfrac{n(n-1)}{2}\) 个互不相同的二元组 \((a_i, b_i)\),满足 \(a<b, a, b\in \{1, 2, ..., n\}\)。(显然,它们取遍了所有这样的二元组)

考虑满足如下条件的排列 \(p\) 的个数:考虑集合 \(S\) 初始为所有的 \(n\) 全排列的集合。\(i\)\(1\) 开始,对每一组 \((a_i, b_i)\),做出询问排列的第 \(a_i\) 和第 \(b_i\) 项那个大?按照 \(p\) 里的情况做出回答后,把 \(S\) 中所有不满足这一条件的排列全部删去。排列 \(p\) 必须满足,每次询问都有至少一个排列被删去。

解法

这道题需要一个核心观察。其实这个观察十分显然,就是原条件等价于如下的条件:

任意三个不同的数 \(a, b, c\),如果 \((a, c)\) 出现在 \((a, b)\)\((b, c)\) 后方,则 \((a, b)\)\((b, c)\) 的回答不同。

等价性十分容易证明。

得到这个观察后,考虑到可以通过统计不同的回答组数量来计数排列。设第 \(i\) 个问询的答案是 \(o_i\)(\(o_i\)\(0\) 表示 \(a\) 大,为 \(1\) 表示 \(b\) 大)

那么只需枚举所有的三元组,就可以得到 \(\dbinom{n}{3}\) 个约束,每个约束形如 \(o_i=o_j\) 或者 \(o_i\ne o_j\)。把这些约束当作边,建图,然后直接跑 dfs 即可。

总结

这道题有什么好说的呢?感觉实在有点过于简单了。可能比较重要的一点在于想到通过计算回答组的数量来计算排列的数量。这是因为排列和回答组都可以相互地确定,或者说,如果只选出不互相矛盾的所有回答组,那么可以建立它们到所有排列的双射。

当时我想到过使用 2-SAT,但是我认为 2-SAT 没法维护正整数之间的大小关系,其实如果我对 2-SAT 理解足够的话,应该能想到维护每一个问询的答案。

Code

注意一下 bool 虽然占用了一个字节,但是它只能表示 \(0/1\),没法表示任何其他数字。

#include <iostream>
#include <random>
#include <map>
#include <algorithm>
using namespace std;
typedef long long ll;
#define rep(i, a, b) for(ll i=a;i<=b;i++)
#define rrep(i, a, b) for(ll i=a;i>=b;i--)
const ll N=409, MOD=1e9+7;
ll n;
ll a[N*N],b[N*N];//!数组没有开大
ll pos[N][N];
vector <ll> to[N*N], ty[N*N];//type表示同向还是反向
ll vst[N*N];//!虽然bool占有一个字节,只能表示0/1,不能表示别的数字
void connect(ll u, ll v, ll typ){
    // cout << u << '-' << v << endl;
    to[u].emplace_back(v);
    to[v].emplace_back(u);
    ty[u].emplace_back(typ);
    ty[v].emplace_back(typ);
}
bool fail=0;
void dfs(ll u, ll co){
    vst[u]=co;
    for(ll i=0;i<to[u].size();i++){
        ll v=to[u][i], targ=(ty[u][i]==1?3-co:co);
        if(vst[v]&&vst[v]==targ)continue;
        if(vst[v]&&vst[v]!=targ){
            fail=1;return;
        }
        dfs(v, targ);
    }
}
int main(){
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    cin>> n;
    rep(i, 1, n*(n-1)/2){
        cin >> a[i] >> b[i];
        pos[a[i]][b[i]]=pos[b[i]][a[i]]=i;
    }
    rep(i, 1, n-2){
        rep(j, i+1, n-1){
            rep(k, j+1, n){
                //选择三个数
                ll ti[3]={pos[i][j], pos[j][k], pos[i][k]};
                sort(ti, ti+3);
                if(pos[i][k]==ti[2])connect(ti[0], ti[1], 1);//1表示不等号反向
                if(pos[i][j]==ti[2])connect(ti[0], ti[1], 0);//0表示不等号同
                if(pos[j][k]==ti[2])connect(ti[0], ti[1], 0);
            }
        }
    }
    ll ans=1;
    rep(i, 1, n*(n-1)/2){
        if(vst[i])continue;
        dfs(i, 1);
        ans*=2;ans%=MOD;
    }
    if(fail){
        cout << 0 << endl;
    }else{
        cout << ans << endl;
    }
    return 0;
}
posted @ 2025-06-25 19:45  yanzihe  阅读(8)  评论(0)    收藏  举报