首页 > 其他分享 >题解 [ABC371G] Lexicographically Smallest Permutation(中文/English)

题解 [ABC371G] Lexicographically Smallest Permutation(中文/English)

时间:2024-09-14 22:14:17浏览次数:9  
标签:return int 题解 ABC371G English operator Modint friend mod

本题解提供英文版,位于示例代码之后。

English version of this editorial is provided after the sample code.

官方题解竟然用 Python 来算高精度 lcm,我来提供一个可以避免一切大整数运算的方法。

考察 \(u\gets P_u\) 这张图的每个置换环。为了使答案字典序最小,显然需要从前往后贪心地取每个位置。

假设贪心地填完了前 \(k-1\) 个元素,第 \(k\) 个元素尚未确定取值(这意味着它是所在置换环中编号最小的元素)。找出其所在的置换环,设长度为 \(len\)。设最终操作次数为 \(W\),则已经填完的置换环会对 \(W\) 的取值做出一些形如 \(W\equiv R\pmod{Q}\) 的规定,使用一个数组 \(req\) 对所有 \(Q\) 记录下对应的 \(R\)。枚举当前置换环对 \(W\) 取值的限制 \(W\equiv\Delta\pmod{len}\),判断这一限制与已有限制是否有矛盾,在所有没有矛盾的 \(\Delta\) 中取使得 \(A_k\) 最小化的一个即可。

时间复杂度 \(O(n\log n)\)。

示例代码 / Sample code:

// Problem: G - Lexicographically Smallest Permutation
// Contest: AtCoder - AtCoder Beginner Contest 371
// URL: https://atcoder.jp/contests/abc371/tasks/abc371_g
// Memory Limit: 1024 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

//By: OIer rui_er
#include <bits/stdc++.h>
#define rep(x, y, z) for(int x = (y); x <= (z); ++x)
#define per(x, y, z) for(int x = (y); x >= (z); --x)
#define debug(format...) fprintf(stderr, format)
#define fileIO(s) do {freopen(s".in", "r", stdin); freopen(s".out", "w", stdout);} while(false)
#define endl '\n'
using namespace std;
typedef long long ll;

mt19937 rnd(std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::system_clock::now().time_since_epoch()).count());
int randint(int L, int R) {
    uniform_int_distribution<int> dist(L, R);
    return dist(rnd);
}

template<typename T> void chkmin(T& x, T y) {if(y < x) x = y;}
template<typename T> void chkmax(T& x, T y) {if(x < y) x = y;}

template<int mod>
inline unsigned int down(unsigned int x) {
	return x >= mod ? x - mod : x;
}

template<int mod>
struct Modint {
	unsigned int x;
	Modint() = default;
	Modint(unsigned int x) : x(x) {}
	friend istream& operator>>(istream& in, Modint& a) {return in >> a.x;}
	friend ostream& operator<<(ostream& out, Modint a) {return out << a.x;}
	friend Modint operator+(Modint a, Modint b) {return down<mod>(a.x + b.x);}
	friend Modint operator-(Modint a, Modint b) {return down<mod>(a.x - b.x + mod);}
	friend Modint operator*(Modint a, Modint b) {return 1ULL * a.x * b.x % mod;}
	friend Modint operator/(Modint a, Modint b) {return a * ~b;}
	friend Modint operator^(Modint a, int b) {Modint ans = 1; for(; b; b >>= 1, a *= a) if(b & 1) ans *= a; return ans;}
	friend Modint operator~(Modint a) {return a ^ (mod - 2);}
	friend Modint operator-(Modint a) {return down<mod>(mod - a.x);}
	friend Modint& operator+=(Modint& a, Modint b) {return a = a + b;}
	friend Modint& operator-=(Modint& a, Modint b) {return a = a - b;}
	friend Modint& operator*=(Modint& a, Modint b) {return a = a * b;}
	friend Modint& operator/=(Modint& a, Modint b) {return a = a / b;}
	friend Modint& operator^=(Modint& a, int b) {return a = a ^ b;}
	friend Modint& operator++(Modint& a) {return a += 1;}
	friend Modint operator++(Modint& a, int) {Modint x = a; a += 1; return x;}
	friend Modint& operator--(Modint& a) {return a -= 1;}
	friend Modint operator--(Modint& a, int) {Modint x = a; a -= 1; return x;}
	friend bool operator==(Modint a, Modint b) {return a.x == b.x;}
	friend bool operator!=(Modint a, Modint b) {return !(a == b);}
};

const int N = 2e5 + 5;

int n, p[N], a[N], vis[N], req[N], ans[N];
vector<int> divs[N];

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cin >> n;
    rep(i, 1, n) cin >> p[i];
    rep(i, 1, n) cin >> a[i];
    per(i, n, 1) for(int j = i; j <= n; j += i) divs[j].push_back(i);
    rep(i, 1, n) req[i] = -1;
    rep(i, 1, n) {
        if(!vis[i]) {
            vector<int> cyc;
            for(int u = i; !vis[u]; u = p[u]) {
                cyc.push_back(u);
                vis[u] = 1;
            }
            // for(int u : cyc) cout << u << " "; cout << endl;
            int len = cyc.size(), steps = -1;
            rep(s, 0, len - 1) {
                bool ok = true;
                for(int d : divs[len]) {
                    if(req[d] != -1 && s % d != req[d]) {
                        ok = false;
                        break;
                    }
                }
                if(ok) {
                    if(steps == -1 || a[cyc[s]] < a[cyc[steps]]) {
                        steps = s;
                    }
                }
            }
            rep(i, 0, len - 1) ans[cyc[i]] = a[cyc[(i + steps) % len]];
            for(int d : divs[len]) req[d] = steps % d;
        }
    }
    rep(i, 1, n) cout << ans[i] << " \n"[i == n];
    return 0;
}

The official solution surprisingly uses Python to compute LCM of big integers. Let me present an algorithm that avoids all big integer computations.

Consider each cycle in the permutation graph \(u\gets P_u\). To make the answer lexicographically smallest, it is obvious that we need to greedily pick each position from left to right.

Suppose the first \(k-1\) elements have been greedily filled, and the value of the \(k\)-th element has yet to be determined (which means it is the smallest-indexed element in its cycle). Find the cycle it belongs to and let its length be \(len\). Let the final number of operations be \(W\), and the cycles that have already been filled impose some conditions on \(W\) in the form of \(W\equiv R\pmod{Q}\). Use an array \(req\) to record the corresponding \(R\) for each \(Q\). Enumerate the current cycle's constraint on \(W\), i.e., \(W\equiv\Delta\pmod{len}\), and check if this constraint conflicts with the existing ones. Among all the \(\Delta\) that do not conflict, pick the one that minimizes \(A_k\).

The time complexity is \(O(n\log n)\).

标签:return,int,题解,ABC371G,English,operator,Modint,friend,mod
From: https://www.cnblogs.com/ruierqwq/p/18414756/abc371g

相关文章

  • P9891 [ICPC2018 Qingdao R] Repair the Artwork 题解
    所求即为选择的区间恰好包含所有\(a_i=2\)的位置的方案数。设所有\(a_i=2\)的位置\(i\)组成集合\(S\),考虑容斥被选中的位置是\(S\)的子集的方案数\(g(S)\)。设\(T\)为\(S\)的子集,\(T\)的贡献\(f(T)\)为:选中的位置都在\(T\)的子集中的方案数乘容斥系数\(......
  • [题解]CF542A Place Your Ad Here
    思路首先因为电视台比广告多一个信息,所以通常来说枚举电视台是更有前途的。因此枚举每一个电视台,考虑所有广告的贡献。对于一个电视台,\(c_i\)是定值,也就是找到所有广告与电视台所表示区间交得最多的那一个。假设枚举的电视台控制了\([L,R]\)区间,则广告\([l,r]\)会有三种方......
  • Luogu P10179 水影若深蓝 题解 [ 绿 ] [ 并查集 ] [ 构造 ]
    水影若深蓝:挺好的一道并查集构造题。观察不难发现“距离为\(2\)”这个条件我们可以通过黑白染色实现,我们把他们的中转点染成与他们相反的颜色,把这两个距离为\(2\)的点染成相同颜色。这个染色问题就很并查集。于是我们用并查集维护相同的种类。显然,当图上只有一个连通块的......
  • 请求HTTP链接的图片等资源被自动变成HTTPS请求的问题解决(顺便可以解决图片防盗链)
    文章目录问题现象问题根本原因常规问题解决办法非Chrome浏览器:控制CSP协议对HTML页面处理nginx配置中处理Chrome浏览器本地处理方式Chrome浏览器通用解决办法(服务器端无法控制新版Chrome这种行为,只能曲线救国--顺便可以解决图片防盗链)网页的网站使用http域名代理服务......
  • P5985 [PA2019] Muzyka pop 题解
    P5985[PA2019]Muzykapop题解是蛮有意思的一道题。\(n\le200\),第一感觉是区间dp,但是又不好设出状态。考虑\(b\)单调递增的过程中的性质,考虑后得到\(b\)的最高含\(1\)的位一定是单调不降的,于是我们考虑将最高的含\(1\)的位设入状态。第一反应是设\(f_{i,j}\)表示......
  • 力扣494-目标和(Java详细题解)
    题目链接:494.目标和-力扣(LeetCode)前情提要:因为本人最近都来刷dp类的题目所以该题就默认用dp方法来做。最近刚学完01背包,所以现在的题解都是以01背包问题为基础再来写的。如果大家不懂01背包的话,建议可以去学一学,01背包问题可以说是背包问题的基础。如果大家感兴趣,......
  • 《Civilization: Beyond Earth》启动失败?《文明太空》msvcr110.dll问题解决方案大放送
    当您在尝试启动《文明:太空》(Civilization:BeyondEarth)时遇到“找不到msvcr110.dll”或“msvcr110.dll缺失”的错误提示,这意味着您的计算机上缺少或损坏了MicrosoftVisualC++2012运行时库中的一个关键组件。msvcr110.dll是许多基于C++开发的游戏和应用程序正常运行所必需的......
  • Thinkpad C13 Yoga Linux声卡驱动问题解决方案等
    ChromebookMorphius:ThinkpadC13Yoga与linux这本子做工真不错,全铝触摸屏,360翻折,还有usi笔槽。续航也很长,能连续用8个小时。安装linuxcoolstar.org,请。如果运行那个脚本有困难(网络问题),你可以尝试打开那个脚本看看biosrom是从哪里下载的。手动下载后用脚本里的flashrom那......
  • 【洛谷 P5266】【深基17.例6】学籍管理 题解(映射+分支)
    【深基17.例6】学籍管理题目描述您要设计一个学籍管理系统,最开始学籍数据是空的,然后该系统能够支持下面的操作(不超过条):插入与修改,格式1NAMESCORE:在系统中插入姓名为NAME(由字母和数字组成不超过20个字符的字符串,区分大小写),分数为()的学生。如果已经有同名的学生则更新这名......
  • [EGOI2024] Infinite Race题解
    [EGOI2024]InfiniteRace妙妙题。我们设\(cnt[x]\)表示当Anika和第\(x\)位选手相遇时Anika至少几次经过终点线。设定初始状态\(cnt[x]=-1\)表示两种等价的情况:Anika还未和第\(x\)位选手相遇过Anika被第\(x\)位选手超越了因此只剩下Anika超越了第\(x\)位选手......