CF891C Envy

思路

  • 有一个性质(我做之前也不知道, 但是现在告诉你)
    • 对于一个图 G, 将他的所有边权 w 的边以任意顺序加入最小生成树中, 图的联通性一定
      • “ 任意顺序 ” : 像 Kruskal 的流程一样, 按边权从小到大, 但是边的编号(输入时的)任意顺序
      • “ 联通性一定 ” : 比如无论那种顺序加边, 将权 w 的权加入后, 1 和 2 一定是联通的, 4 和 5 一定不联通
  • 将询问的边按照边权为第一关键字, 询问标号为第二关键字排序
    • 边权相同的、同一次询问内的边放在一起处理
  • 根据上面的性质, 当前边权变为 w 时, 所有 <w 的边都可以加入并查集(此操作无需撤销)
    • 对于同一次询问的边:
      • 逐条加入, 判断是否合法
        • 如果加入这条边之前, 该边的两端点 u, v 就在同一个集合中, 那么非法
      • 同一次询问的边处理完之后, 将上述的加边操作全部撤销, 进入下一个询问
  • 这样的话我们不难发现我们把权值相同且不处在当前询问中的边后加入了,而处于当前询问中的会优先加入,那么加入是合法的可能性会更大

代码

  • 如果你 WA on TEST 48
    • 检查一下代码中加注释的地方
    • 处理同一次询问的时候有没有判断边权是否改变 !
# include <bits/stdc++.h>
# define int long long
using namespace std;
const int N = 1.5e6 + 10;
int n, m, q;
int u, v, w, k;
int ans[N];
struct Edge{
int u, v, w;
bool operator < (const Edge &p)const{
return w < p.w;
}
}e[N], ee[N];
int cnte;
struct Add{
int qid, eid;
bool operator < (const Add &p)const{
if(e[eid].w == e[p.eid].w){
return qid < p.qid;
}else{
return e[eid].w < e[p.eid].w;
}
}
}qq[N];
int cntq;
struct UFS{
int s[N], si[N], cnt;
int st[N], tp;
void Insert(){
++cnt;
s[cnt] = cnt;
si[cnt] = 1;
}
int Find(int x){
return (x == s[x] ? x : Find(s[x]));
}
void Merge(int x, int y, int ba){
x = Find(x);
y = Find(y);
if(x == y){
return;
}
if(si[x] < si[y]){
swap(x, y);
}
s[y] = x;
si[x] += si[y];
if(ba == 1){
st[++tp] = y;
}
}
void Delete(int x){
while(tp > x){
int k = st[tp];
si[s[k]] -= si[k];
s[k] = k;
tp--;
}
}
}s;
signed main(){
// freopen("1.in", "r", stdin);
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
cin >> n >> m;
for(int i = 1; i <= m; i++){
cin >> u >> v >> w;
e[i].u = ee[i].u = u;
e[i].v = ee[i].v = v;
e[i].w = ee[i].w = w;
}
sort(ee + 1, ee + 1 + m);
cnte = 1;
cin >> q;
for(int i = 1; i <= q; i++){
cin >> k;
ans[i] = 1;
for(int j = 1; j <= k; j++){
cin >> qq[++cntq].eid;
qq[cntq].qid = i;
}
}
sort(qq + 1, qq + 1 + cntq);
int up = cntq;
cntq = 1;
for(int i = 1; i <= n; i++){
s.Insert();
}
while(cntq <= up){
w = e[qq[cntq].eid].w;
int qid = qq[cntq].qid;
if(cntq > 1 && w != e[qq[cntq - 1].eid].w || qid != qq[cntq - 1].qid){
s.Delete(0);
}
while(cnte <= m && ee[cnte].w < w){
s.Merge(ee[cnte].u, ee[cnte].v, 0ll);
cnte++;
}
while(cntq <= up && qq[cntq].qid == qid && e[qq[cntq].eid].w == w){ // 这里这里
if(s.Find(e[qq[cntq].eid].u) == s.Find(e[qq[cntq].eid].v)){
ans[qid] = 0;
}
if(ans[qid]){
s.Merge(e[qq[cntq].eid].u, e[qq[cntq].eid].v, 1ll);
}
cntq++;
}
}
for(int i = 1; i <= q; i++){
if(ans[i]){
cout << "YES\n";
}else{
cout << "NO\n";
}
}
}
posted on   Bubble_e  阅读(12)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话



点击右上角即可分享
微信分享提示