题解 P9697【[GDCPC2023] Canvas】
好题。
后面的操作会覆盖前面的操作,这东西不好处理,我们不妨时光倒流,将问题转化为一个位置一旦被填了数,就再也不会变了。如果解决了这个问题,只需将操作序列倒过来,就得到了原问题的解。
显然,所有 的操作会最先被执行,所有 的操作会最后被执行。只需要给所有 的操作确定顺序即可,不妨设所有这样的操作都满足 。
一旦操作 被执行,则所有形如 的操作都可以被执行。在这一系列操作涉及到的位置中,除了 被填成了 ,其他位置都被填成了 。我们称 为启动操作,称启动操作引发出的一系列操作为连锁操作。这种传递关系使我们想到图论建模。
对于每个形如 的操作,连接一条从 指向 的有向边。将图缩点,显然我们只会在入度为 的强连通分量中选择恰好一条边作为启动操作,这条边的启动会导致它所在的强连通分量以及在它下游的所有强连通分量中的边成为连锁操作。我们不需要付出任何代价就可以把入度不为 的强连通分量全部填成 ,而对于入度为 的强连通分量,我们最多只会将一个点填为 。
关于启动操作的选择还有一点要注意。如果一个入度为 的强连通分量中,已经有点被形如 的操作钦定填 了,则以这个点的一条出边作为启动操作,就可以少将一个点填为 (这就是上面说“最多”的原因)。
时间复杂度 。
// Problem: T368340 [GDCPC2023] H-Canvas
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/T368340?contestId=135929
// Memory Limit: 1 MB
// Time Limit: 3000 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(x > y) x = y;}
template<typename T> void chkmax(T& x, T y) {if(x < y) x = y;}
const int N = 5e5 + 5;
int T, n, m, l[N], x[N], r[N], y[N], deg[N], vis[N], res[N], two[N];
vector<tuple<int, int>> e[N];
vector<int> ans, aft;
int dfn[N], low[N], tms, ins[N], col[N], scc;
stack<int> stk;
void tarjan(int u) {
dfn[u] = low[u] = ++tms;
stk.push(u);
ins[u] = 1;
for(auto [v, id] : e[u]) {
if(!dfn[v]) {
tarjan(v);
chkmin(low[u], low[v]);
}
else if(ins[v]) {
chkmin(low[u], dfn[v]);
}
}
if(dfn[u] == low[u]) {
++scc;
while(true) {
int v = stk.top();
stk.pop();
ins[v] = 0;
col[v] = scc;
if(u == v) break;
}
}
}
void dfs(int u) {
vis[u] = 1;
for(auto [v, id] : e[u]) {
ans.push_back(id);
if(!vis[v]) dfs(v);
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
for(cin >> T; T; --T) {
cin >> n >> m;
rep(i, 1, m) {
cin >> l[i] >> x[i] >> r[i] >> y[i];
if(x[i] == 2 && y[i] == 2) {
ans.push_back(i);
two[l[i]] = two[r[i]] = 1;
}
else if(x[i] == 1 && y[i] == 1) {
aft.push_back(i);
}
else {
if(x[i] > y[i]) {
swap(l[i], r[i]);
swap(x[i], y[i]);
}
e[l[i]].emplace_back(r[i], i);
}
}
rep(u, 1, n) if(!dfn[u]) tarjan(u);
rep(u, 1, n) for(auto [v, id] : e[u]) if(col[u] != col[v]) ++deg[col[v]];
rep(u, 1, n) if(!deg[col[u]] && !vis[u] && two[u]) dfs(u);
rep(u, 1, n) if(!deg[col[u]] && !vis[u]) dfs(u);
for(int i : aft) ans.push_back(i);
reverse(ans.begin(), ans.end());
for(int i : ans) {
res[l[i]] = x[i];
res[r[i]] = y[i];
}
cout << accumulate(res + 1, res + 1 + n, 0) << endl;
for(int i : ans) cout << i << " "; cout << endl;
rep(i, 1, n) {
deg[i] = vis[i] = res[i] = dfn[i] = low[i] = col[i] = two[i] = 0;
e[i].clear();
}
ans.clear();
aft.clear();
tms = scc = 0;
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现