Circle:思维难度并没有很大,难点主要还是细节的特判上。
先转化题意:构造一张无自环的有向图,使得每个点的出度、入度均为 \(1\),并且部分点走 \(l\) 步后必须回到自己。
因为每个点的出度、入度均为 \(1\),所以这张图一定由若干个有向环组成。
然后发现,在不能走自环的情况下 \(u\) 走 \(l\) 步回到 \(u\) 的条件相当于 \(u\) 所处的环的长度 \(\bm{len}\) 必须是 \(\bm l\) 的一个约数,否则一定无法返回自己。
同时可以发现,如果一个环的长度不是质数,那么这个环一定可以接着拆分。因为拆出来的环长一定都是原环的约数。由此可知选长度为质数的环一定不劣。
于是问题被转化为了,能否从 \(l\) 的质因子中选择若干个数作为环长,使得环长总和大于等于 \(1\) 的个数,且小于等于 \(n\)。我们只需要把全部的 \(1\) 都放入这个环中,把剩下的 \(0\) 随便编一个环即可。
这显然是一个背包问题,因为 \(\bm l\) 的质因子最多只有 \(\bm{\log l}\) 个,所以时间复杂度 \(O(n\log l)\),注意要记录转移的前驱。最后遍历可能的环长总和,进行构造即可。
注意一些边界条件:
- \(S\) 全部为 \(0\) 时,随便构造一个排列即可。
- \(l = 0\),随便构造一个排列即可。
- 环长总和为 \(n - 1\) 的时候,剩下的一个节点因为无法连自环,所以不是个合法的方案。
#include <bits/stdc++.h>
#define fi first
#define se second
#define eb(x) emplace_back(x)
#define pb(x) push_back(x)
#define lc(x) (tr[x].ls)
#define rc(x) (tr[x].rs)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
using pi = pair<int, int>;
const int N = 500005;
ll n, m, a[N], dp[N], pre[N], c[N], ans[N];
bitset<N> vis;
int prm[N], cnt, b[N], bcnt, ccnt;
void init()
{vis[1] = vis[0] = 1;for(int i = 2; i < N; i++){if(vis[i] == 0)prm[++cnt] = i;for(int j = 1; j <= cnt && i * prm[j] < N; j++){vis[i * prm[j]] = 1;if(i % prm[j] == 0) break;}}
}
void solve()
{cin >> n >> m;int num1 = 0;bcnt = 0;for(int i = 1; i <= n && i <= m; i++)if(vis[i] == 0 && m % i == 0)b[++bcnt] = i;for(int i = 1; i <= n; i++){char c;cin >> c;a[i] = c - '0';num1 += a[i];}if(num1 == 0 || m == 0){for(int i = 1; i < n; i++) cout << i + 1 << " ";cout << 1 << "\n";return;}memset(dp, 0, sizeof(dp));dp[0] = 1;for(int i = 0; i <= n; i++){if(dp[i] == 0) continue;for(int j = 1; j <= bcnt; j++){int v = i + b[j];if(v > n) continue;if(dp[v] == 0){dp[v] = 1;pre[v] = b[j];}}}vector<int> res;for(int i = num1; i <= n; i++){if(i == n - 1) continue;if(dp[i]){int now = i;while(now){res.push_back(pre[now]);now -= pre[now];}break;}}if(res.size() == 0){cout << "-1\n";return;}ccnt = 0;for(int i = 1; i <= n; i++)if(a[i] == 1)c[++ccnt] = i;for(int i = 1; i <= n; i++)if(a[i] == 0)c[++ccnt] = i;int cur = 0;for(auto itm : res){for(int i = 1; i < itm; i++){++cur;ans[c[cur]] = c[cur + 1];}++cur;ans[c[cur]] = c[cur - itm + 1];}if(cur != n){for(int i = cur + 1; i < n; i++)ans[c[i]] = c[i + 1];ans[c[n]] = c[cur + 1];}for(int i = 1; i <= n; i++) cout << ans[i] << " ";cout << "\n";
}
int main()
{ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);init();int t;cin >> t;while(t--) solve();return 0;
}