CSP-S模拟13 [排序,Xorum,有趣的区间问题,无聊的卡牌问题](好久没更过考试总结了呢)

CSP-S模拟13

今天的破防除了考试保龄以外,还有自己的涩图题解传到了hsez的同学那里,属实是完完全全的社死了。

image
image

丢大人了啊。TST将会臭名昭著

A.排序

CF1375E Inversion SwapSort

给定长度为 \(n\) 的序列 \(a_i\),求一种将每个逆序对下标 \((u,v)\) 的排序,使依次交换每个 \((a_u,a_v)\) 后,\(a_i\) 不减。

其实考试题是弱化版,原题是一个序列,考试题是一个排列。序列会出现重复的数,会更难处理一些,我们先假定 \(a\) 是一个排列。

因为最后要 \(a_i\) 不减,所以最后每个数放在哪里是固定的。要求交换每个逆序对,我们可以 \(n^2\) 扫一遍求出所有的逆序对,记录下每组逆序对在 \(a\)位置

之后去考虑每组逆序对交换的顺序。由于要交换逆序对的个数次,所以在每次交换后逆序对数只能减 \(1\),即每一次交换只能影响当前交换的逆序对,不能影响其他的逆序对。比如:\(5,4,2,3,1\)\(5\) 不能直接和 \(1\) 交换,这样 \(4,2,3\)\(1\) 构成的逆序对都会被影响,交换次数就假掉了。

所以,我们要优先交换 \(a\) 值大的逆序对。详细一点,对于逆序对 \((a_i,a_j)\),在 \(a_i\) 构成的所有逆序对中,我们要先交换 \(a_j\) 小的。对于 \(a_i\) 不同,优先要交换 \(i\) 小的。

对于序列其实同理,只是会有相等的情况。同样,对于逆序对 \((a_i,a_j)\),如果 \(a_j\) 相同,先交换 \(j\) 小的。

Code

#include<cstdio>
#include<vector>
#include<algorithm>

#define Pair pair< int, int >
#define Make(x, y) make_pair(x, y)

using namespace std;

const int MAXN = 1010;
int n, cnt;
int num[MAXN], val[MAXN];
vector< Pair > out;

bool cmp(const Pair &x, const Pair &y){
    if(x.first == y.first){
        if(num[x.second] != num[y.second]) return num[x.second] > num[y.second];
        return x.second > y.second;
    }
    return x.first < y.first;
}

inline int read(){
    int x = 0, f = 1;
    char c = getchar();

    while(c < '0' || c > '9'){
        if(c == '-') f = -1;
        c = getchar();
    }
    while(c >= '0' && c <= '9'){
        x = (x << 1) + (x << 3) + (c ^ 48);
        c = getchar();
    }

    return x * f;
}

int main(){
    n = read();
    for(register int i = 1; i <= n; i++){
        num[i] = read();
        val[i] = num[i];
    }

    sort(val + 1, val + 1 + n);
    cnt = unique(val + 1, val + 1 + n) - val - 1;
    for(register int i = 1; i <= n; i++){
        int pos = lower_bound(val + 1, val + 1 + cnt, num[i]) - val;
        num[i] = pos;
    }    
    
    for(register int i = 1; i <= n; i++)
        for(register int j = i + 1; j <= n; j++)
            if(num[i] > num[j]) out.push_back(Make(i, j));
    
    sort(out.begin(), out.end(), cmp);

    printf("%ld\n", out.size());
    for(register int i = 0; i < out.size(); i++)
        printf("%d %d\n", out[i].first, out[i].second);
    
    return 0;
}

B.Xorsum

CF1427E Xum

给出的 \(x\) 一定为奇数,则它二进制表示下的最低位一定是 \(1\),所以我们考虑如何把 \(x\) 其他位置的 \(1\) 消去。

依次消除 \(x\) 当前的最高位。注意对同一个数进行相加操作相当于左移一位,想要让 \(x\) 的最低位和最高位对齐,一共需要左移 \((x)_{2}\) 的位数 \(-1\) 位,设这个数为 \(y\)。当 \(x\) 异或 \(y\) 后,\(x\) 的最高位就被消掉了,设这个数为 \(z\)

\(r = y + z\)\(r\) 的低位和 \(z\) 相同,高位相当于 \(y\) 的高位左移一位,中间不同的一位是因为 \(y\)\(z\) 相加最高位的进位产生。之后我们将 \(y\) 左移一位,记作 \(s\)。当我们将 \(s\)\(r\) 异或后,得到 \(t\)\(t\) 的最高位和 \(y\) 的最高位相同,其余位和 \(x\) 相同,\(t\) 异或 \(x\) 就可以得到 \(t\)\(highbit\),记为 \(u\)。我们用 \(u\) 每次消去 \(y\) 的一个 \(1\),直到 \(y\) 只剩下最低位的 \(1\),这样就得到了 \(x\)\(highbit\),就可以消掉 \(x\) 的最高位。一直消下去,直到 \(x\)\(1\) 即可。

Code

#include<cstdio>
#include<vector>
#include<algorithm>

#define LL long long

using namespace std;

LL x;

struct Answer{
    LL opt, x, y;
};

vector<Answer> out;

LL lowbit(LL x){
    return x & (-x);
}

LL Change(LL x){
    LL y = x, res = x >> 1/*每次需要左移 x 的二进制表示的位数 - 1位*/;
    while(res){
        out.push_back((Answer){1, y, y});
        y <<= 1;
        res >>= 1;
    }
    //这时 y 的最低次位已经和 x 的最高次位对齐了
    LL z = x ^ y; //消去最高次位上的 1 
    out.push_back((Answer){2, x, y});
    //printf("x = %lld y = %lld z = %lld\n", x, y, z);
    //然后去找 x 的 highbit,也就是把 y 消到只剩下一个 1
    LL r = y + z;
    out.push_back((Answer){1, y, z});
    LL s = y + y;
    out.push_back((Answer){1, y, y});
    LL t = r ^ s;
    out.push_back((Answer){2, r, s});
    LL u = t ^ x; 
    out.push_back((Answer){2, t, x});

    while(y != lowbit(y)){
        if(y & u){
            out.push_back((Answer){2, y, u});
            y ^= u;
        } 
        out.push_back((Answer){1, u, u});       
        u += u;
        
        //printf("y = %lld\n", y);
    }

    //printf("first x = %lld\n", x);

    out.push_back((Answer){2, x, y});
    x ^= y;
    

    return x;
}

int main(){
    scanf("%lld", &x);

    while(x != 1) x = Change(x);

    printf("%ld\n", out.size());
    for(register int i = 0; i < out.size(); i++){
        if(out[i].opt == 1) printf("%lld + %lld\n", out[i].x, out[i].y);
        else if(out[i].opt == 2) printf("%lld ^ %lld\n", out[i].x, out[i].y);
    }
    
    return 0;
}

C.有趣的区间问题

CF1609F Interesting Sections

考虑枚举最大值和最小值的 popcount,那么问题等价于,给定一个序列 \(a_i\),其中有一些位置是关键位置,要求有多少个区间,满足其最大值和最小值都是关键位置。

枚举区间问题无非两个套路,扫描线和CDQ,这里用扫描线。枚举右端点 \(r\),扫描到 \(r\) 时,我们记 \(f_l\) 表示 \([l,r]\) 中最大值是否关键位置,以及 \([l,r]\) 中最小值是否是关键位置,那么显然 \(f_l\) 的变化可以通过单调栈求出,那么对于一个右端点 \(r\) 和一个 popcount \(v\),其对答案的贡献就是 \(f_l=2\)\(l\) 的个数。注意到 \(f_l\) 的上界就是 \(2\),因此这个可以通过区间最大值及其出现次数的线段树维护。

以及,这题卡常,用一下 fread 之类的差不多就能卡过去了。

Code

#include<cstdio>
#include<vector>
#include<algorithm>

#define LL long long

using namespace std;

const int MAXN = 1e6 + 10, SIZE = 65;
int n;
LL ans;
LL num[MAXN];
int cnt[MAXN], temp[SIZE];
int stk_max[MAXN], top_max, stk_min[MAXN], top_min;

struct Change{
    int pos, l, r, v;
};

vector<Change> q[SIZE];

inline char gc(){
	static char buf[MAXN], *p1 = buf, *p2 = buf;
	return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, MAXN, stdin), p1 == p2) ? EOF : *p1++;
}

inline LL read(){
	LL x = 0, f = 1;
    char c = gc();
	
    while(c < '0' || c > '9'){
        if(c == '-') f = -1;
        c = gc();
    }
	while(c >= '0' && c <= '9'){
        x = (x << 1) + (x << 3) + (c ^ 48);
        c = gc();
    }

	return x * f;
}

inline LL lowbit(LL x){
    return x & (-x);
}

inline int popcount(LL x){
    int ans = 0;
    while(x){
        ++ans;
        x -= lowbit(x);
    }
    return ans;
}

struct Segment_Tree{
    struct Tree{
        int l, r;
        int sum;
        int max;
        int lazy;
    }tr[MAXN << 2];

    inline int lson(int rt){
        return rt << 1;
    }

    inline int rson(int rt){
        return rt << 1 | 1;
    }

    inline void Pushup(int rt){
        tr[rt].sum = 0;
        tr[rt].max = max(tr[lson(rt)].max, tr[rson(rt)].max);
        if(tr[rt].max == tr[lson(rt)].max) tr[rt].sum += tr[lson(rt)].sum;
        if(tr[rt].max == tr[rson(rt)].max) tr[rt].sum += tr[rson(rt)].sum;
    }

    void Build(int rt, int l, int r){
        tr[rt].l = l;
        tr[rt].r = r;
        tr[rt].max = 0;
        tr[rt].lazy = 0;
        tr[rt].sum = r - l + 1;

        if(l == r) return;

        int mid = (l + r) >> 1;
        Build(lson(rt), l, mid);
        Build(rson(rt), mid + 1, r);
    }

    inline void Pushdown(int rt){
        if(tr[rt].lazy){
            tr[lson(rt)].max += tr[rt].lazy;
            tr[rson(rt)].max += tr[rt].lazy;
            tr[lson(rt)].lazy += tr[rt].lazy;
            tr[rson(rt)].lazy += tr[rt].lazy;
            tr[rt].lazy = 0;
        }
    }

    void Update(int rt, int l, int r, int data){
        if(tr[rt].l >= l && tr[rt].r <= r){
            tr[rt].max += data;
            tr[rt].lazy += data;
            return;
        }

        Pushdown(rt);

        int mid = (tr[rt].l + tr[rt].r) >> 1;
        if(l <= mid) Update(lson(rt), l, r, data);
        if(r > mid) Update(rson(rt), l, r, data);

        Pushup(rt);
    }
}S;

int main(){
    n = read();
    for(register int i = 1; i <= n; ++i){
        num[i] = read();
        cnt[i] = popcount(num[i]);
        ++temp[cnt[i]];
    }

    //for(register int i = 1; i <= n; i++)
    //    printf("cnt[%d] = %d\n", i, cnt[i]);

    for(register int i = 1; i <= n; ++i){
        while(top_min && num[stk_min[top_min]] > num[i]){
            int p = stk_min[top_min--];
            q[cnt[p]].push_back((Change){i, stk_min[top_min] + 1, p, -1});
        }
        q[cnt[i]].push_back((Change){i, stk_min[top_min] + 1, i, 1});
        stk_min[++top_min] = i;

        while(top_max && num[stk_max[top_max]] < num[i]){
            int p = stk_max[top_max--];
            q[cnt[p]].push_back((Change){i, stk_max[top_max] + 1, p, -1});
        }
        q[cnt[i]].push_back((Change){i, stk_max[top_max] + 1, i, 1});
        stk_max[++top_max] = i;
    }

    for(register int i = 0; i <= 64; ++i){
        if(!temp[i]) continue;

        S.Build(1, 1, n);

        for(register int j = 1, sit = 0; j <= n; ++j){
            while(sit < q[i].size() && q[i][sit].pos <= j){
                S.Update(1, q[i][sit].l, q[i][sit].r, q[i][sit].v);
                ++sit;
            }

            if(S.tr[1].max == 2) ans += S.tr[1].sum;
        }
    }
    
    printf("%lld\n", ans);

    return 0;   
}

D.无聊的卡牌问题

CF1427F Boring Card Game

没搞懂呢,咕。

搞懂了,就是细节有点恶心,搞了一个上午。

Code

#include<queue>
#include<cstdio>
#include<algorithm>

using namespace std;

const int MAXN = 1210;
int n, tot/*联通块的数量*/, num/*不存在依赖关系的为 0 的联通快的数量*/;
int card[MAXN], belong[MAXN]/*这张牌归谁取*/, type[MAXN]/*联通快的类型*/, size[MAXN]/*联通快的大小*/;
int deg[MAXN]/*联通块的入度*/, son[MAXN]/*联通块的儿子*/;
int block[MAXN][4]; //每个联通块的成员
int stk[MAXN], top;
bool vis[MAXN];
queue<int> q1, q2;

inline int read(){
    int x = 0, f = 1;
    char c = getchar();

    while(c < '0' || c > '9'){
        if(c == '-') f = -1;
        c = getchar();
    }
    while(c >= '0' && c <= '9'){
        x = (x << 1) + (x << 3) + (c ^ 48);
        c = getchar();
    }

    return x * f;
}

int main(){
    n = read() * 3;
    for(register int i = 1; i <= n; i++){
        card[i] = read();
        belong[card[i]] = 1; //是先手
    }

    n <<= 1;
    for(register int i = 1; i <= n; i++){
        if(!top || type[stk[top]] != belong[i]){
            stk[++top] = ++tot;
            type[tot] = belong[i];
            ++size[tot];
            block[tot][size[tot]] = i;
        }
        else{
            int p = stk[top];
            ++size[p];
            block[p][size[p]] = i;

            if(size[p] == 3){ //弹栈
                int q = stk[top - 1];
                son[p] = q;
                ++deg[q];
                --top;
            }
        }
    }

    for(register int i = 1; i <= tot; i++) //去找不存在依赖关系的为 0 的联通快
        if(!deg[i] && !son[i] && !type[i]) ++num;

    for(register int i = 1; i <= tot; i++){
        if(!deg[i] && type[i]) q1.push(i);
        if(!deg[i] && !type[i]) q2.push(i);
    }

    for(register int tim = 1; tim <= tot; tim++){ //第几次取
        if(tim & 1){ //到先手取
            while(!q1.empty()){
                int t = q1.front();
                q1.pop();

                vis[t] = true;
                if(son[t]){
                    --deg[son[t]];
                    if(!deg[son[t]] && !vis[son[t]]){
                        q2.push(son[t]);
                        if(!son[son[t]]) ++num;
                    }
                }
                printf("%d %d %d\n", block[t][1], block[t][2], block[t][3]);
                break;
            }
        }
        else{
            while(!q2.empty()){
                int t = q2.front();
                q2.pop();

                if(tim < tot && !son[t] && num == 1 && !q2.empty()){
                    q2.push(t);
                    continue;
                }

                vis[t] = true;
                if(son[t]){
                    --deg[son[t]];
                    if(!deg[son[t]] && !vis[son[t]]) q1.push(son[t]);
                }
                if(!son[t]) --num;
                printf("%d %d %d\n", block[t][1], block[t][2], block[t][3]);
                break;
            }
        }
    }

    return 0;
}
posted @ 2022-09-28 08:56  TSTYFST  阅读(82)  评论(7编辑  收藏  举报