题解:P13795 [SWERC 2023] Flag performance

· · 题解

前置知识

题意

给你一个 N 的排列,问 K 次操作后让排列单调递增的本质不同方案数有多少,多测。

## Part1 思考一个问题,怎么求最小操作数。 这是一个经典结论,考虑 $i$ 向 $p_i$ 连边,那么最后的答案就是图中所有环大小 $-1$ 之和。 这是显然的,考虑原本的一次交换的 $x,y$ 在同一个环上,此时交换等价于断成两个不同的环,不难证明上述正确。 考虑推广一下,此时如果不在相同环上,是等价与连环的。 那么我们现在就有一个想法,从结束状态转移回给定状态来确定答案数。 ## Part2 一个朴素的想法是记录当前操作数与不同大小环个数状态。 但是 $N\leq 30$,如果朴素的 DP 会是 $N^N$ 难以通过。 事实上,我们的状态数实则很小,打表发现实际状态数只有大概 $6000$ 种,下文钦定其为 $S$。 那么我们就可以用 `map<vector<int>,int>` 来优化状态。 不难发现可以预处理,每次再把重复部分除掉即可。‘ 转移分四种,考虑合并不同大小,合并相同大小,拆分成不同大小,拆分成相同大小。 现在我们就有 $O(N^2\cdot K\cdot S\cdot \log{NS} +TN)$ 的复杂度,可以通过。 ## Part3 如果只有如此其实也就是个蓝。 事实上,这道题的数据范围可以放到 $K\leq 1000$,考虑如下优化。 首先可以将 `map` 换成 `unordered_map`,这需要手写 Hash。 其次我们发现实现过程中转移再 Hash 出来这一步其实是没有必要的,考虑对其预处理,实现精细可以将复杂度降到 $O(N\cdot K\cdot S)$。 ## code: 实现较为简单。 ```cpp #include<bits/stdc++.h> using namespace std; #define int long long const int mod=998244353; const int base=719; const int N=35,K=1e3+5,M=1e4+5; int n,T,p[N]; struct Hash { unsigned long long operator()(const vector<int>&v)const { unsigned long long r=0; for(int x:v) { r=r*base+x; } return r; } }; unordered_map<vector<int>,int,Hash>id; int tr[M][M]; vector<pair<int,int>>G[M]; int tot; void dfs(const vector<int>&now) { for(size_t i=0;i<now.size();i++) { int x=now[i]; for(int j=1;j<x;j++) { if(j>x-j)break; vector<int>nxt=now; nxt.erase(nxt.begin()+i); nxt.push_back(j); nxt.push_back(x-j); sort(nxt.begin(),nxt.end()); if(!id.count(nxt)) { id[nxt]=++tot; dfs(nxt); } tr[id[now]][id[nxt]]+=(j*2==x)?(x/2):x; } } for(size_t i=0;i<now.size();i++) { for(size_t j=i+1;j<now.size();j++) { vector<int>nxt=now; nxt.erase(nxt.begin()+j); nxt.erase(nxt.begin()+i); nxt.push_back(now[i]+now[j]); sort(nxt.begin(),nxt.end()); if(!id.count(nxt)) { id[nxt]=++tot; dfs(nxt); } tr[id[now]][id[nxt]]+=now[i]*now[j]; } } } int f[M][K]; struct DSU { int fa[N],sz[N]; void init() { for(int i=1;i<=n;i++) { fa[i]=i; sz[i]=1; } } int find(int x) { return fa[x]==x?x:fa[x]=find(fa[x]); } void merge(int x,int y) { x=find(x); y=find(y); if(x==y)return; fa[x]=y; sz[y]+=sz[x]; } }dsu; void init() { vector<int>S={n}; id[S]=++tot; dfs(S); for(int u=1;u<=tot;u++) { for(int v=1;v<=tot;v++) { if(tr[u][v]) { G[u].push_back({v,tr[u][v]%mod}); } } } vector<int>all1; for(int i=0;i<n;i++) { all1.push_back(1); } f[id[all1]][0]=1; for(int d=1;d<=1000;d++) { for(int u=1;u<=tot;u++) { for(auto[v,w]:G[u]) { f[u][d]=(f[u][d]+f[v][d-1]*w)%mod; } } } } void solve() { int k; cin>>k; for(int i=1;i<=n;i++) { cin>>p[i]; } dsu.init(); for(int i=1;i<=n;i++) { dsu.merge(i,p[i]); } vector<int>cur; for(int i=1;i<=n;i++) { if(dsu.find(i)==i) { cur.push_back(dsu.sz[i]); } } sort(cur.begin(),cur.end()); cout<<f[id[cur]][k]<<"\n"; } signed main() { // system("fc ex_perm4.out perm.out"); // freopen("ex_perm4.in","r",stdin); // freopen("perm.out","w",stdout); ios::sync_with_stdio(0); cin.tie(0),cout.tie(0); cin>>n>>T; init(); while(T--) solve(); return 0; } ```