《加 训》

arc165 C

https://atcoder.jp/contests/arc165/tasks/arc165_c

不难发现答案只有1条边和两条边的可能性

分类讨论一下

两条边一定是一个中心点,然后连出两条边,且中心点的颜色和连出去的两个点的颜色不相同

所以我们对每个点,先跑出$mn + mn_1$,即最小加次小

然后把它们丢进去和原本的边权进行从小到大排序,一条条取。

对于每一条被枚举到的边来说,如果它是一个一条边的情况,那么一定有两边颜色不同。也就是动态二分图判定。利用并查集判断一下即可。

如果遇到一条边,它的加入导致这个图不是二分图了,就输出这条边。

如果遇到一个两个边的情况,就直接输出

因为构成这个边的两个结构一定被枚举到过,并且填好色了,如果修改这些颜色只会导致答案变成更加劣化的一条边。

#include <bits/stdc++.h>
#define pa pair<int,int>
const int mx = 2e5;
using namespace std;
int N,M;
vector<pa> G[mx + 10];
int fa[2 * mx + 10];
int Getfa(int x){
    if (x == fa[x]) return x;
    else return fa[x] = Getfa(fa[x]);
}
struct Edge{
    int x,y,w;
};
struct Edge1{
    int Type;
    int rt,x,y,w;
};
int temp(Edge a, Edge b){
    return (a.w < b.w);
}
Edge w[mx + 10];
vector<Edge1> as;
int temp1(Edge1 a, Edge1 b){
    if (a.w == b.w) return a.Type < b.Type;
    return (a.w < b.w);
}
int sp[mx + 10];
int main(){
    cin >> N >> M;
    for (int i = 1 ; i <= 2 * N + 2; i ++){
        fa[i] = i;
    }
    for (int i = 1 ; i <= M ; i ++){
        int x,y,z;
        cin >> x >> y >> z;
        G[x].push_back({y,z});
        G[y].push_back({x,z});
        w[i].x = x;
        w[i].y = y;
        w[i].w = z;
    }
    if (M == 1){
        cout << w[1].w;
        return 0;
    }
    int ans = 1e9+7;
    for (int i = 1 ; i <= N ; i ++){
        int mn = 1e9+7,mn1 = 1e9+7,x = 0 , y = 0;
        for (auto v : G[i]){
            int d = v.second;
            if (d < mn) mn1 = mn,y = x,x= i,mn = d;
            else if (d < mn1) mn1 = d,y = i;
        }
        if (x != 0 && y != 0)
            as.push_back({1,i,x,y,mn+mn1});
    }
    sort(w+1,w+M+1,temp);
    for (int i = 1 ; i <= M ; i ++){
        as.push_back({0,0,w[i].x,w[i].y,w[i].w});
    }
    sort(as.begin(),as.end(),temp1);
    //cout << as.size() << endl;
    for (auto v : as){
        int tp = v.Type,rt = v.rt,x=v.x,y=v.y,w=v.w;
        int fx = Getfa(x),fy = Getfa(y),fxx = Getfa(x + N),fyy = Getfa(y + N);
            if (tp == 0){
            if (fx != fyy) fa[fx] = fyy;
            if (fy != fxx) fa[fy] = fxx;
            int a = Getfa(x) , b = Getfa(x + N);
            if (a == b){
                cout << w;
                return 0;
            }
            a = Getfa(y) , b = Getfa(y + N);
            if (a == b){
                cout << w << endl;
                return 0;
            }
        }
        else{
            cout << w;
            return 0;
        }
    }
    return 0;
}

arc165 D

https://atcoder.jp/contests/arc165/tasks/arc165_d

考虑我们限制的实质,实际上可以把限制表达到一个图上。

<a,c>表示$a$要比$c$小

那么我们对于初始的限制跑一下tarjan,找出其中的环。

对于环上的点,它们势必要全部相等。

因此,对于scc之间的约束,显然我们不需要再考虑它们(实际上就是找到了一个能比较大小的位置)

对于scc内,即两个位置的字符相等

而对于字典序来说,两个字符相等的话,我们就可以往下再走一个位,这样会产生新的约束。

我们把新约束重新建图,并且建图的时候,把相同权值缩成一个点,这样会产生一个新的图。而新的图里面如果存在一个scc,就说明这个新scc内的块的值要全部相等,合并一下即可。

不断重复这个过程,实际上最多会做n次(因为每次至少合并进一个点),而每次是O(n)的,所以是$O(n^2)$。

退出的条件是没有新的点被合并

 

#include<bits/stdc++.h>
#define maxn 3011
using namespace std;
vector<int>G[maxn],new_G[maxn];
stack<int>s;
int n,m;
int fa[maxn];
int dfn[maxn],vis[maxn],viss[maxn],low[maxn],color[maxn],colornum,cnt,in[maxn];
int Getfa(int x){
    if (x == fa[x]) return x;
    else return fa[x] = Getfa(fa[x]);
}
bool toposort(int n){
    queue<int>q;
    int res=0;
    for(int i=1;i<=n;i++)  //n  节点的总数
        if(in[i]==0) q.push(i),res++;  //将入度为0的点入队列
    if(res>1) return 0;
    vector<int>ans;   //ans 为拓扑序列,这个题目里没用到 
    while(!q.empty())
    {    //cout<<233<<endl;
        int p=q.front(); q.pop(); // 选一个入度为0的点,出队列
        //cout<<p<<endl;
        ans.push_back(p);
        res=0;
        for(int i=0;i<new_G[p].size();i++)
        {    
            int y=new_G[p][i];
            //if(p==2)cout<<G[p][i]<<endl;
            in[y]--;
            if(in[y]==0)
                q.push(y),res++;
                //cout<<p<<" "<<y<<endl;  
        }
        if(res>1) return 0;
    }
    return 1;
}

void tarjan(int x)
{
    dfn[x]=low[x]=++cnt;
    s.push(x);
    vis[x]=true;
    for(int i=0;i<G[x].size();i++)
    {
        int q=G[x][i];
        if (!dfn[q])
        {
            tarjan(q);
            low[x]=min(low[x],low[q]);
        }
        else if (vis[q]) low[x]=min(low[x],dfn[q]);
    }
    if (low[x]==dfn[x])
    {
        colornum++;
        int t;
        do{
            t=s.top();s.pop();
            color[t]=colornum;
            vis[t]=false;
        }while(t!=x);
    }
}
void init(int n){
        colornum=0,cnt=0;
        memset(dfn,0,sizeof(dfn));
        memset(low,0,sizeof(low));
        memset(vis,0,sizeof(vis));
        memset(color,0,sizeof(color));
        memset(in,0,sizeof(in));
        for(int i=1;i<=n;i++) G[i].clear();
}
struct Node{
    int x,x1,y,y1;
}a[maxn];
int N,M;
int repre[maxn];
int main(){
    cin >> N >> M;
    for (int i = 1 ; i <= N ; i ++)
        fa[i] = i;
    for (int i = 1 ; i <= M ; i ++){
        cin >> a[i].x >> a[i].x1 >> a[i].y >> a[i].y1;
    }
    bool flag = false;
    while (1){
        init(N);
        for (int i = 1 ; i <= N ; i ++)
            G[i].clear();
        for (int i = 1 ; i <= M ; i ++){
            while (Getfa(a[i].x) == Getfa(a[i].y) && a[i].x <= a[i].x1 && a[i].y <= a[i].y1){
                a[i].x ++;
                a[i].y ++;
            }
            if (a[i].y == a[i].y1 + 1){
                flag = true;
                break;
            }
            if (a[i].x == a[i].x1 + 1) {
                continue;
            }
            int fx = Getfa(a[i].x),fy = Getfa(a[i].y);
            G[fx].push_back(fy);
        }
        if (flag){
            cout << "No";
            return 0;
        }
        memset(repre,0,sizeof(repre));
        for (int i = 1 ; i <= N ; i ++)
            if (!dfn[Getfa(i)])
                tarjan(Getfa(i));
        bool flag1 = false;
        for (int i = 1 ; i <= N ; i ++){
            int fx = Getfa(i);
            int yy = color[fx];
            if (!repre[yy]){
                repre[yy] = fx;
            }
            int fy = Getfa(repre[yy]);
            if (fx != fy) {
                fa[fx] = fy;
                flag1 = true;
            }
        }
        if (!flag1) break;
    }
    cout << "Yes";
    return 0;
}

 ARC164

C


https://atcoder.jp/contests/arc164/tasks/arc164_c

其实a和b都是考虑当前操作对答案的贡献。

记录c = down[i] - face[i] ,显然小A每次会希望取c最小的那个,因为这样能让B获得的额外贡献最少。

而小B则也是取C最小的那个,因为它这么取的话,能获得最大的额外贡献

 

优先队列模拟即可。

#include <bits/stdc++.h>
#define int long long
#define pa pair<int,int>
const int mx = 2e5; 
using namespace std;
int a[mx + 10],b[mx + 10];
priority_queue<pa,vector<pa>, greater<pa> > mp;
bool flag = false;
signed main(){
    int N;
    cin >> N;
    if (N == 1){
        int x,y;
        cin >> x >> y;
        cout << y << endl;
        return 0;
    }
    bool flag2 = false,flag1 = false;
    int mx = -1e9,mn = 1e9,mx1 = -1e9;
    int ans = 0;
    for (int i = 1 ; i <= N ; i ++){
        cin >> a[i] >> b[i];
        mp.push({b[i]-a[i],0});
        //cout << b[i] - a[i] << "????"<<endl;
        ans = ans + a[i];
    }
    for (int i = 1 ; i <= N ; i ++){
        pa tp = mp.top();
        mp.pop();
        mp.push({-tp.first,1^tp.second});
        tp = mp.top();
        if (tp.second == 1){
            //cout << i << " " << "qwq" << endl;
            ans = ans - tp.first;
            //cout << tp.first << endl;
        }
        mp.pop();
    }
    cout << ans;
    return 0;
}

 ARC161D

实际上每次删一个点的限制应该是最强的

考虑删除一个点后,密度如何变化

假设这个点的度是a

$D' = \frac{nD-a}{n-1} < D$

解出来发现$a > D$

于是问题就变成了:

每个点的度都$>D$,是否存在一个图的边数恰好为$ND$

考虑一下握手定理,现在我们假设每个点的度为$D+K$

有$(D+K) * N / 2 = ND$

解一下这个式子会发现$K$有一个解,就是$D$

于是我们就可以这么构造:

把每个点$i$都连向$i+j$,$1<= j <= D$

这样一定出来每个点的度都是$2*D$

考虑这个图什么时候会寄

$2*D >= N$的时候,因为会出现度数不够分配的情况。

于是结束了(

#include <bits/stdc++.h>
using namespace std;
int main(){
    int N,D;
    cin >> N >> D;
    if (2 * D >= N){
        cout << "No\n";
        return 0;
    }
    cout << "Yes\n";
    for (int i = 0 ; i < N ; i ++){
        for (int j = i + 1 ; j <= i + D ; j ++){
            int y = j;
            if (y >= N) y = (y % N);
            cout << i + 1 << " " << y + 1 << endl;
        }
    }
    return 0;
} 

 

posted @ 2023-10-18 02:36  si_nian  阅读(69)  评论(0)    收藏  举报