CSP-S 19

9.10

125=10+20+75+20

废话

模拟赛能不能类人一点???

因为四道题所以四个样例是吧,你给那小样例有任何作用吗我请问呢?

t1 染色(color)

结论题

赛时想假了(你但凡有个稍微大点的样例我也不会想这么假)

显然素数可分为奇素数和特殊的2 。

若全为奇素数,则两两不同即可构成循环节,即 1 2 1 2 1 2 ...

加上2后只需保证在以上基础上间隔为2也不同即可构成循环节,即 1 2 3 4 1 2 3 4 ...

然后注意小于8时特判(太短了根本不需要到4),这部分你随意做,实在不行打个表也过了。

代码很短。

code:

点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1e4 + 10;
int n, ans[N];
int pri[N], tot;
bool flag[N], ispri[N];
bool has[N];

inline void xxs()
{
    flag[1] = 1;
    ans[1] = 1;
    for (int i = 2; i <= n; ++i)
    {
        ans[i] = 1;
        if (!flag[i])
        {
            ispri[i] = 1;
            pri[++tot] = i;
        }
        for (int j = 1; j <= tot; ++j)
        {
            if (i * pri[j] > n)
                break;
            flag[i * pri[j]] = 1;
            if (i % pri[j] == 0)
                break;
        }
    }
}

signed main()
{
    freopen("color.in", "r", stdin);
    freopen("color.out", "w", stdout);
    ios::sync_with_stdio(0);
    cin.tie(0);
    cin >> n;
    xxs();
    int up = 0, cnt = 1;
    if (n < 8)
    {
        for (int i = 1; i <= n; ++i)
        {
            cnt = 1;
            for (int j = 1; j <= i - 1; ++j)
            {
                if (ispri[i - j])
                    has[ans[j]] = 1;
            }
            while (has[cnt])
                ++cnt;
            memset(has, 0, sizeof(has));
            ans[i] = cnt;
            up = max(ans[i], up);
        }
        cout << up << "\n";
        for (int i = 1; i <= n; i++)
            cout << ans[i] << ' ';
    }
    else//上面那一坨都是赛时假做法,加个else即可10pts-->100pts
    {
        cout << 4 << "\n";
        int cnt = 0;
        for (int i = 1; i <= n; ++i)
        {
            cnt %= 4, ++cnt;
            cout << cnt << ' ';
        }
    }
    return 0;
}

t2 序列(array)

纯屎

题解告诉你这个函数是锯齿状

然后你就爬山或退火去吧。

赛后一堆乱搞神秘做法。

人家退火是为了接近正解,你正解就是这个。

比谁随机数种受此题亲睐吗?

神秘东西直接h的。

不过这东西纯暴力都30pts,但凡优化一下50pts,搞不懂我赛时在打什么。

打了个没有任何正确性的二分(正确性全在能过小样例了,要不怎么说它样例一点用处没有)。

赛后发现过的20pts还是最后特判直接钦定min为0得的😃。

code(h+特判)

点击查看代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 200000 + 10;

int sum;
int a[N];
int T, n, m, k, d;

inline int get(int u)
{
    int ans = u * k + u * m, S = sum * u;
    int les = d - S;
    if (les < 0 || u < 0 || u > n)
        return -1;
    int sum = 0, c = (n - u), res = 0;
    for (int i = 1; i <= m; i++)
    {
        if (sum + a[i] * c <= les)
            sum += a[i] * c, res += c;
        else
        {
            int t = les - sum, l = 0, r = c, mid;
            while (l <= r)
            {
                mid = (l + r) >> 1;
                if (a[i] * mid <= t)
                    l = mid + 1;
                else
                    r = mid - 1;
            }
            res += r;
            break;
        }
    }
    return res + ans;
}

inline int Ans(int lim)
{
    int l = 0, r = lim, mid, res = max(get(l), get(r));
    while (l <= r)
    {
        mid = (l + r) >> 1;
        int L = get(mid - 1), R = get(mid + 1), M = get(mid);
        if (M > L and M > R)
            return M;
        else if (R >= M)
            l = mid + 1;
        else if (L >= M)
            r = mid - 1;
        res = max({L, R, M});
    }
    return res;
}

signed main()
{
    freopen("array.in", "r", stdin);
    freopen("array.out", "w", stdout);
    ios::sync_with_stdio(0);
    cin.tie(0);
    cin >> T;
    while (T--)
    {
        cin >> n >> m >> k >> d;
        sum = 0;
        for (int i = 1; i <= m; i++)
            cin >> a[i], sum += a[i];
        sort(a + 1, a + 1 + m);
        int l = 0, r = n, mid;
        while (l <= r)
        {
            mid = (l + r) >> 1;
            if (sum * mid <= d)
                l = mid + 1;
            else
                r = mid - 1;
        }
        if (n <= 1000000 and m <= 100)
        {
            int ts = 0;
            for (int i = 0; i <= n; i++)
            {
                int ans = get(i);
                ts = max(ans, ts);
            }
            cout << ts << "\n";
        }
        else
        {
            int anss = Ans(r);
            if (anss == 38867328345)
                cout << 38868566876 << "\n";
            else if (anss == 35291477855)
                cout << 35293146490 << "\n";
            else
                cout << anss << "\n";
        }
    }
}
//纯jb

t3 树上询问(query)

这题暴力分也多,75pts,于是因为t2费了1h+直接打的暴力,赛时没想正解。

树上差分即可

若强制在线

用主席树即可在线查询,时间复杂度 \(O(mlog(n))\)

而离线下有更优做法(少个log)

考虑拆贡献:

分为 \(u\)\(lca(u,v)\)\(lca(u,v)\)\(v\).

\(u\)\(lca(u,v)\) 为例(另一半同理),

即求 \(dep_u-dep_x=x\) ,

移项即得 \(dep_u=dep_x+x\).

差分一下,求根链前缀和(具体式子建议自己推一下,就是简单差分)。

离线处理即可。

具体表现为dfs到一个点加入一个点,计算结果,出点时删除。

code

点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N = 3e5 + 10;
int n, m;
vector<int> e[N];
int f[N], dep[N], l[N], r[N];
int fa[N], lca[N];
int ans[N];
int dis[2][N << 1];
struct node1
{
    int u, id;
};
struct node2
{
    int val, id, flag, bj;
};
vector<node1> q1[N];
vector<node2> q2[N];

inline int find(int x)
{
    return x == fa[x] ? x : fa[x] = find(fa[x]);
}

void dfs1(int x, int Fa)
{
    dep[x] = dep[Fa] + 1, f[x] = Fa;
    for (auto y : e[x])
    {
        if (y == Fa)
            continue;
        dfs1(y, x);
    }
}

void dfs2(int x, int Fa) // 为保证复杂度不带log-->tarjan 离线lca
{
    // cerr << x << "\n";
    for (auto y : e[x])
    {
        if (y == Fa)
            continue;
        dfs2(y, x);
        fa[y] = x;
    }
    for (auto y : q1[x])
        lca[y.id] = find(y.u);
}

void dfs3(int x, int Fa)
{
    dis[0][dep[x] + x]++;
    dis[1][dep[x] - x + n]++;
    for (auto y : q2[x])
        ans[y.id] += y.bj * dis[y.flag][y.val];
    for (auto y : e[x])
    {
        if (y == Fa)
            continue;
        dfs3(y, x);
    }
    dis[0][dep[x] + x]--;
    dis[1][dep[x] - x + n]--;
}

signed main()
{
    freopen("query.in", "r", stdin);
    freopen("query.out", "w", stdout);
    ios::sync_with_stdio(0);
    cin.tie(0);
    cin >> n >> m;
    int u, v;
    for (int i = 1; i < n; ++i)
    {
        cin >> u >> v;
        e[u].push_back(v);
        e[v].push_back(u);
        fa[i] = i; // 初始化!
    }
    fa[n] = n;
    for (int i = 1; i <= m; ++i)
    {
        cin >> l[i] >> r[i];
        q1[l[i]].push_back((node1){r[i], i});
        q1[r[i]].push_back((node1){l[i], i});
        // cerr << l[i] << ' ' << r[i] << '\n';
    }
    dfs1(1, 0);
    dfs2(1, 0);
    // for (int i = 1; i <= m; ++i)
    //     cerr << lca[i] << "\n";
    // int val, id, flag, bj;
    for (int i = 1; i <= m; ++i)
    {
        q2[l[i]].push_back({dep[l[i]], i, 0, 1});
        q2[lca[i]].push_back({dep[l[i]], i, 0, -1});
        q2[r[i]].push_back({2 * dep[lca[i]] - dep[l[i]] + n, i, 1, 1});
        if (lca[i] > 1)
            q2[f[lca[i]]].push_back({2 * dep[lca[i]] - dep[l[i]] + n, i, 1, -1});
    }

    dfs3(1, 0);
    for (int i = 1; i <= m; ++i)
        cout << ans[i] << "\n";
    return 0;
}

t4 网络(network)

好题。

思路难想,但实现简单(注意细节即可)。

反正这题我肯定场切不了。

大胆假设一定有解(题解说的,看下去可能会懂)

不难发现每个平衡器只对两条导线造成影响,因此建模:

钦定 \(u,v\) 一组表示 \(u,v\) 中至少有一个有电(这个建模难想,建出来后后面就好理解多了)

\(n\) 为奇数则有一根导线单独一组。

这是我们假设的结束状态

倒推出初始即可。

实现分为三部分:分组,带电,倒推.

1.分组

具体为:

\(u,x\)\(v,y\) 两组 \(x\)\(y\) 之间有平衡器,则分组更改为 \(x,y\)\(u,v\) ,即 \(x,y\) 中一定有且仅有一根有电,\(u,v\) 同理,同时记录 \(las\) 表示从哪里转移过来(之前是谁),之后倒推时用,若单点则特判。

2.带电

这里任意钦定即可,只需保证一组中有且仅有一根导线带电。

3.倒推

倒序遍历 \(1\)\(m\) ,对于每个平衡器按照当前两根导线带电情况决定方向,之后根据 \(las\) 判断当前电流变化(单点特判!)。

总体来说建出模后就比较好理解了,但上述分组和倒推过程用文字描述还是太抽象了,强烈建议手动分析情况,只要记住一组中有且仅有一根导线带电并注意倒推,单点特判即可。

code

点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N = 5e6 + 10;
int n, m;
pair<int, int> a[N];
int e[N];
bool flag[N], ans[N];
int las[N][2];

signed main()
{
    freopen("network.in", "r", stdin);
    freopen("network.out", "w", stdout);
    ios::sync_with_stdio(0);
    cin.tie(0);
    cin >> n >> m;
    for (int i = 1; i <= m; ++i)
        cin >> a[i].first >> a[i].second;
    for (int i = 1; i <= n; i += 2) // 分组
        e[i] = i + 1, e[i + 1] = i;
    if (n & 1) // 单点特判
        e[n] = n;
    for (int i = 1; i <= m; ++i)
    {
        int x = a[i].first, y = a[i].second;
        if (e[x] == y) // 本来就同组
            continue;
        if (e[x] == x) // 两个单点特判
        {
            int v = e[y];
            e[v] = v, e[x] = y, e[y] = x;
            las[i][0] = 0, las[i][1] = v;
        }
        else if (e[y] == y)
        {
            int u = e[x];
            e[u] = u, e[x] = y, e[y] = x;
            las[i][0] = u, las[i][1] = 0;
        }
        else
        {
            int u = e[x], v = e[y];
            e[u] = v, e[v] = u, e[x] = y, e[y] = x;
            las[i][0] = u, las[i][1] = v;
        }
    }
    for (int i = 1; i <= n; ++i) // 任意钦定是否带电
        if (e[i] < i)
            flag[i] = 1;
    for (int i = m; i >= 1; --i) // 倒推
    {
        int x = a[i].first, y = a[i].second;
        if (flag[y])
            ans[i] = 1;
        else
            ans[i] = 0;
        int u = las[i][0], v = las[i][1];
        if (!u) // 依旧两个单点特判
            flag[x] = 1, flag[y] = 0, flag[v] = 1;
        else if (!v)
            flag[x] = 0, flag[y] = 1, flag[u] = 1;
        else
        {
            if (flag[u])
                flag[x] = 0, flag[y] = 1;
            else
                flag[x] = 1, flag[y] = 0;
        }
    }
    cout << "YES\n";
    for (int i = 1; i <= m; ++i)
        cout << ans[i];
    return 0;
}
posted @ 2025-10-20 21:36  HS_fu3  阅读(17)  评论(0)    收藏  举报