CSP-S模拟10

T1.欧几里得的噩梦

第一眼,这不是线性基板子题吗。但是值域是25e5,但是我们发现它的一个神奇性质,一个数的二进制中只有两个一。我们定义高位为x,低位为y。如果线性基中p[x]空,直接插入,令p[x]=y;如果非空,x这一位被消掉,令x=p[x],如果x这时小于y就交换一下,因为线性基的构造是从高到低的。我们发现当这个数不能被插入时当且仅当有一个时刻x=y。这样暴力跳的时间复杂度不能保证,但是数据太水了,基本和正解一个速度。但是我们还是需要优化的,发现x=p[x]的状态为一棵树,而且这个关系是可传递的,并且如果存在某一时刻x=y,那么之后这些位的p相同。所以这启发我们用并查集维护,每次find它们的最低位,看是否相等,如果相等,说明异或和为0。

代码
#define sandom signed
#define fre(x, y) freopen(#x ".in", "r", stdin), freopen(#y ".out", "w", stdout);
#include <bits/stdc++.h>
#define re register int 
using namespace std; int wrt[20], TP;
const int Z = 5e5 + 10; const int mod = 1e9 + 7; typedef long long ll;
inline int read() { int x = 0, f = 0; char c = getchar(); while (!isdigit(c)) f |= c == '-', c = getchar(); while (isdigit(c)) x = (x << 1) + (x << 3) + (c ^ 48), c = getchar(); return f ? -x : x; }
inline void write(int x) { TP = 0; if (x < 0) putchar('-'), x = -x; while (x >= 10) wrt[++TP] = x % 10, x /= 10; wrt[++TP] = x; while (TP) putchar(wrt[TP--] | 48); putchar(' '); }
inline int qpow(int b) { ll res = 1; while (b--) res = res * 2 % mod; return res; }

int n, m, ans[Z], tot;
int p[Z];
inline int find(int x) { return x == p[x] ? x : p[x] = find(p[x]); }
inline bool insert(int x, int y)
{
    x = find(x), y = find(y);
    if (x == y) return false;
    if (x < y) swap(x, y);
    p[x] = y; return true;
}

sandom main()
{
    n = read(), m = read();
    for (re i = 0; i <= m; i++) p[i] = i;
    for (re i = 1; i <= n; i++) 
    {
        int op = read(), x = read(), y = 0;
        if (op == 2) y = read();
        if (insert(x, y)) ans[++tot] = i;
    }
    write(qpow(tot)), write(tot), putchar('\n');
    for (re i = 1; i <= tot; i++) write(ans[i]);
    return 0;
}

T2.清扫

考试只打了菊花图的,但是考后发现只需要对此拓展一下,对于每一个节点都判断一下就可以了。但是CD大佬有了更加简洁、通俗的方法。将贪心转化为方程问题。对于一棵子树,它的叶子节点中要么两两配对,两个叶子节点分别减1,根节点减1;要么一个叶子节点与子树外的一个叶子节点配对,叶子节点减1,根节点减1。我们设x为第一种情况的操作数,y为第二种情况的操作数。定义sum为子树的石子和(不包括自己)。则有方程组

2x+y=sum[rt]x+y=a[rt]

显然方程有解要满足x,y>=0,同时还需要规定一个上界,即为max[rt]<=a[rt]max[rt]为子树中最大的sum),因为超过了的话,显然父亲根本不够减完儿子。对于每一个非叶子节点,进行这样的处理,并把节点的石子数减去x,y的开销,作为新的叶子节点,向上回溯。最后需要判断根节点的石子数是否为0。细节:根节点的度数大于1,不然就作为叶子了。

代码
#define sandom signed
#define fre(x, y) freopen(#x ".in", "r", stdin), freopen(#y ".out", "w", stdout);
#include <bits/stdc++.h>
#define re register int 
using namespace std;
const int Z = 1e5 + 10;
inline int read() { int x = 0, f = 0; char c = getchar(); while (!isdigit(c)) f |= c == '-', c = getchar(); while (isdigit(c)) x = (x << 1) + (x << 3) + (c ^ 48), c = getchar(); return f ? -x : x; }
inline int max(int a, int b) { return a > b ? a : b; } inline int min(int a, int b) { return a < b ? a : b; }

int n, m, k, ans;
vector <int> e[Z];
int a[Z], root;
int sum[Z], siz[Z];
bool dfs(int rt, int fa)
{
    if (e[rt].size() == 1) { sum[rt] = a[rt]; return true; }//叶子节点
    for (re i = 0; i < e[rt].size(); i++)
    {
        int son = e[rt][i];
        if (son == fa) continue;
        if (!dfs(son, rt)) return false;
        sum[rt] += sum[son];
        siz[rt] = max(siz[rt], sum[son]);
    }
    if (siz[rt] > a[rt]) return false;
    int x = sum[rt] - a[rt], y = 2 * a[rt] - sum[rt];
    sum[rt] = y;//将它作为叶子节点上传
    return x >= 0 && y >= 0;
}

sandom main()
{
    n = read();
    for (re i = 1; i <= n; i++) a[i] = read();
    if (n == 2) { a[1] == a[2] ? puts("YES") : puts("NO"); return 0; }
    for (re i = 1; i < n; i++)
    {
        int x = read(), y = read();
        e[x].push_back(y), e[y].push_back(x);
    }
    for (re i = 1; i <= n; i++) if (e[i].size() > 1) { root = i; break; }
    if (dfs(root, 0) && sum[root] == 0) puts("YES");
    else puts("NO");
    return 0;
}

T3.购物

这么水的题,我竟然没有做!!!结论题,但是证明是难点。先说结论:对于某一个价格w,k的取值范围是[w2,w]。按a排序后,定义s[i]为a的前缀和。对于其中一个a[i]的加入,新扩展的区间最远将到s[i],中间部分要么连续,要么只在[s[i1],a[i]2]有空隙,要么连续,对于[a[i]2,s[i]]一定可以被覆盖。
首先明确:1式[a[i]2,a[i]]以及2式[s[i]2,s[i]]一定可以覆盖,3式[1,s[i1]]以及处理完,默认全部覆盖。需证明[a[i]2,s[i]]一定可以覆盖。
证明:假设前i1个已经处理好,把问题看作两条线段的覆盖问题。分情况讨论:
1.3式包含1式,此时有a[i]2<a[i]<s[i1]s[i]=s[i1]+a[i]2a[i]<s[i]<2s[i1]a[i]2<a[i]<s[i]2<s[i1]<s[i],因为s[i1]前已经处理完,且2式与s[i1]相交,得证。覆盖连续。
2.3式与1式相交,此时有a[i]2<s[i1]<a[i]s[i]=s[i1]+a[i]2s[i1]<s[i]<2a[i]a[i]2<s[i1]<s[i]2<a[i]<s[i],因为1式与2式相交,得证。覆盖连续。
3.3式与1式分离,此时有s[i1]<a[i]2<a[i]s[i]=s[i1]+a[i]2s[i1]<s[i]<2a[i]a[i]2<s[i1]<s[i]2<a[i]<s[i],因为1式与2式相交,得证。只有(s[i1],a[i]2)断层。
证毕,开始贪心。

代码
#define sandom signed
#define fre(x, y) freopen(#x ".in", "r", stdin), freopen(#y ".out", "w", stdout);
#include <bits/stdc++.h>
#define re register int 
#define int long long 
using namespace std; int wrt[20], TP;
const int Z = 1e5 + 10;
inline int read() { int x = 0, f = 0; char c = getchar(); while (!isdigit(c)) f |= c == '-', c = getchar(); while (isdigit(c)) x = (x << 1) + (x << 3) + (c ^ 48), c = getchar(); return f ? -x : x; }
inline void write(int x) { TP = 0; if (x < 0) putchar('-'), x = -x; while (x >= 10) wrt[++TP] = x % 10, x /= 10; wrt[++TP] = x; while (TP) putchar(wrt[TP--] | 48); putchar('\n'); }
inline int max(int a, int b) { return a > b ? a : b; } inline int min(int a, int b) { return a < b ? a : b; }
inline int lowbit(int x) { return x & (-x); }

int n, m, k, ans;
int a[Z];

sandom main()
{
    fre(test, test);
    n = read();
    for (re i = 1; i <= n; i++) a[i] = read();
    sort(a + 1, a + 1 + n);
    int sum = 0;
    for (re i = 1; i <= n; i++)
    {
        int j = (a[i] - 1) / 2 + 1;
        if (j <= sum) ans += a[i];//相交
        else ans += sum + a[i] - j + 1;//断层
        sum += a[i];//维护前缀和
    }
    cout << ans;
    return 0;
}

T4.ants

回滚莫队板子题。permu的加强版。但是当时用普通莫队加山海经水过了,这次被卡了,不仅如此,由于我的莫队没有排序……,喜提20分。由于是板子,直接上代码。

代码
#define sandom signed
#define fre(x, y) freopen(#x ".in", "r", stdin), freopen(#y ".out", "w", stdout);
#include <bits/stdc++.h>
#define re register int 
using namespace std; int wrt[20], TP;
const int Z = 1e5 + 10;
inline int read() { int x = 0, f = 0; char c = getchar(); while (!isdigit(c)) f |= c == '-', c = getchar(); while (isdigit(c)) x = (x << 1) + (x << 3) + (c ^ 48), c = getchar(); return f ? -x : x; }
inline void write(int x) { TP = 0; if (x < 0) putchar('-'), x = -x; while (x >= 10) wrt[++TP] = x % 10, x /= 10; wrt[++TP] = x; while (TP) putchar(wrt[TP--] | 48); putchar('\n'); }
inline int max(int a, int b) { return a > b ? a : b; } inline int min(int a, int b) { return a < b ? a : b; }

int n, m, k, ans[Z];
int be[Z], p[Z], d[Z];
struct ant
{
    int l, r, id;
    friend bool operator <(ant A, ant B)
    {
        if (be[A.l] == be[B.l]) return A.r < B.r;//保证同一块内右端点只扩展
        return be[A.l] < be[B.l];//分块讨论
    }
}; ant que[Z];
int L[Z], R[Z];
int l, r, res, top;
struct delte { int a, b, c, d, e; }; delte stk[Z];
inline void add(int x, bool op)
{
    L[x] = R[x] = x;
    if (L[x - 1]) L[x] = L[x - 1];
    if (R[x + 1]) R[x] = R[x + 1];
    if (op) stk[++top] = delte{L[x], R[L[x]], R[x], L[R[x]], x};
    R[L[x]] = R[x], L[R[x]] = L[x];
    res = max(res, R[x] - L[x] + 1);
}
inline void del(delte x) { R[x.a] = x.b, L[x.c] = x.d, L[x.e] = R[x.e] = 0; }
inline int violent(int l, int r)//在同一块内暴力扫
{
    int ans = 1, cnt = 1;
    for (re i = l; i <= r; i++) d[i] = p[i];
    sort(d + l, d + r + 1); d[l - 1] = -1;
    for (re i = l; i <= r; i++) 
    {
        if (d[i] == d[i - 1] + 1) cnt++;
        else cnt = 1;
        ans = max(ans, cnt);
    }
    return ans;
}

sandom main()
{
    n = read(), m = read(); int t = sqrt(n);
    for (re i = 1; i <= n; i++) be[i] = (i - 1) / t + 1;
    for (re i = 1; i <= n; i++) p[i] = read();
    for (re i = 1; i <= m; i++) que[i].l = read(), que[i].r = read(), que[i].id = i;
    sort(que + 1, que + 1 + m);
    for (re i = 1; i <= m; i++)
    {
        ant q = que[i];
        if (be[q.l] != be[que[i - 1].l])//进入下一块清空
        {
            for (re i = 1; i <= n; i++) L[i] = R[i] = 0;
            r = min(t * be[q.l], n), l = r + 1; res = 0;
        }
        if (be[q.l] == be[q.r]) ans[q.id] = violent(q.l, q.r);//同一块暴力扫
        else 
        {
            while (r < q.r) add(p[++r], 0);//持续扩展
            int tmp = res;
            while (l > q.l) add(p[--l], 1);//扩展完撤销
            ans[q.id] = res; res = tmp;
            while (top) del(stk[top--]), l++;//回滚撤销
        }
    }
    for (re i = 1; i <= m; i++) write(ans[i]);
    return 0;
}

警钟敲响::不要4道题共用一个cpp,不要编译错文件,不要交错代码,交代码之前请检查CE情况!!!

posted @   sandom  阅读(33)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示