《加 训》
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; }

浙公网安备 33010602011771号