2023/11/16
先是周三训练补题
k题,赛时队友写的,讨论是用dfn序来判断返祖边,也是学到了怎么来判断有向图中的返祖边
做法:dfs的时候,我们只看一条链上的点,我们正常赋值dfn序。如果这是出现dfn[v]<dfn[u],那么这条边一定是返祖边。然后我们回溯的时候把u点的dfn序赋为1e9。这样代表这个点被访问过,不用访问,且也不会出现下一次dfn[v]<dfn[u]的情况
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define Acode ios::sync_with_stdio(false), cin.tie(0)
#define int long long
int n, m;
const int N = 4e5 + 10;
vector<pair<int, int>> g[N];
int dfn[N];
int tot;
vector<int> ans;
int vis[N];
void dfs(int u)
{
dfn[u] = ++tot;
for (auto it : g[u])
{
int v = it.first;
int id = it.second;
if (!dfn[v])
{
ans.push_back(id);
dfs(v);
}
else if (dfn[v] > dfn[u])
{
ans.push_back(id);
continue;
}
else if (dfn[v] < dfn[u])
continue;
}
dfn[u] = 1e9;
}
void solve()
{
cin >> n >> m;
for (int i = 1; i <= m; i++)
{
int u, v;
cin >> u >> v;
if (u == v)
continue;
g[u].push_back({v, i});
}
for (int i = 1; i <= n; i++)
{
if (!dfn[i])
dfs(i);
}
for (auto x : ans)
{
vis[x] = 1;
}
cout << "YES\n";
if (ans.size() >= m / 2)
{
cout << ans.size() << endl;
for (auto x : ans)
{
cout << x << " ";
}
}
else
{
cout << m - ans.size() << endl;
for (int i = 1; i <= m; i++)
{
if (!vis[i])
cout << i << " ";
}
}
}
signed main()
{
Acode;
int T = 1;
while (T--)
{
solve();
}
return 0;
}
然后看了题解,发现我们想复杂了,u->v,无非两种情况,u大于v或者v大于u。那么我们对两种情况的边分别建图,那么一定有一张图中的边数会大于一半。(因为题目只要求不出现环就行,其他的都没要求)
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define Acode ios::sync_with_stdio(false), cin.tie(0)
#define int long long
int n, m;
const int N = 4e5 + 10;
vector<int> g1;
vector<int> g2;
void solve()
{
cin >> n >> m;
for (int i = 1; i <= m; i++)
{
int u, v;
cin >> u >> v;
if (u == v)
continue;
if (u > v)
g1.push_back(i);
else
g2.push_back(i);
}
if (g1.size() >= m / 2)
{
cout << "YES\n";
cout << g1.size() << endl;
for (auto x : g1)
{
cout << x << " ";
}
}
else if (g2.size() >= m / 2)
{
cout << "YES\n";
cout << g2.size() << endl;
for (auto x : g2)
{
cout << x << " ";
}
}
else
cout << "NO\n";
}
signed main()
{
Acode;
int T = 1;
while (T--)
{
solve();
}
return 0;
}
B题,计算几何。
一开始不会算多边形面积,队友一句话在圆里,用1/2absin角度,点醒梦中人,刚好题目给的也是角度。
赛时用dp来写,但是wa2,一直过不去
下午调了两个小时没调出来,还是不知道自己错哪里,人已经开始恶心了
1852A - Ntarsis' Set
题目是好题,两个二分要调20分钟,看来还是没想清楚。
思路:对于一个数,我们每次删数后,它位置会前进多少呢,这取决于它当前所在的位置,已经a数组中有几个数小于它当前的位置。 而且这个答案在值域上是有单调性的,过小我们模拟它走完k轮之后,他的位置会为0,过大位置大于1.因为最后的答案,在最后的数组中是第一位,所以一个数最后的位置大于1说明前面还有数。
走k轮,值域上二分答案,每轮二分的得出这一轮要前进多少位置
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define Acode ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define int long long
const int N = 2e5 + 10;
int a[N];
int n, k;
int query(int x)
{
int l = 1, r = n;
while (l <= r)
{
int mid = l + r >> 1;
if (a[mid] <= x) //找有多少数小于等于这个数字,刚好等于的话就减到零,可以判断
l = mid + 1;
else
r = mid - 1;
}
return r;
}
bool check(int mid)
{
for (int i = 1; i <= k; i++)
{
int x = query(mid);
// cerr << "TTTT" << x << endl;
mid -= x;
}
if (mid >= 1)
return true;
else
return false;
}
void solve()
{
cin >> n >> k;
for (int i = 1; i <= n; i++)
cin >> a[i];
if (a[1] != 1)
{
cout << 1 << endl;
return;
}
int l = 1, r = 1e15;
while (l <= r)
{
int mid = l + r >> 1;
if (check(mid))
r = mid - 1;
else
l = mid + 1;
// cerr << mid << " " << check(mid) << endl;
}
cout << l << endl;
}
signed main()
{
Acode;
int T = 1;
cin >> T;
while (T--)
{
solve();
}
return 0;
}
标签:20231116,cout,int,cin,long,dfn,define
From: https://www.cnblogs.com/chenchen336/p/17837455.html