day 9 模拟赛 5

模拟赛 5

T1 图染色

给定一张 n 个点 m 条边的无向图,你需要为每个顶点染色,使得:

  • 对于任意一种颜色,不存在只由它构成的环;
  • 对于任意两种在图中出现过的颜色,存在只由这两种颜色构成的环。

你可以只使用一种颜色,而且染上同一种颜色的所有节点不需要联通。

要求线性。

首先不存在环时,全都染一种颜色即可。-------------------------------20分

先考虑一种绝对正确的染色方法:

任意顺序枚举所有点,一个点不可以染成颜色 x 当且仅当这个点是一个环上的一点,且这个环的其它点的颜色都是 x。这样如果染色 x 就违反了第一个条件。

尝试把这个点染上颜色 1,2,3,...,直到合法为止,如果没有颜色可以,就要新增一个颜色。

新增的颜色为何满足条件 2?因为我们增加一个颜色的时候,必须满足这个点在之前所有颜色的环中,所有对于新颜色和任意的旧颜色,刚好有一个环。-------------------------------40分

考虑如何实现,我们用一个并查集,维护不同颜色的所有连通块,那么一个点所在环的其他颜色颜色相同,等价于这个点与一个颜色的连通块有两条边相连,每当决定一个点的颜色后,遍历这个点的所有出边,合并并查集即可。忽略并查集复杂度,复杂度 \(O(n+m)\)。-------------------------------100分

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int t,n,m;
vector<int>g[N];
int c[N];
map<pair<int,int>,int>cnt;
bool c_ok[N];
vector<int>fa[N],sz[N];
int find(int col,int x){
    if(fa[col][x]==x)return x;
    return fa[col][x]=find(col,fa[col][x]);
}
void merge(int col,int x,int y){
    int rx=find(col,x),ry=find(col,y);
    if(rx==ry)return;
    if(sz[col][rx]<sz[col][ry])swap(rx,ry);
    fa[col][ry]=rx;
    sz[col][rx]+=sz[col][ry];
}
void init_color(int col){
    fa[col].resize(n+1);
    sz[col].resize(n+1);
    for(int i=1;i<=n;i++){
        fa[col][i]=i;
        sz[col][i]=1;
    }
}
int main(){
    freopen("color.in","r",stdin);
    freopen("color.out","w",stdout);
    cin>>t;
    while(t--){
        cin>>n>>m;
        for(int i=1;i<=n;i++){
            g[i].clear();
            c[i]=0;
			fa[i].clear();
            sz[i].clear();
        }
        cnt.clear();
        for(int i=1;i<=m;i++){
            int u,v;cin>>u>>v;
            g[u].push_back(v);
            g[v].push_back(u);
        }
        int co_cnt=0;
        for(int u=1;u<=n;u++){
            for(int i=1;i<=co_cnt;i++)c_ok[i]=1;
            vector<pair<int,int>>added;
            for(int v:g[u]){
                if(c[v]==0)continue;
                int rt=find(c[v],v);
                pair<int,int>p={c[v],rt};
                if(!cnt[p])cnt[p]=0;
                cnt[p]++;
                added.push_back(p);
                if(cnt[p]>=2)c_ok[c[v]]=0;
            }
            int choose=0;
            for(int i=1;i<=co_cnt;i++){
                if(c_ok[i]){
                    choose=i;
                    break;
                }
            }
            if(!choose){
                co_cnt++;
                init_color(co_cnt);
                c[u]=co_cnt;
            }else{
                c[u]=choose;
                for(int v:g[u]){
                    if(c[v]==choose){
                        merge(choose,u,v);
                    }
                }
            }
            for(auto p:added)cnt[p]--;
        }
        cout<<co_cnt<<"\n";
        for(int i=1;i<=n;i++)cout<<c[i]<<" ";
        cout<<"\n";
    }
    return 0;
}

T2 林波舞

有 n 根横杆,第 i 根横杆一开始的高度为 \(a_i\) 单位,移速为每秒 \(v_i\) 单位(正数代表向上,负数代表向下)。杆子的活动范围为 0~\(10^9\) 单位,当它尝试向这个范围之外移动时会停止不动。

你决定将这些横杆按照某种顺序依次摆放在离起点 1~n 单位处,让表演者依次穿过这些横杆,必须匀速。

找出一个尽可能小的数 h 使得:存在一种摆放方式,使得不论表演者选择怎样的前进速度,他穿过横杆时对应横杆高度的最小值都不大于 h。\(n\le10^5\)

不难想到二分答案,考虑如何 check。

部分分给了一些提示:当 \(v_i\ge 0\) 时,显然舞者越慢越好,对于 \(v_i>0\) 的柱子舞者都可以等无限长时间等过去,答案即为所有 \(v_i=0\) 柱子的高度最小值。-------------------------------5分

\(v_i<0\) 时,显然舞者越快越好,所有她在一瞬间移动过去,答案为所有柱子初始高度的最小值。-------------------------------25分

假设我们知道一个柱子的位置,以及高度的最小值 h,那么这可以将舞者的速度锁定在一个范围中。

  • \(v>0\) 时,舞者移动的越慢,走到那里时柱子的高度越高,所以能确定一个速度的最大值。而且把柱子放到位置 1 有机会让舞者通过时高度最低。由 \(a+vt=h\),可得 \(t=\frac{h-a}{v}\),由 \(v_{r}=\frac{s}{t}\)\(v_r=\frac{v}{h-a}\)
  • \(v<0\) 时,舞者移动的越快,走到那里时柱子的高度越高,所以能确定一个速度的最小值。而且把柱子放到位置 n 有机会让舞者通过时高度最低。由 \(a-|v|t=h\),可得 \(t=\frac{h-a}{|v|}\),即 \(t=\frac{h-a}{-v}\),由 \(v_{l}=\frac{s}{t}\)\(v_l=\frac{-vn}{a-h}\)特殊地,如果一个向下移动的杆子的初始高度就小于 h,那么舞者是不可能挑出速度的,直接合法。
  • \(v=0\) 时,如果高度 \(a<h\),那么舞者也是不可能挑出速度的,直接合法。

把所有柱子都放到 1/n 试一次,可以求得范围 \(maxl<v'<maxr\)\(v'\) 是舞者的速度),如果 \(maxl\ge maxr\),覆盖所有实数,说明舞者不能选出一个 \(v'\),让最低高度大于 h。

注意二分的精度问题,时间复杂度 \(O(n\log V)\)。-------------------------------100分

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int t,n;
int a[N],v[N];
bool check(double h){
    double maxl=0,minr=1e18;
    for(int i=1;i<=n;i++){
        if(v[i]>0){
            if(h>a[i])minr=min(minr,(double)v[i]/(h-a[i]));
        }else if(v[i]==0){
            if(a[i]<=h)return 1;
        }else{
            if(a[i]>h)maxl=max(maxl,(double)(-v[i])*n/(a[i]-h));
            else return 1;
        }
    }
    if(maxl>=minr)return 1;
    return 0;
}
int main(){
    freopen("limbo.in","r",stdin);
    freopen("limbo.out","w",stdout);
    cin>>t;
    while(t--){
        cin>>n;
        for(int i=1;i<=n;i++)cin>>a[i]>>v[i];
        double l=0,r=1e9;
        for(int i=1;i<=200;i++){
            double mid=(l+r)/2;
            if(check(mid))r=mid;
            else l=mid;
        }
        printf("%.10f\n",l);
    }
    return 0;
}
posted on 2026-07-16 20:55  郑浩崎  阅读(5)  评论(0)    收藏  举报