Gravatar
yrtiop
积分:2101
提交:309 / 808

显然有一个 $\mathcal O(m\log m)$ 的 LCT 做法:将边按照 $a$ 升序排序,依次加入,动态维护最小生成树,这是 LCT 的经典应用。

那我不会 LCT,又该怎么通过这个题呢?

令人疑惑的是,榜上有大量的神秘复杂度 SPFA 做法,还有评论区中的二分套二分。这显然是错的,事实上不会 LCT 也有对应的常规手段处理。

秉持着 "直接做就好了" 的信念来处理,考虑二分答案转化为判定问题,设二分值为 $mid$。我们要求 $a_{\max} + b_{\max} \le mid$,不好利用判定的优势,于是转化一下,令 $c = mid - b$,那么就是 $a_{\max}\le c_{\min}$。

这个限制可以改写成 $\forall (a, c), a\le c_{\min}\le c$。此时就比较明晰了,使用线段树分治 + 可撤销并查集维护每个 $c_{\min}$ 对应的边,判断是否能形成一棵生成树,如果可行便 check 成功,如果不存在这样的 $c$ 则 check 失败。

时间复杂度 $\mathcal O(m\log^2 m\log n)$。代码也比较好写。

冷静下来想想,这个做法其实是有一些道理的(接下来是自己的碎碎念,并不一定很有意义)。

由 [SDOI2008] 洞穴勘测 我们知道,对于简单的,只含边的加入 / 撤销,判断联通状态,可离线的 LCT 问题,我们可以采用线段树分治 + 可撤销并查集来维护。结合 LCT 维护最小生成树的背景,猜想可以通过一些转化,让条件变成形如 "一条边有少数个存在的范围,判断是否能形成一棵生成树" 这样的问题。

一开始的一些推断告诉我们,直接做不存在好的数学性质,并且只能 LCT 维护,进而想到尝试二分答案转化为相对容易的判定。通过常见手段对限制进行改写,从而使得判定条件中的常数 $mid$ 带来更多作用。结合前面的感觉,得到正确的解法。

COGS 的 G++9.30 评测机还没有好。。。代码就放在 这里 了。由于卡常,代码并不是很可读。



题目1685  [NOI 2014]魔法森林      5      评论
2024-02-10 15:58:03    
Gravatar
yrtiop
积分:2101
提交:309 / 808

考虑 $k = 1$ 的情况,不妨设 $a$ 降序,此时显然是呈 $\dots, a_4, a_2, a_1, a_3, a_5, \dots$ 状。

然后考虑 $k > 1$,根据数论结论我们知道环长是 $n / \gcd(n, k)$,对于一种环长 $d$,和初始 $k = 1$ 的状态有 $\mathcal O(n / d)$ 处修改,于是记忆化处理,时间复杂度根据调和级数为 $\mathcal O(n\ln n)$。


题目3374  [NOI Online 2020 1st]最小环(民间数据)      5      评论
2024-01-29 15:32:38    
Gravatar
小金
积分:1723
提交:289 / 548

本题求的是图中的环的平均值的最大值,可以发现这其实就是一个01分数规划,数据处理稍微有点麻烦

用map实现车次字符串与整数序号的对应,以每条线路的起始车次为边的起点,终止车次为终点,线路上的所有车次的速度和为权值建图,每次二分答案mid,在新图(即原图的每条边的权值都减去mid)中用spfa找正环。如果存在正环,l=mid;如果不存在,r=mid

最后一组数据会被卡,需要玄学优化,如果spfa中循环次数超过100000,直接返回存在正环的结果(当所有点的入队次数超过2n时,我们就认为图中有很大可能是存在负环/正环的)



题目3640  [CH 6B12]最优高铁环 AAAAAAAA      7      评论
2024-01-17 20:08:31    
Gravatar
健康铀
积分:1063
提交:212 / 557

分享一个贪心做法

把每个点所被控制对象的个数称为入度,则入度为0的点必不能加入集合,那么他们所控制的点加入集合就是最优方案。如x的入度为0且控制y,则y优先加入集合,y所控制的点z的入度减一。当z的入度减少为0,那么z所控制的点f同样加入集合,再查询f所控制的点入度减一.....以此类推,所有点的优先加入方案就可以推出来(除了环)

环就更简单了,既然互相控制那么直接取一半的点就可以了


题目3496  [IOI 2008]创世纪      6      评论
2024-01-09 20:36:44    
Gravatar
Lixj
积分:237
提交:88 / 241

这道题如果你的思维很局限,可以使用queue,先创建一个队列,正常输入,在queue中进行运算,代码如下:


#include<bits/stdc++.h>
using namespace std;
queue<int> q;
int a,n,cnt;//cnt代表位置
int main(){
    freopen("ysf.in","r",stdin);
    freopen("ysf.out","w",stdout);
    cin>>a>>n;
    for(int i=1;i<=a;i++)
        q.push(i);//q.push()输入
    while(!q.empty()){//如果为空退出循环
        cnt++;
        if(cnt==n){//如果到了出圈的人
            cout<<q.front()<<" ";//q.front()出圈
            q.pop();//删除出圈的人,q.pop()删除
            cnt=0;//重新计数
        }
         else { //如果这人不用出圈,重新刷新一下
             int x = q.front();
             q.pop();
             q.push(x);
         }
    }
    return 0;
}



题目3888  约瑟夫问题      3      评论
2024-01-08 21:30:02    
Gravatar
yrtiop
积分:2101
提交:309 / 808

原神大王系列怎么没有这道题?

考虑把 $(\sum a, \sum b)$ 作为一个生成树的坐标,那么这个点的贡献就是它和原点围成的面积。

这样的问题有一个经典结论是有可能成为答案的点一定构成一个下凸包。

然后有这么一个算法叫 QuickHull。具体地,先求出 $\sum a$ 最小的点 $A$ 和 $\sum b$ 最小的点 $B$,然后找一个离 $A, B$ 最远的点 $C$。$A, B$ 可以直接最小生成树求。

这个条件可以看成 $S_{\triangle ABC}$ 最大化,用向量叉乘的式子画一画发现要最小化 $(x_B - x_A)y_C + (y_A - y_B)x_C$ 加上一堆常量,那令一条边的权值是 $b\times (x_B - x_A) + a\times (y_A - y_B)$ 跑最小生成树即可。当求出来的这个值 $\ge 0$ 的时候说明直线左下方没有点了,停止;否则递归处理 $(A, C), (C, B)$ 之间的情况。

据说这样凸包上点的期望个数是 $\mathcal O((na)^{\frac{2}{3}})$ 个。


题目2891  [BOI 2011] Timeismoney      6      评论
2024-01-07 11:58:04