P8819 [CSP-S 2022] 星战 (很厉害的随机化想法)

本题 trick : 随机化,求出度转化成入度

简化下题意
有n个点 m条单向边 每条边有激活和失活两种状态,一共有4中操作
1.失活一条 u->v 的边
2.失活终点是 v 的边
3.激活 u->v 的边
4.激活终点是 v 的边

问你每次修改后 每个点的出度是否都为 1.

50分的做法就是暴力修改,对于 1操作和3操作 都是 可以 o(1)解决,对于 2操作和 4操作需要 o(n)。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

typedef pair<int, int> pir;
const int N = 5e5 + 5;
int out[N];
std::vector<int> g[N];
map<int, int>mp[N];

void solve()
{
    int n, m;
    cin >> n >> m;
    int cnt = 0;
    for(int i = 1; i <= m; i++)
    {
        int u, v;
        cin >> u >> v;
        g[v].push_back(u);
        mp[v][u] = 1;
        out[u]++;
        if(out[u] == 1)cnt++;
        else if(out[u] == 2)cnt--;
    }
    int q;
    cin >> q;
    while(q--)
    {
        int op, u, v;
        cin >> op;
        if(op == 1)
        {
            cin >> u >> v;
            mp[v][u] = 0;
            out[u]--;
            if(out[u] == 0)cnt--;
            else if(out[u] == 1)cnt++;
        }
        else if(op == 2)
        {
            cin >> u;
            for(int v : g[u])
            {
                if(mp[u][v])
                {
                    out[v]--;
                    if(out[v] == 0)cnt--;
                    else if(out[v] == 1)cnt++;
                    mp[u][v] = 0;
                }
            }
        }
        else if(op == 3)
        {
            cin >> u >> v;
            mp[v][u] = 1;
            out[u]++;
            if(out[u] == 1)cnt++;
            else if(out[u] == 2)cnt--;
        }
        else
        {
            cin >> u;
            for(int v : g[u])
            {
                if(mp[u][v] == 0)
                {
                    out[v]++;
                    if(out[v] == 1)cnt++;
                    else if(out[v] == 2)cnt--;
                    mp[u][v] = 1;
                }
            }
        }
        if(cnt == n)cout << "YES\n";
        else cout << "NO\n";
    }
}

int main()
{
    // freopen("data.in", "r", stdin);
    // freopen("data.out", "w", stdout);
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    std::cout.tie(0);

    solve();
}

发现时间复杂度的瓶颈在于 2操作 和 4操作. 这个复杂度在维护出度的时候是很难避免的。

一个比较牛逼的想法就是不维护出度,改成维护入度。

发现如果维护入度,那么对于4个操作都复杂度都是 o(1).
当符合每个点的出度都为 1 时,那么出度 总和就是 n,入度总和也是 n。
入度为 n 只是符合题目的一个 必要条件。
本题就是使用随机化使得这个必要条件无限接近充分必要条件。

具体做法是 对每个点i赋一个随机的权值 w[i].
对于每个点u的入度和就是

r[u]=vuw[v]

现在这个条件 (i=1nr[i])==(i=1nw[i])
就是满足 题目要求的一个 必要条件,但是呢因为随机化,就会无限的接近充分必要条件。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

typedef pair<int, int> pir;
const int N = 5e5 + 5;

void solve()
{
    int n, m;
    cin >> n >> m;
    std::mt19937 rnd(time(0));
    std::vector<ll> w(n + 1);
    ll sum = 0;
    for(int i = 1; i <= n; i++)
    {
        w[i] = rnd();
        sum += w[i];
    }
    std::vector<ll> r(n + 1), g(n + 1);
    ll tot = 0;
    for(int i = 1; i <= m; i++)
    {
        int u, v;
        cin >> u >> v;
        r[v] += w[u];
        tot += w[u];
    }
    for(int i = 1; i <= n; i++)g[i] = r[i];
    int q;
    cin >> q;
    while(q--)
    {
        int op, u, v;
        cin >> op;
        if(op == 1)
        {
            cin >> u >> v;
            r[v] -= w[u];
            tot -= w[u];
        }
        else if(op == 2)
        {
            cin >> u;
            tot -= r[u];
            r[u] = 0;
        }
        else if(op == 3)
        {
            cin >> u >> v;
            r[v] += w[u];
            tot += w[u];
        }
        else
        {
            cin >> u;
            tot += g[u] - r[u];
            r[u] = g[u];
        }
        if(tot == sum)cout << "YES\n";
        else cout << "NO\n";
    }
}

int main()
{
    // freopen("data.in", "r", stdin);
    // freopen("data.out", "w", stdout);
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    std::cout.tie(0);

    solve();
}
/*


*/
posted @   pipipipipi43  阅读(65)  评论(0编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
点击右上角即可分享
微信分享提示