首页 > 其他分享 >CSP-S模拟10

CSP-S模拟10

时间:2022-09-24 19:46:01浏览次数:43  
标签:10 ch const int ll stk maxn CSP 模拟

体活就是用来上体育的~~~Cat到操场上跑了n圈,6:00~6:25,并且边跑边唱歌,就是那首我最喜欢的"世界问,你是谁,来自哪……"好像路上有人因此回头多看了两眼,今天的云好美,一路上的老师有好多在拍照的,仰望天空不知道Dream是近在咫尺还是远在天涯……好久没有这样放纵了呢……

 

A. 欧几里得的噩梦

建个图,把x和y连边(没有y就建一个虚点m+1,把x和m+1连边),这个图不能成环,用并查集判一下环。意思就是要把能用小数异或得到的值删掉,如果有一个a,b是1的数,还有一个b,c是1的数,还有一个c,d是1的数,可以得到a,d,再来一个a,d是1的数就可以把它去掉了,方案数就是2^被选上的数的个数。

code

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 5e5 + 2;
const int N = 1e5 + 2;
const ll mod = 1e9 + 7;

int n, m, fa[maxn], nns;
bool del[maxn];

inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
        if(ch == '-')
        {
            f = -1;
        }
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        x = (x << 1) + (x << 3) + (ch ^ 48);
        ch = getchar();
    }
    return x * f;
}

int find(int x)
{
    if(x == fa[x]) return x;
    return fa[x] = find(fa[x]);
}

ll qpow(ll a, ll b)
{
    ll ans = 1;
    while(b)
    {
        if(b & 1) ans = ans * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return ans;
}

int main()
{
    n = read(); m = read();
    for(int i=1; i<=m+1; i++) fa[i] = i;
    for(int i=1; i<=n; i++)
    {
        int tp = read();
        if(tp == 1)
        {
            int x = read();
            int fx = find(x), fy = find(m+1);
            //printf("fx = %d  fy = %d\n", fx, fy);
            if(fx == fy) nns++, del[i] = 1;
            else fa[fx] = fy;
        }
        else 
        {
            int x = read(), y = read();
            int fx = find(x), fy = find(y);
            //printf("fx = %d  fy = %d\n", fx, fy);
            if(fx == fy) nns++, del[i] = 1;//fx == fx 不交个0分改不了低错是吗!?
            else fa[fx] = fy;
        }
    }
    printf("%lld %d\n", qpow(2, n-nns), n-nns);
    for(int i=1; i<=n; i++)
    {
        if(!del[i]) printf("%d ", i);
    }
    
    return 0;
}

 

B. 清扫

树上两点间最短路径是确定的,可以由每个点的值推出(这个点和父节点相连的边)每条边要覆盖的次数,和叶子节点相连的所有边权和就是点权,和非叶子节点相连的所有边权和是点权的2倍,因为要从这个点的两侧任选叶子把它的点权消掉,每消一个点权花费两条边。

于是上面的操作把只有叶子之间可以相互消的问题转到了任意的点上,就可以从下到上地判断合法了。判断合法有3个限制条件:(设这个点的孩子权值和为sum)

1.sum>=a[u],这些sum可以相互消,这样两个孩子对应a[u]-1,也可以连出去,这样一个孩子使a[u]-1,如果不满足这个条件表示所有sum都向外消都无法使a[u]变成0.

2.sum<=a[u]*2,如果不满足这个条件表示所有sum都内部消也能使a[u]提前消为0.

3.最大的孩子的权值小于等于a[u],否则这个孩子消不掉了。

code

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 1e5 + 3;
const ll mod = 1e9 + 7;

int n, m, du[maxn], rt;
ll a[maxn];
vector<int> son[maxn];

inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
        if(ch == '-')
        {
            f = -1;
        }
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        x = (x << 1) + (x << 3) + (ch ^ 48);
        ch = getchar();
    }
    return x * f;
}

void dfs(int u, int fa)
{
    if(du[u] == 1) return;
    ll sum = 0, mx = 0;
    for(int i=0; i<son[u].size(); i++)
    {
        int v = son[u][i];
        if(v == fa) continue;
        dfs(v, u);
        sum += a[v];
        mx = max(mx, a[v]);
    }
    if(sum < a[u] || sum > 2*a[u]) printf("NO\n"), exit(0);
    if(mx > a[u]) printf("NO\n"), exit(0);
    a[u] = 2 * a[u] - sum;
}

int main()
{
    n = read();
    for(int i=1; i<=n; i++) a[i] = read();
    if(n == 2)
    {
        if(a[1] != a[2]) printf("NO\n");
        else printf("YES\n");
        exit(0);
    }
    for(int i=1; i<n; i++)
    {
        int x = read(), y = read();
        son[x].push_back(y); son[y].push_back(x);
        du[x]++; du[y]++;
    }
    int rt = 1;
    for( ;du[rt]==1; rt++);
    dfs(rt, 0);
    if(!a[rt]) printf("YES\n");
    else printf("NO\n");
    
    return 0;
}

 

C. 购物

用类似背包dp的东西结合二进制数枚举乱搞一波……本来打算用线段树来搞区间覆盖,后来忽然发现计算出可以搭配的每一个值按从小到大的顺序枚举就可以直接加上它的范围:(表示向上取整)(a+1)/2~a

code

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 3e6 + 2;
const int N = 1e5 + 2;
const ll mod = 1e9 + 7;

int n, a[N], sum, Max, ans;
bool f[maxn];

inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
        if(ch == '-')
        {
            f = -1;
        }
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        x = (x << 1) + (x << 3) + (ch ^ 48);
        ch = getchar();
    }
    return x * f;
}
/*
struct Segmenttree
{
    struct node 
    {
        int sum, lazy;
    }t[maxn<<2];
    void pushup(int x)
    {
        t[x].sum = t[x<<1].sum + t[x<<1|1].sum;
    }
    void pushdown(int x, int l, int r, int mid)
    {
        int ls = x<<1, rs = x<<1|1;
        t[ls].lazy = t[rs].lazy = 1;
        t[ls].sum = mid-l+1;
        t[rs].sum = r-mid;
        t[x].lazy = 0;
    }
    void update(int x, int l, int r, int L, int R)
    {
        if(L <= l && r <= R)
        {
            t[x].sum = r-l+1;
            t[x].lazy = 1;
            return;
        }
        int mid = (l + r) >> 1;
        if(t[x].lazy) pushdown(x, l, r, mid);
        if(L <= mid) update(x<<1, l, mid, L, R);
        if(R > mid) update(x<<1|1, l, mid, L, R);
        pushup(x);
    }
    int query(int x, int l, int r, int L, int R)
    {
        if(L <= l && r <= R)
        {
            return t[x].sum;
        }
        int mid = (l + r) >> 1, ans = 0;
        if(t[x].lazy) pushdown(x, l, r, mid);
        if(L <= mid) ans += query(x<<1, l, mid, L, R);
        if(R > mid) ans += query(x<<1|1, mid+1, r, L, R);
        return ans;
    }
}T;
*/
set<ll> s;
ll res;
inline void check(int num)
{
    ll sum = 0;
    for(int i=1; i<=n; i++)
    {
        if(num & (1<<(i-1))) sum += a[i];
    }
    s.insert(sum);
}

int main()
{
    n = read();
    if(n <= 3)
    {
        for(int i=1; i<=n; i++) a[i] = read();
        Max = 1 << n;
        for(int i=1; i<Max; i++)
        {
            check(i);
        }
        ll las = 0;
        for(ll x : s)
        {
            ll mi = max((x+1)/2, las+1);
            //printf("mi = %lld x = %lld\n", mi, x);
            res += x - mi + 1;
            las = x;
        }
        printf("%lld\n", res);
        exit(0);
    }
    for(int i=1; i<=n; i++)
    {
        a[i] = read(); sum += a[i];
    }
    f[0] = 1;
    for(int i=1; i<=n; i++)
    {
        for(int j=sum; j>=a[i]; j--)
        {
            f[j] |= f[j-a[i]];
        }
    }
    int las = 0;
    for(int i=1; i<=sum; i++)
    {
        if(f[i])
        {
            int mi = max((i+1)/2, las+1);
            ans += i - mi + 1;
            //printf("mi = %d i = %d\n", mi, i);
            las = i;
            //Min = (i+1)/2;
            //Max = i;
            //T.update(1, 1, sum, Min, Max);
        }
    }
    //ans = T.query(1, 1, sum, 1, sum);
    printf("%d\n", ans);
    
    return 0;
}

但是没有想到每个数的范围其实是(a+1)/2~suma,suma代表排序后到1的前缀和,就是我们根本不用去找它们可以组成哪些数,我们只需要边界就够了。

suma一定是前(啊临时假设到i了吧)i个数能组成的最大值,(a+1)/2一定是新保证加入第i个数来配凑可得的最小值,中间的值产生的区间一定被包含在这个区间范围内并且没有空余。假设a[i]和a[i-1]凑在一起组成了新数,它除以二向下取整一定会大于(a[i]+1)/2,并且由于a[i-1]<a[i],a[i-1]/2 < a[i]/2,(a[i]+a[i-1])/2 < a[i],左端点使得覆盖范围联通,右端点使得覆盖范围拓展。以此类推得到一个大数和一个小数组合产生的区间都像这样。

code

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 3e6 + 2;
const int N = 1e5 + 2;
const ll mod = 1e9 + 7;

int n;
ll a[maxn], sum[maxn], ans;

inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
        if(ch == '-')
        {
            f = -1;
        }
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        x = (x << 1) + (x << 3) + (ch ^ 48);
        ch = getchar();
    }
    return x * f;
}

int main()
{
    n = read();
    for(int i=1; i<=n; i++) a[i] = read();
    sort(a+1, a+1+n);
    for(int i=1; i<=n; i++)
    {
        sum[i] = sum[i-1] + a[i];
    }
    for(int i=1; i<=n; i++)
    {
        ll mi = max(sum[i-1]+1, (a[i]+1)/2);
        //printf("%lld %lld\n", mi, sum[i]);
        ans += sum[i] - mi + 1;
    }
    printf("%lld\n", ans);
    
    return 0;
}

 

D. ants

permu这个题我还做过,然而忘了,Cat不叫Cat了,干脆叫狗熊啃玉米好了……虽然当时那个线段树并不是正解,但是50-20=30……

所以就去鹤了一下题解(并查集版本的,似乎有别的做法更快),大概回滚莫队的原理是整个代码最不好理解的东西了:因为删除或插入中的某一项过于复杂,于是就只保留一个,在这个题里是只有增加,对于同一范围内有序的项增加,无序的项增加无效需要删除影响,范围都改了那就直接全部清空就行。

code
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 1e5 + 2;
const int N = 1e5 + 2;
const ll mod = 1e9 + 7;

int n, m, siz[maxn], fa[maxn], stk[maxn], res, top, a[maxn], ans[maxn], t;
bool vis[maxn];

inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
        if(ch == '-')
        {
            f = -1;
        }
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        x = (x << 1) + (x << 3) + (ch ^ 48);
        ch = getchar();
    }
    return x * f;
}

struct node 
{
    int l, r, pos, id;
    bool operator < (const node &T) const 
    {
        return pos == T.pos ? r < T.r : pos < T.pos;
    }
}q[maxn];

int find(int x) 
{
    return x == fa[x] ? x : find(fa[x]);//没有路径压缩!我真手欠!
}

void merge(int x, int y)
{
    x = find(x), y = find(y);
    if(x == y) return;
    if(siz[x] > siz[y]) swap(x, y);
    fa[x] = y;
    siz[y] += siz[x];
    stk[++stk[0]] = x;
}

void add(int x)
{
    vis[x] = 1;
    if(vis[x-1]) merge(x, x-1);
    if(vis[x+1]) merge(x, x+1);
    res = max(res, siz[find(x)]);
}

void del()
{
    while(stk[0] > top)
    {
        siz[find(stk[stk[0]])] -= siz[stk[stk[0]]];
        fa[stk[stk[0]]] = stk[stk[0]];
        stk[0]--;
    }
}

int main()
{
    n = read(); m = read();
    t = sqrt(n);
    for(int i=1; i<=n; i++) a[i] = read();
    for(int i=1; i<=m; i++)
    {
        q[i].l = read(); q[i].r = read();
        q[i].pos = (q[i].l-1)/t+1;
        q[i].id = i;
    }
    sort(q+1, q+m+1);
    int l, r, pos;
    for(int i=1; i<=m; i++)
    {
        if(q[i].pos != q[i-1].pos)
        {
            for(int j=1; j<=n; j++)
            {
                fa[j] = j; vis[j] = 0; siz[j] = 1;
            }
            res = stk[0] = 0;
            l = pos = q[i].pos*t+1;
            r = l-1;
        }
        if(q[i].pos == (q[i].r-1)/t+1)
        {
            int now = 1, len = 1;
            for(int j=q[i].l; j<=q[i].r; j++) stk[++stk[0]] = a[j];
            sort(stk+1, stk+stk[0]+1);
            for(int j=1; j<=stk[0]; j++)
            {
                if(stk[j] == stk[j-1]+1 && j>1) now++;
                else now = 1;
                len = max(len, now);
            }
            ans[q[i].id] = len;
            stk[0] = 0;
        }
        else 
        {
            while(r < q[i].r) add(a[++r]);
            int flag = res;
            top = stk[0];
            while(l > q[i].l) add(a[--l]);
            ans[q[i].id] = res;
            res = flag;
            while(l < pos) vis[a[l++]] = 0;
            del();
        }
    }
    for(int i=1; i<=m; i++) printf("%d\n", ans[i]);
    
    return 0;
}

 

标签:10,ch,const,int,ll,stk,maxn,CSP,模拟
From: https://www.cnblogs.com/Catherine2006/p/16726334.html

相关文章

  • 力扣1095——山脉数组中查找目标值
    1095.山脉数组中查找目标值难度困难(这是一个 交互式问题 )给你一个 山脉数组 mountainArr,请你返回能够使得 mountainArr.get(index) 等于 target 最小 的......
  • 9.24-CSP-S模拟10
    T1欧几里得的噩梦一眼线性基题,可以证明在模拟线性基插入时,任何时候当前数的为1的位都不会超过二,于是模拟的做法就很显然了。但是这显然复杂度还是错的,赛时百分之八十的人......
  • Downie 4 for Mac中文版安装包(最好用的视频下载软件)V4.5.10直装版
    DownieforMac是一款MacOS平台上一款好用的视频下载工具,轻松从数千个不同的网站下载视频。支持youtube等主流网站视频,最大的特点最是支持网站多且可以多点同时下载,只需粘......
  • 2022NOIP前模拟赛订正情况
    √表示已订正,×表示不在能力范围之内,空表示未订正日期ABCD订正地址2022.9.3√√√×https://www.luogu.com.cn/contest/828672022.9.7√××ht......
  • 10、整合Mybatis框架
    mybatis中文文档:https://blog.csdn.net/qq_41182402/article/details/121281405UserMapper.xmlsql语句点击查看代码<?xmlversion="1.0"encoding="UTF-8"?><!DOC......
  • 200天1000题 (DAY 8)
    200天1000题(DAY8)目前总题数:36目前CF分数:1336(+11)今天打了一下CFRound#822(DIV2)手速比以前快,过了3个题,D想到一个双指针贪心的写法,但实现出来的代码有漏洞,疯狂WA......
  • ASEMI快恢复二极管6A10参数,6A10规格,6A10封装
    编辑-ZASEMI快恢复二极管6A10参数:型号:6A10最大重复峰值反向电压(VRRM):1000V最大RMS电桥输入电压(VRMS):700V最大直流阻断电压(VDC):1000V最大平均正向整流输出电流(IF):6A峰值......
  • [总结]2022.9.24 挖土机杯 CSP-J 组模拟赛 R1
    [总结]2022.9.24挖土机杯CSP-J组模拟赛R1P1赛时情况看到T1,显然是道白给。但我想了一会。依旧先把题目读完。T2有点模拟的样子,但又有点简单;T3显然dp;T4连乱搞都不会......
  • CF1310C Au Pont Rouge 解题报告
    题意翻译给出一个长度为\(n\)的字符串\(S\)以及整数\(m,k\)。对于一个把\(S\)分割成非空的\(m\)段的一个方案,我们用这个方案中分割出的字典序最小的一个串代表......
  • 力扣106 构造二叉树
      class Solution {public:    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {    return reBuild(inorder, postorder);......