20231116

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,一直过不去

下午调了两个小时没调出来,还是不知道自己错哪里,人已经开始恶心了

晚上补完了c,dp思路完全对,sin互周角相反的东西没想到。dp[n] [p]就表示前n个点,取了p个点的最大值,将数组扩一倍来处理环,循环枚举起点(真不错的方法,比我赛时的好些)。然后再枚举最后一段区间来dp转移。

我们要取n+1个点,就是起点取两遍,这样就可以处理不过圆心的情况

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define double long double
#define Acode ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define int long long
const int N = 2e5 + 10;
double a[N];
int n, p;
double dp[200][200];
const double pi = acos(-1.0);
double get(double x, double y)
{
    double suf = y - x;
    if (suf < 0)
        suf += 360;
    suf = suf / 180.0;
    return (1.0 / 2) * sin(suf * pi);
}

void solve()
{
    cin >> n >> p;
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];
        a[i + n] = a[i];
    }
    double ans = 0;
    for (int i = 1; i <= n; i++)
    {
        memset(dp, -0x3f, sizeof dp);
        dp[1][1] = 0;
        for (int j = i + 1; j <= i + n; j++)
        {
            for (int k = 2; k <= p + 1; k++)
            {
                for (int t = i; t <= j - 1; t++)
                {
                    dp[j - i + 1][k] = max(dp[j - i + 1][k], dp[t - i + 1][k - 1] + get(a[t], a[j]));
                }
            }
        }
        ans = max(ans, dp[n + 1][p + 1]);
    }
    cout << fixed << setprecision(8) << 1e6 * ans << endl;
}

signed main()
{
    Acode;
    int T = 1;
    // cin >> T;
    while (T--)
    {
        solve();
    }
    return 0;
}

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;
}
posted @ 2023-11-17 00:49  ヤ﹎句号悠灬  阅读(39)  评论(0)    收藏  举报