概述
周一 3.22(杂题)
Hongcow Builds A Nation (并查集 + 思维)
其实这道题并不难
只是在比赛中为了快点做出,乱猜是贪心,然后思路错误浪费了大量时间,依然WA
所以比赛中静下心和平时一样想题目才是发挥地最好的
这题就是用并查集处理出几个联通分量,没有被标记的联通分量看作一个大联通分量
然后联通分量内部连边,然后未被标记的大联通分量和被标记联通分量里面个数最多的连边
最后减去原来的边就ok了
其实蛮简单的
#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
const int N = 1e3 + 10;
int f[N], a[N], cnt[N], n, m, k;
int find(int x)
{
if(f[x] == x) return x;
return f[x] = find(f[x]);
}
int main()
{
scanf("%d%d%d", &n, &m, &k);
_for(i, 1, n) f[i] = i;
while(k--)
{
int x; scanf("%d", &x);
a[x] = 1;
}
_for(i, 1, m)
{
int u, v;
scanf("%d%d", &u, &v);
f[find(u)] = find(v);
}
int sum = 0, mx = 0, t = 0;
_for(i, 1, n)
{
if(a[i]) a[find(i)] = 1;
cnt[find(i)]++;
}
_for(i, 1, n)
if(f[i] == i && a[i])
{
sum += cnt[i] * (cnt[i] - 1) / 2;
if(a[i]) mx = max(mx, cnt[i]);
}
_for(i, 1, n)
if(!a[find(i)])
t++;
sum += mx * t + t * (t - 1) / 2;
printf("%dn", sum - m);
return 0;
}
Stairs and Elevators(二分查找)
这道题考试的时候都没读题,其实蛮简单的,二分一下就完事了
注意到这道题给的范围很大到1e8
所以给的坐标直接二分一下找到相邻的位置就好了
找到相邻的位置无非就四种可能,四种可能里面取最优就好了
注意可能并不需要走楼梯和电梯,当x相同的时候,要特判一下,我因为这个WA了一次
还有总是重复的量可以用变成存一下,代码会简洁很多,也容易调试
#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
const int N = 1e5 + 10;
int n, m, l[N], e[N], cl, ce, v;
int main()
{
scanf("%d%d%d%d%d", &n, &m, &cl, &ce, &v);
_for(i, 1, cl) scanf("%d", &l[i]);
_for(i, 1, ce) scanf("%d", &e[i]);
int q; scanf("%d", &q);
while(q--)
{
int ans = 1e9, x1, y1, x2, y2;
scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
if(y1 > y2) swap(x1, x2), swap(y1, y2);
if(x1 == x2)
{
printf("%dn", abs(y1 - y2));
continue;
}
int dx = abs(x1 - x2), dy = abs(y1 - y2);
int now = lower_bound(l + 1, l + cl + 1, y1) - l;
if(now != cl + 1) ans = min(ans, dx + dy + 2 * max(l[now] - y2, 0));
if(now - 1 >= 1) ans = min(ans, dx + dy + 2 * abs(y1 - l[now - 1]));
now = lower_bound(e + 1, e + ce + 1, y1) - e;
dx = (dx + v - 1) / v;
if(now != ce + 1) ans = min(ans, dx + dy + 2 * max(e[now] - y2, 0));
if(now - 1 >= 1) ans = min(ans, dx + dy + 2 * abs(y1 - e[now - 1]));
printf("%dn", ans);
}
return 0;
}
D. Timetable(分组背包 + 暴力)
这道题真的一言难尽
考试的时候一读完题就知道是分组背包
要预处理一下每一天翘i节课最多能少掉多少时间
结果我没怎么想清楚,感觉是贪心,然后就写了贪心
比赛时一定要和平时一样想清楚,静下心。不要随便贪心,这道题的贪心是错误的
赛后发现贪心是错误的,如果暴力求这个,复杂度理论上是500的三次方,会超时
然后我就想什么方法优化,然后一直想不出
然而最后发现不会超时,可能是因为1.25e8比较极限,没超得太远,而且数据也没那么强
我在这浪费了很多时间,一直以为会超时。有时候到1e8比较极限的时候也可以试一下,说不定数据水
#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
const int N = 500 + 10;
struct node{ int w, v; };
vector<node> ve[N];
int f[N], L[N], R[N];
int n, m, k, sum;
int main()
{
scanf("%d%d%d", &n, &m, &k);
_for(j, 1, n)
{
int t = 0, cnt = 0, l;
_for(i, 1, m)
{
int x; scanf("%1d", &x);
if(!x) continue;
if(!t) l = i;
else L[++cnt] = i - t;
t = i;
}
if(!t) continue;
sum += t - l + 1;
_for(i, 1, cnt) R[i] = L[cnt - i + 1];
_for(i, 1, cnt) L[i] += L[i - 1], R[i] += R[i - 1];
int w = 0, mx = 0;
_for(p, 1, min(cnt, k))
{
mx = 0;
_for(i, 0, p)
mx = max(mx, L[i] + R[p - i]);
ve[j].push_back(node{++w, mx});
}
ve[j].push_back(node{++w, mx + 1});
}
_for(i, 1, n)
for(int j = k; j >= 0; j--)
REP(t, 0, ve[i].size())
{
int w = ve[i][t].w, v = ve[i][t].v;
if(j >= w) f[j] = max(f[j], f[j - w] + v);
}
printf("%dn", sum - f[k]);
return 0;
}
A. Zebras(思维 + set)
比赛时有两种思路,一种是推出规律,找1的个数和0的个数的关系,结果没推出来
第二种就是for很多遍,每一遍都得出一个序列
其实2e5的数据,尽管会删除数据,for很多遍肯定超时的,我当时就是太着急了,有一个思路还没想正确性就开始写,浪费时间又WA
那么正解肯定是for一遍,O(n)或者O(nlogn)
我想了挺久,发现可以边for就边加入各个序列当中,一个1就要找到末尾为0的序列,一个0就要找到末尾为1的序列
一开始遍历一遍末尾符合的序列结果超时
后来反应到可以用两个set来维护末尾为1的序列和末尾为0的序列的集合,每次都是logn
然后就AC了
还是挺有趣的,这个思维我还是过了很久才想到,还是做题太少,继续加油
#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
const int N = 2e5 + 10;
vector<int> ans[N];
set<int> s1, s2; //s1末尾为1 s2末尾为0
int cnt;
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
string s;
cin >> s;
int len = s.size();
int flag = 1;
REP(k, 0, len)
{
if(s[k] == '0')
{
if(s1.empty())
{
ans[++cnt].push_back(k + 1);
s2.insert(cnt);
}
else
{
int t = *s1.begin(); //begin()是迭代器,看作指针
ans[t].push_back(k + 1);
s1.erase(t); s2.insert(t);
}
}
else
{
if(s2.empty()) { flag = 0; break; }
else
{
int t = *s2.begin();
ans[t].push_back(k + 1);
s2.erase(t); s1.insert(t);
}
}
}
_for(i, 1, cnt)
if(ans[i].size() % 2 == 0)
{
flag = 0;
break;
}
if(!flag) puts("-1");
else
{
printf("%dn", cnt);
_for(i, 1, cnt)
{
printf("%d ", ans[i].size());
for(auto x: ans[i])
printf("%d ", x);
puts("");
}
}
return 0;
}
今天一口气补了四道题,都是独立做出,很爽
其实还是有实力做出来cf 1500到2000的题目的,但是考试时发挥不出来。
考试时应该静下心想题,想清楚,像平时一样,这是我要改进的
周二 3.23 (杂题 + 欧拉回路)
AtCoder - agc006_b(构造题)
说实话这道题我是一点思路都没有
感觉挺复杂,因为每三个数都要取一遍
最后看了题解
发现很巧妙,只要中间放上x - 1 x x + 1就可以了
可以推出来这样一直往上,x会一直在中间
没想到这么简单
这种构造题真的挺巧妙的,看似很复杂,却能用一种很简单的方式解决
构造题不要想太复杂,往往解法很简单。不要有心理障碍
#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
const int N = 2e5 + 10;
int ans[N];
int main()
{
int n, x;
scanf("%d%d", &n, &x);
if(x == 1 || x == 2 * n - 1)
{
puts("No");
return 0;
}
ans[n - 1] = x - 1;
ans[n] = x;
ans[n + 1] = x + 1;
int now = 0;
_for(i, 1, 2 * n - 1)
{
if(ans[i]) continue;
now++;
if(now == x - 1) now = x + 2;
ans[i] = now;
}
puts("Yes");
_for(i, 1, 2 * n - 1) printf("%dn", ans[i]);
return 0;
}
P2731 [USACO3.3]骑马修栅栏 Riding the Fences(欧拉回路模板题)
欧拉回路可以很形象地理解为一笔画问题
一笔画就是一条路径,每条边经过且仅经过一次
这样的路叫做欧拉路
如果是一条回路,就交欧拉回路
理解了这个定义,那么一些性质就很容易理解
无向图中,欧拉回路度数都为偶数,欧拉路仅有两个度数为奇数的点
有向图中,欧拉回路入度=出度,欧拉路中一个点入度多1,一个点出度多1,其他入度等于出度
那么这么求呢?
算法的思路是这样的,找到一个环,删除,继续找一个环,然后把两个环拼接成一个环,一直重复下去
看起来复杂,思路很简单,dfs的时候每走过一条边就删除掉
关键点在于在这个点的出边遍历完之后加入栈记录答案,这个过程恰好完成了两个环拼接的过程
注意这里不能边搜边记录答案,要遍历完出边,把这个点加入栈
最后倒序输出
#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
const int N = 500 + 10;
int g[N][N], d[N], m, mx, mi = 1e9;
stack<int> ans;
void dfs(int u)
{
_for(v, mi, mx)
if(g[u][v])
{
g[u][v]--;
g[v][u]--;
dfs(v);
}
ans.push(u);
}
int main()
{
scanf("%d", &m);
while(m--)
{
int u, v;
scanf("%d%d", &u, &v);
g[u][v]++; g[v][u]++;
d[u]++; d[v]++;
mi = min(mi, min(u, v));
mx = max(mx, max(u, v));
}
int st = mi;
_for(i, mi, mx)
if(d[i] & 1)
{
st = i;
break;
}
dfs(st);
while(!ans.empty())
{
printf("%dn", ans.top());
ans.pop();
}
return 0;
}
周三 3.24 (欧拉回路)
先再明确一下定义
欧拉路是每条边只经过一次且经过所有顶点,即一笔画经过所有点
是否存在欧拉路或欧拉回路的判定:
有两个条件
一个是 图是联通的,有向边则视为无向边(称为基图),可以用并查集维护,最后为一个联通分量
一个是度数的关系,这个是很容易推出来的
如果要求具体的路径才dfs
「一本通 3.7 例 2」单词游戏(建模 + 欧拉路判定)
这道题搞了我好久
建模真的太骚了
第一反应可以是单词为点,然后去连边,找一条路径使得每个点只经过一次
第一连边会超时,第二这种路径是哈密尔顿路,求这个路很麻烦
然后我就灵光一闪,把26个字母看作点,单词看作边
这样每条边只经过一次,这样建模不就刚好是欧拉路的定义吗
然后我又卡住了,单词看作边要怎么连呢
我开始是想末字母有一条出边,首字母有一条入边,然后看度数的关系
可是判顶的时候还要看联通,所以不能光看度数的关系
最后我看了题解,竟然是一个单词首字母向末字母连一条边
太秀了
这样子没那么直接,但是分析一波是对的,这样保留了这个单词的信息
因此根据前面说的那两个条件判定欧拉图就好了
#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
const int N = 1e3 + 10;
int in[30], out[30], f[30], vis[30], n, cnt;
char s[N];
int find(int x)
{
if(f[x] == x) return x;
return f[x] = find(f[x]);
}
int main()
{
int T; scanf("%d", &T);
while(T--)
{
memset(in, 0, sizeof(in));
memset(out, 0, sizeof(out));
memset(vis, 0, sizeof(vis));
_for(i, 0, 25) f[i] = i;
cnt = 0;
scanf("%d", &n);
_for(i, 1, n)
{
scanf("%s", s);
int len = strlen(s);
int u = s[0] - 'a', v = s[len - 1] - 'a';
out[u]++; in[v]++;
if(!vis[u]) vis[u] = 1, cnt++;
if(!vis[v]) vis[v] = 1, cnt++;
if(find(u) != find(v)) cnt--;
f[find(u)] = find(v);
}
int cnt1 = 0, cnt2 = 0, flag = 1;
_for(i, 0, 25)
{
if(in[i] == out[i]) continue;
if(in[i] - out[i] == 1) cnt1++;
else if(out[i] - in[i] == 1) cnt2++;
else { flag = 0; break; }
}
if(cnt != 1 || !flag || !( (!cnt1 && !cnt2) || (cnt1 == 1 && cnt2 ==1))) puts("The door cannot be opened.");
else puts("Ordering is possible.");
}
return 0;
}
也可以度数+dfs,看能否经过所有边
#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
const int N = 1e3 + 10;
int in[30], out[30], n, sum;
struct node{ int v, vis; };
vector<node> g[N];
char s[N];
void dfs(int u)
{
for(auto& x: g[u]) //如果要修改边的值的话,这里要引用
if(!x.vis)
{
x.vis = 1;
dfs(x.v);
sum++;
}
}
int main()
{
int T; scanf("%d", &T);
while(T--)
{
memset(in, 0, sizeof(in));
memset(out, 0, sizeof(out));
_for(i, 0, 25) g[i].clear();
scanf("%d", &n);
_for(i, 1, n)
{
scanf("%s", s);
int len = strlen(s);
int u = s[0] - 'a', v = s[len - 1] - 'a';
g[u].push_back(node{v, 0});
out[u]++; in[v]++;
}
int cnt1 = 0, cnt2 = 0, flag = 1, st = -1;
_for(i, 0, 25)
{
if(in[i] == out[i]) continue;
if(in[i] - out[i] == 1) cnt1++;
else if(out[i] - in[i] == 1) cnt2++, st = i;
else { flag = 0; break; }
}
if(!flag || !( (!cnt1 && !cnt2) || (cnt1 == 1 && cnt2 ==1)))
{
puts("The door cannot be opened.");
continue;
}
if(st == -1)
_for(i, 0, 25)
if(out[i])
{
st = i;
break;
}
sum = 0;
dfs(st);
if(sum == n) puts("Ordering is possible.");
else puts("The door cannot be opened.");
}
return 0;
}
hdu 1878 (判定欧拉图模板题)
#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
const int N = 1e3 + 10;
int deg[N], f[N], n, m;
int find(int x)
{
if(f[x] == x) return x;
return f[x] = find(f[x]);
}
int main()
{
while(scanf("%d", &n) && n)
{
memset(deg, 0, sizeof(deg));
_for(i, 1, n) f[i] = i;
int cnt = n;
scanf("%d", &m);
while(m--)
{
int u, v;
scanf("%d%d", &u, &v);
deg[u]++; deg[v]++;
if(find(u) != find(v)) cnt--;
f[find(u)] = find(v);
}
int flag = 1;
_for(i, 1, n)
if(deg[i] & 1)
{
flag = 0;
break;
}
if(flag && cnt == 1) puts("1");
else puts("0");
}
return 0;
}
周四 3.25 (欧拉回路)
「一本通 3.7 练习 2」Ant Trip(欧拉回路 + 找规律猜结论)
一开始想暴力,就是不断跑欧拉路不断删边,看能跑多少次
然后超时了
后来想到可以通过度数来判定,不用真的去跑欧拉回路,要具体路径才跑
那么有欧拉路或者欧拉回路前提是联通,所以用并查集维护多个联通分量
然后我就任意画图看要几笔才能画完,感觉和奇点个数有关
然后我一直画不出奇点个数为奇数的图,一直是0 2 4 6
我还发现,0 和 2的时候是一次(也即欧拉路判定条件),而4个奇点要2笔,6个奇点要3笔
发现每画一笔会少两个奇点。那么就可以按照这个算个数了
交上去WA了一发,发现忽略了孤立点的情况。
孤立点要删掉,因为其根本没有边,欧拉路是经过每个边有且仅有一次。
因此统计答案时忽略孤立点就好了
#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
const int N = 1e5 + 10;
int deg[N], f[N], cnt[N], n, m;
int find(int x)
{
if(f[x] == x) return x;
return f[x] = find(f[x]);
}
int main()
{
while(~scanf("%d%d", &n, &m))
{
memset(deg, 0, sizeof(deg));
memset(cnt, 0, sizeof(cnt));
_for(i, 1, n) f[i] = i;
while(m--)
{
int u, v;
scanf("%d%d", &u, &v);
deg[u]++; deg[v]++;
f[find(u)] = find(v);
}
_for(i, 1, n)
if(deg[i] & 1)
cnt[find(i)]++;
int ans = 0;
_for(i, 1, n)
if(deg[i] && find(i) == i)
{
if(cnt[i] <= 2) ans++;
else ans += cnt[i] / 2;
}
printf("%dn", ans);
}
return 0;
}
「一本通 3.7 练习 3」John's Trip(欧拉回路模板题)
输出边的顺序就好了
注意两个点
1.dfs之前要先判断一下度数,度数正确是前提。如果度数不对,即使dfs可以得到一个路径,也是不行的
2.如果题目要求输出边的编号,那么删边的时候用一个vis数组来保存是否访问过就好。
3.注意重边和自环。删边用边的编号删去,不要用map<pair<int, int>, bool> 这个无法处理重边的情况
#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
const int N = 50;
const int M = 2000;
struct node{ int v, id; };
vector<node> g[N];
stack<int> ans;
int deg[N], vis[M], n, m, st, cnt;
void dfs(int u)
{
for(node x: g[u])
{
int v = x.v, id = x.id;
if(!vis[id])
{
vis[id] = 1;
dfs(v);
ans.push(id);
}
}
}
int main()
{
int x, y, z;
while(scanf("%d%d", &x, &y) && x && y)
{
cnt = 1;
REP(i, 0, N) g[i].clear();
memset(deg, 0, sizeof(deg));
memset(vis, 0, sizeof(vis));
st = min(x, y);
scanf("%d", &z);
g[x].push_back(node{y, z});
g[y].push_back(node{x, z});
deg[x]++; deg[y]++;
while(scanf("%d%d", &x, &y) && x && y)
{
scanf("%d", &z);
g[x].push_back(node{y, z});
g[y].push_back(node{x, z});
deg[x]++; deg[y]++;
cnt++;
}
int flag = 1;
REP(i, 0, N) //一定先判度数
if(deg[i] & 1)
{
puts("Round trip does not exist.");
flag = 0;
break;
}
if(!flag) continue;
while(!ans.empty()) ans.pop();
dfs(st);
if(ans.size() != cnt) puts("Round trip does not exist.");
else
{
while(!ans.empty())
{
printf("%d", ans.top());
if(ans.size() == 1) puts("");
else printf(" ");
ans.pop();
}
}
}
return 0;
}
周五 3.26 (欧拉回路)
「一本通 3.7 练习 4」太鼓达人(欧拉回路 + 建模)
我又一次跳坑,二进制数看作点,去连边
花了一个小时写完后发现错了。
突然意识这道题和单词接龙的非常像,可以说是一道题
同样,一个二进制数就是一条边,头向尾连,这时头是除去最后一个字符,尾是出去第一个字符
欧拉回路建模的核心在于明确什么是边,边经过且只经过一次
然后还有一些细节看代码
#include<bits/stdc++.h>
#define mp make_pair
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
const int N = 1100;
map<pair<int, int>, bool> vis;
vector<int> g[N];
stack<int> ans;
int k;
int num(string s)
{
int res = 0, len = s.size();
REP(i, 0, len)
{
res <<= 1; //乘2写上面,不然最后多乘一次
res += s[i] - '0';
}
return res;
}
void dfs(int u)
{
for(int v: g[u])
if(!vis[mp(u, v)]) //没有重边可以用map删边.
{
vis[mp(u, v)] = 1;
dfs(v);
}
ans.push(u); //点在循环结束后加入
}
int main()
{
cin >> k;
REP(i, 0, 1 << k)
{
string t = "", t1 = "", t2 = ""; //初始化,类似int x = 0
REP(j, 0, k)
{
if(i & (1 << j)) t += "1";
else t += "0";
}
REP(j, 0, k - 1) t1 += t[j], t2 += t[j + 1]; //注意string用法 是 += 不是 =
g[num(t1)].push_back(num(t2)); //注意是有向边
}
REP(i, 0, 1 << (k - 1)) sort(g[i].begin(), g[i].end()); //排序保证字典序
dfs(0);
cout << (1 << k) << " ";
while(ans.size() > 1) //最后回到起点不用输出
{
int t = ans.top();
if(t & (1 << (k - 2))) cout << "1";
else cout << "0";
ans.pop();
}
return 0;
}
周六 3.27 (欧拉回路)
「一本通 3.7 练习 6」原始生物(欧拉回路 + 建模 + 找规律)
这道题感觉就像是前面几道题的综合
首先建模很套路了,一个遗传密码就是一条边,首向尾连接
然后问题就变成了一个有向图,需要几笔画才能画完
我开始跑欧拉回路,看能跑几次。发现在不存在一笔画完的情况下跑欧拉回路是错误的
这个图存在欧拉回路再去跑才是对的
那么显然就要看每个联通分量的度数了
这里我卡了很久,没什么思路
然后我意识到,一条路径,起点出度-1,终点入度-1,中间的点的出度和入度减去相同的数
也就是除非是作为起点和终点,否则一个点的出度和入度的差值是相同的
那么比如一个点出度比入度大3,那么这个点一定要以它为起点跑3次
所以我们可以统计所有出度大于入度时的差值和(入度大于出度叶可以,一样的),就是要跑的次数
这里有个坑点就是,差值和为0的时候也要跑一次,这时是欧拉回路
知道了跑欧拉路的次数之后,就可以按照题意算出答案了,答案为次数 + 联通分量中边的个数
#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
const int N = 1e3 + 10;
int in[N], out[N], f[N], cnt[N], cnt1[N], n;
int find(int x)
{
if(f[x] == x) return x;
return f[x] = find(f[x]);
}
int main()
{
_for(i, 1, 1000) f[i] = i;
scanf("%d", &n);
while(n--)
{
int l, r;
scanf("%d%d", &l, &r);
out[l]++; in[r]++;
int x = find(l), y = find(r);
if(x == y) cnt[x]++;
else
{
f[x] = y;
cnt[y] += cnt[x] + 1;
}
}
_for(i, 1, 1000)
if(out[i] > in[i])
cnt1[find(i)] += out[i] - in[i];
int ans = 0;
_for(i, 1, 1000)
if((in[i] || out[i]) && f[i] == i)
ans += cnt[i] + max(cnt1[i], 1);
printf("%dn", ans);
return 0;
}
周日 3.28 (欧拉回路)
「一本通 3.7 练习 5」相框(欧拉回路)
这题太骚了,我独立做到80分,有一种情况没考虑到
首先抽象出模型,以度数为核心考虑
烧熔操作可以理解为把度数拆开,例如3拆成1 + 2
那么为了最终成一个环,要分两种情况
如果只有一个联通分量就简单,大于2的全部拆成一堆2,如果为奇数就多留下一个1
为什么要2呢,因为环上每个度数都为2
从度数满足去推为一个环,我手画了一下,发现度数对了一定有其中一种方式可以弄成环。不一定度数对了就是环,但一定有一种方式度数对了就是环
之后合并,把奇数对合并就好
如果有多个联通分量就麻烦
目标是每个联通分量弄成一条链,最后一起合并成一个大环
对于其中的一个联通分量
同样大于2的点拆,最后合并度数为1的点
这里有个特例,就是不存在奇数点的情况
我开始想的是最后再把环拆成链就行了
然后我没想到的是,当烧熔的过程中,如果度数大于2,比如4,是可以拆成2 + 1 + 1的
也就是说,如果有点度数大于2,就不用最后再把环拆为链
就是这个点让我一直80分
此外,如果有一端为0那就新创一个点,把它合并到体系当中。记得相应空间也改一改
#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
const int N = 1e5 + 1e3 + 10;
int deg[N], f[N], num[N], k[N], n, m;
int find(int x)
{
if(f[x] == x) return x;
return f[x] = find(f[x]);
}
int main()
{
REP(i, 1, N) f[i] = i;
scanf("%d%d", &n, &m);
while(m--)
{
int u, v;
scanf("%d%d", &u, &v);
if(!u) deg[u = ++n]++; else deg[u]++;
if(!v) deg[v = ++n]++; else deg[v]++;
f[find(u)] = find(v);
}
int cnt = 0;
_for(i, 1, n)
if(deg[i] && f[i] == i) //联通分量个数
cnt++;
int ans = 0;
if(cnt == 1)
{
int t = 0;
_for(i, 1, n)
{
if(deg[i] > 2) ans++;
if(deg[i] & 1) t++; //奇数个数
}
ans += t / 2; //合并两端
}
else
{
_for(i, 1, n)
{
if(deg[i] > 2) ans++, k[find(i)] = 1;
if(deg[i] & 1) num[find(i)]++; //奇数个数
}
_for(i, 1, n)
if(deg[i] && f[i] == i)
{
if(num[i] == 0) ans += !k[i];
else ans += num[i] / 2 - 1;
}
ans += cnt;
}
printf("%dn", ans);
return 0;
}
现在开始如果队内没有比赛的话,自己弄一套div1 abc,英文要适应
A. Basic Diplomacy(构造)
看似复杂的这种构造题
YES与NO的区分条件往往是很简单,很极端的
这道题只要必须选的人超过m/2就不行,其他时候一定行
行的话,那就把必须选的人先选,然后剩下就选那些选的次数最少的人就行
#include<bits/stdc++.h>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
const int N = 1e5 + 10;
vector<int> g[N];
int cnt[N], n, m;
int main()
{
int T; scanf("%d", &T);
while(T--)
{
memset(cnt, 0, sizeof(cnt));
scanf("%d%d", &n, &m);
int mx = 0;
_for(i, 1, m)
{
g[i].clear();
int k; scanf("%d", &k);
_for(j, 1, k)
{
int x; scanf("%d", &x);
g[i].push_back(x);
if(k == 1) mx = max(mx, ++cnt[x]);
}
}
if(mx > (m + 1) / 2) puts("NO");
else
{
puts("YES");
memset(cnt, 0, sizeof(cnt));
_for(i, 1, m)
if(g[i].size() == 1)
cnt[g[i][0]]++;
_for(i, 1, m)
{
if(g[i].size() == 1) printf("%d ", g[i][0]);
else
{
int ma = 1e9, t;
for(int j: g[i])
if(ma > cnt[j])
{
ma = cnt[j];
t = j;
}
printf("%d ", t);
cnt[t]++;
}
}
puts("");
}
}
return 0;
}
晚上做b做着有点疲惫了
明天把bc做了,每周一套div1 abc
最后
以上就是瘦瘦钢笔为你收集整理的大一下第四周学习笔记周一 3.22(杂题)周二 3.23 (杂题 + 欧拉回路)周三 3.24 (欧拉回路)周四 3.25 (欧拉回路)周五 3.26 (欧拉回路)周六 3.27 (欧拉回路)周日 3.28 (欧拉回路)的全部内容,希望文章能够帮你解决大一下第四周学习笔记周一 3.22(杂题)周二 3.23 (杂题 + 欧拉回路)周三 3.24 (欧拉回路)周四 3.25 (欧拉回路)周五 3.26 (欧拉回路)周六 3.27 (欧拉回路)周日 3.28 (欧拉回路)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复