20230608 怎么全是二分图
施工中 未完待续
P4568 [JLOI2011] 飞行路线
经典分层图最短路板子
code:
#include<bits/stdc++.h>
using namespace std ;
const int N = 5000721 ;
int head[N] , nxt[N] , to[N] , v[N] , cnt ;
int dis[N] , vis[N] ;
int n , m , k , s , t ;
void cmb( int x , int y , int z ){
to[++cnt] = y ;
v[cnt] = z ;
nxt[cnt] = head[x] ;
head[x] = cnt ;
}
struct node{
int id , dis ;
friend bool operator < ( node a , node b ){
return a.dis > b.dis ;
}
};
priority_queue<node> q ;
void dijkstra( int s ){
dis[s] = 0 ;
q.push( (node){s,0} ) ;
while( !q.empty() ){
int prs = q.top().id ;
q.pop() ;
if( vis[prs] )
continue ;
vis[prs] = 1 ;
for( int i = head[prs] ; i ; i = nxt[i] ){
int y = to[i] ;
if( dis[prs] + v[i] < dis[y] ){
dis[y] = dis[prs] + v[i] ;
q.push( (node){y,dis[y]} ) ;
}
}
}
}
int main () {
scanf("%d%d%d" ,&n ,&m ,&k ) ;
scanf("%d%d" ,&s ,&t ) ;
for( int i = 1 ; i <= m ; ++i ){
int x , y , z ;
scanf("%d%d%d" ,&x ,&y ,&z ) ;
for( int j = 0 ; j <= k ; ++j ){
cmb( x + n * j , y + n * j , z ) ;
cmb( y + n * j , x + n * j , z ) ;
}
for( int j = 0 ; j < k ; ++j ){
cmb( x + n * j , y + n * ( j + 1 ) , 0 ) ;
cmb( y + n * j , x + n * ( j + 1 ) , 0 ) ;
}
}
if( k >= m ){
printf("0") ;
exit(0) ;
}
memset( dis , 0x3f , sizeof(dis) ) ;
dijkstra(s) ;
printf("%d" ,dis[t + n * k] ) ;
return 0 ;
}
CF449B Jzzhu and Cities
充分诠释了什么叫题目中的每一个条件都不是白给的
首先到 1 的最短路 想到反跑 dijktra 结果这题双向边直接省了
然后发现所有的特殊边都是从 1 往外连的 那么如果删了这条边会对最短路有影响 当且仅当这条边是 1 到这个点的唯一最短路
所以我们在原最短路的基础上加一个计数 当 dis 相同时直接判最短路数量 注意删完之后最短路数量--
code:
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e6 + 0721;
const int M = 1e5 + 0721;
ll dis[M];
int head[M], nxt[N], to[N], val[N], cnt;
ll sum[M];
int query[M], qdis[M];
bool vis[M];
int n, m, k;
struct node {
ll id, dis;
friend bool operator < (node a, node b) {
return a.dis > b.dis;
}
};
priority_queue<node> q;
inline void cmb(int x, int y, int z) {
to[++cnt] = y;
val[cnt] = z;
nxt[cnt] = head[x];
head[x] = cnt;
}
void dijkstra(int x) {
memset(dis, 0x3f, sizeof dis );
dis[x] = 0;
q.push((node){ (ll)x, dis[x] });
while (!q.empty()) {
int now = q.top().id;
q.pop();
if (vis[now]) continue;
vis[now] = 1;
for (int i = head[now]; i; i = nxt[i]) {
int y = to[i];
if (dis[y] > dis[now] + val[i]) {
dis[y] = dis[now] + val[i];
sum[y] = 1;
q.push((node) { (ll)y, dis[y] });
} else if (dis[y] == dis[now] + val[i]) ++sum[y];
}
}
}
int main() {
scanf("%d%d%d", &n, &m, &k);
for (int i = 1; i <= m; ++i) {
int x, y, z;
scanf("%d%d%d", &x, &y, &z);
cmb(y, x, z);
cmb(x, y, z);
}
for (int i = 1; i <= k; ++i) {
int u, y;
scanf("%d%d", &u, &y);
cmb(1, u, y);
cmb(u, 1, y);
query[i] = u;
qdis[i] = y;
}
dijkstra(1);
int ans = 0;
for (int i = 1; i <= k; ++i) {
if (dis[query[i]] < qdis[i]) ++ans;
else if (dis[query[i]] == qdis[i] && sum[query[i]] > 1) ++ans, --sum[query[i]];
}
printf("%d",ans);
return 0;
}
CF666B World Tour
首先需要一个全源最短路 但是 floyd 显然是会 t 掉的
发现这题边权都为 1 直接 bfs 即可
然后肯定是不能枚举四个点的 直接 t 飞
考虑
然后发现 b c 两个点 可以直接涵盖到所需要的三段最短路
但是这样的话三段最短路都需要 O(1) 查询
首先 b -> c 肯定是 O(1) 的
那么对于剩下两段呢
想到预处理出到达 b 和从 c 出发路径最长的点
但是由于这题是 a -> b -> c -> d 要考虑点重复的问题
首先是不太可能与自身重复的 那么 a 就有同时与 c d 重复的可能
所以对于最长的点 我们要取前三个防止重复
另外注意 一定要保证联通才能更新答案
code:
#include <bits/stdc++.h>
using namespace std;
const int N = 0x0d00;
const int M = 1e4 + 0721;
int head[N], to[M], nxt[M], cnt;
int dis[N][N];
int q[M], hh, tt;
bool vis[N];
int n, m;
int ans[4];
int maxx;
struct node {
int id[4];
int maxn[4];
} too[N], from[N];
inline void cmb(int x, int y) {
to[++cnt] = y;
nxt[cnt] = head[x];
head[x] = cnt;
}
void bfs(int x) {
memset(vis, 0, sizeof vis );
dis[x][x] = 0;
hh = 1, tt = 0;
q[++tt] = x;
while (hh <= tt) {
int now = q[hh];
++hh;
vis[now] = 1;
for (int i = head[now]; i; i = nxt[i]) {
int y = to[i];
if (vis[y]) continue;
if (dis[x][y] == 0)
dis[x][y] = dis[x][now] + 1;
if (!vis[y])
q[++tt] = y;
}
}
}
void init(int x) {
for (int i = 1; i <= n; ++i) {
if (dis[i][x] > too[x].maxn[1]) {
too[x].maxn[1] = dis[i][x];
too[x].id[1] = i;
}
}
for (int i = 1; i <= n; ++i) {
if (too[x].id[1] == i) continue;
if (dis[i][x] > too[x].maxn[2]) {
too[x].maxn[2] = dis[i][x];
too[x].id[2] = i;
}
}
for (int i = 1; i <= n; ++i) {
if (too[x].id[1] == i || too[x].id[2] == i) continue;
if (dis[i][x] > too[x].maxn[3]) {
too[x].maxn[3] = dis[i][x];
too[x].id[3] = i;
}
}
for (int i = 1; i <= n; ++i) {
if (dis[x][i] > from[x].maxn[1]) {
from[x].maxn[1] = dis[x][i];
from[x].id[1] = i;
}
}
for (int i = 1; i <= n; ++i) {
if (from[x].id[1] == i) continue;
if (dis[x][i] > from[x].maxn[2]) {
from[x].maxn[2] = dis[x][i];
from[x].id[2] = i;
}
}
for (int i = 1; i <= n; ++i) {
if (from[x].id[1] == i || from[x].id[2] == i) continue;
if (dis[x][i] > from[x].maxn[3]) {
from[x].maxn[3] = dis[x][i];
from[x].id[3] = i;
}
}
}
void work() {
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= n; ++j) {
if (i == j) continue;
for (int kk = 1; kk <= 3; ++kk) {
int u = too[i].id[kk];
if (u == i || u == j) continue;
for (int kkk = 1; kkk <= 3; ++kkk) {
int v = from[j].id[kkk];
if (v == i || v == j || v == u) continue;
if (dis[u][i] == 0 || dis[i][j] == 0 || dis[j][v] == 0) continue;
if (maxx < dis[u][i] + dis[i][j] + dis[j][v]) {
maxx = dis[u][i] + dis[i][j] + dis[j][v];
ans[0] = u, ans[1] = i, ans[2] = j, ans[3] = v;
}
// cout << i << " " << j << " " << u << " " << v << endl;
}
}
}
}
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; ++i) {
int x, y;
scanf("%d%d", &x, &y);
cmb(x, y);
}
for (int i = 1; i <= n; ++i) bfs(i);
for (int i = 1; i <= n; ++i) init(i);
work();
// for (int i = 1; i <= n; ++i) {
// for (int j = 1; j <= n; ++j) cout << dis[i][j] << " ";
// cout << endl;
// }
// cout << maxx << endl;
printf("%d %d %d %d",ans[0],ans[1],ans[2],ans[3]);
return 0;
}
CF1268B Domino for Young
黑白染色 显然答案是
显然当
我们讨论
显然这个时候 多出来的格子如果都是一个颜色 就能取到上界了
然后对于一个杨表 如果有相邻两行长度一样的 就删去最右边那列
如果有相邻两列高度一样的 就删去最上面那行
那么如果删到不能删的情况 一定长这样
显然我们可以让这个玩意只空出来白格(即比如高度为偶数的列 直接密铺 对于高度为奇数的列 空出最上面一格)
进而的证
code:
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 3e5 + 0721;
int a[N], n;
ll ans1, ans2;
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
for (int i = 1; i <= n; ++i) {
if (i & 1) {
ans1 += a[i] / 2 + a[i] % 2;
ans2 += a[i] / 2;
} else {
ans1 += a[i] / 2;
ans2 += a[i] / 2 + a[i] % 2;
}
}
printf("%lld",min(ans1, ans2));
return 0;
}
CF623A Graph and String
观察一下 只有 ac 之间没有连边
那么对于原图的补图 所有的边一定都只是 ac 之间的边
然后就能想到二分图
没有连边的点就是 b
但是此时注意 原图要求所有的 ac 之间都没有连边
那么在补图中一定要满足对于每个 a 一定与每个 c 都有连边 而这个是二分图不一定满足的
所以我们要判构造完的字符串是否满足所有 ac 之间都不连边
code:
#include <bits/stdc++.h>
using namespace std;
const int N = 521;
int _mp[N][N], mp[N][N];
int cl[N];
bool vis[N];
int n, m;
int q[N], h, t;
void bfs(int x) {
h = 1, t= 0;
q[++t] = x;
while (h <= t) {
int now = q[h];
++h;
for (int i = 1; i <= n; ++i) {
if (!mp[now][i]) continue;
if (cl[i] == -1) {
cl[i] = !cl[now];
q[++t] = i;
} else if (cl[i] == cl[now]) {
printf("No");
exit(0);
}
}
}
}
int main() {
memset(cl, -1, sizeof cl );
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; ++i) {
int x, y;
scanf("%d%d", &x, &y);
_mp[x][y] = _mp[y][x] = 1;
}
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= n; ++j) {
// cout << i << " " << j << " " << _mp[i][j] << endl;
if (_mp[i][j] == 0 && i != j) {
mp[i][j] = 1;
// cout << i << " " << j << endl;
vis[i] = vis[j] = 1;
}
}
}
for (int i = 1; i <= n; ++i) {
if (!vis[i]) continue;
if (cl[i] != -1) continue;
cl[i] = 0;
bfs(i);
}
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= n; ++j) {
if (_mp[i][j] && cl[i] + cl[j] == 1) {
printf("No");
return 0;
}
}
}
printf("Yes\n");
for (int i = 1; i <= n; ++i) {
if (cl[i] == -1) printf("b");
else if (cl[i] == 0) printf("a");
else printf("c");
}
return 0;
}
CF1093D Beautiful Graph
先找性质 发现 1 和 3 其实是等价的 只不过是两个不同的选择
那我们考虑每条边必须连一奇一偶 显然是个二分图
那么对于两个集合 都有可能作为奇数点的集合
假设奇数集合大小为
当两个集合大小不同时 方案数就是
注意到图不一定保证联通 所以我们将每个连通块的方案数相乘就行
code:
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 6e5 + 0721;
const int mod = 998244353;
int head[N], nxt[N], to[N], _cnt;
int cl[N];
int q[N], h, t;
int T;
int n, m;
int cnt[2];
ll ans;
bool fail;
inline void cmb(int x, int y) {
to[++_cnt] = y;
nxt[_cnt] = head[x];
head[x] = _cnt;
}
void bfs(int x) {
cnt[0] = cnt[1] = 0;
cl[x] = 0;
++cnt[0];
h = 1, t = 0;
q[++t] = x;
while (h <= t) {
int now = q[h];
++h;
for (int i = head[now]; i; i = nxt[i]) {
int y = to[i];
if (cl[y] == -1) {
cl[y] = !cl[now];
++cnt[cl[y]];
q[++t] = y;
} else if (cl[y] == cl[now]) {
fail = 1;
return;
}
}
}
}
int main(){
scanf("%d", &T);
while (T--) {
ans = 1;
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i) cl[i] = -1;
for (int i = 1; i <= n; ++i) head[i] = 0;
_cnt = 0;
fail = 0;
for (int i = 1; i <= m; ++i) {
int x, y;
scanf("%d%d", &x, &y);
cmb(x, y);
cmb(y, x);
}
for (int i = 1; i <= n; ++i) {
if (fail) break;
if (cl[i] != -1) continue;
bfs(i);
if (fail) break;
ll tmp1 = 1, tmp2 = 1;
for (int j = 1; j <= cnt[0]; ++j) {
tmp1 <<= 1;
tmp1 %= mod;
}
for (int j = 1; j <= cnt[1]; ++j) {
tmp2 <<= 1;
tmp2 %= mod;
}
tmp1 = (tmp1 + tmp2) % mod;
ans = ans * tmp1 % mod;
}
if (fail) printf("0\n");
else printf("%lld\n",ans);
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】