CSP模拟13

套路题不会。考虑重修一下。

T1

经过考后的采访,joke3579、gtm1514、Chen_jr、Muel_imj四个人考场上写的Checker都拍过了然后都挂了零。

实际上从前往后扫,只要交换每个位置上比这个位置小 \(1\) 的数即可,因为这样会且仅会减少调整的这一对逆序对。

#include <algorithm>
#include <iostream>
#include <cstdio>
using namespace std;
int n,cnt,a[1010],pos[1010];
struct node{
    int l,r;
}q[1000010];
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        pos[i]=a[i];
    }
    for(int i=1;i<=n;i++){
        while(a[i]!=i){
            for(int j=i+1;j<=n;j++){
                if(a[i]==a[j]+1){
                    swap(a[i],a[j]);
                    q[++cnt]={i,j};
                    break;
                }
            }
        }
    }
    printf("%d\n",cnt);
    for(int i=1;i<=cnt;i++)printf("%d %d\n",q[i].l,q[i].r);
}

T2

考虑一个人类智慧构造。
首先我们把这个数左移若干位,直到最后一位与第一位重合,此时异或即可消掉最后一位。
然后我们将这个异或和与左移后的数加起来,那么最高位之前的就是异或和左移一位,最高位之后不变,都异或起来可以找到这个数的最高位。将最高位和这个数异或一下可以消掉,逐步消直至消到 \(1\) 即可。

#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define int long long
int x,cnt;
struct node{
    int od,x,y;
}q[100010];
int lowbit(int x){
    return x&-x;
}
signed main(){
    scanf("%lld",&x);
    while(x!=1){
        int y=x;
        for(int i=1;i<=__lg(x);i++){
            q[++cnt]={1,y,y};
            y<<=1;
        }
        q[++cnt]={2,x,y};
        int z=x^y;
        q[++cnt]={1,y,z};
        int ret=y+z;
        q[++cnt]={1,y,y};
        int tmp=y<<1;
        q[++cnt]={2,tmp,ret};
        tmp^=ret;
        q[++cnt]={2,tmp,x};
        int ans=x^tmp;
        while(y!=lowbit(y)){
            if(y&ans){
                q[++cnt]={2,ans,y};
                y^=ans;
            }
            q[++cnt]={1,ans,ans};ans<<=1;
        }
        q[++cnt]={2,x,y};x^=y;
    }
    printf("%lld\n",cnt);
    for(int i=1;i<=cnt;i++)printf("%lld %lld %lld\n",q[i].od,q[i].x,q[i].y);
}

T3

大水题,然后考场没写出来,属于是套路都不会了。赶紧退役吧。
我们枚举popcount,然后问题就变成了区间最大值+最小值=2(而且是最大值)。这是个经典扫描线问题。
注意我们要把所有的操作离线下来,不能顺序扫一遍所有的,要不然复杂度不对。
虽然我这个CF上被卡常了。

#include <algorithm>
#include <cstdio>
#include <iostream>
#include <vector>
#define lson rt<<1
#define rson rt<<1|1
using namespace std;
int n,top1,top2,cnt[70],c[1000010],maxx[1000010],minn[1000010];
long long ans,a[1000010];
struct node{
    int l,r,max,cnt,lazy;
}tree[4000010];
void pushup(int rt){
    tree[rt].max=max(tree[lson].max,tree[rson].max);
    tree[rt].cnt=(tree[rt].max==tree[lson].max?tree[lson].cnt:0)+(tree[rt].max==tree[rson].max?tree[rson].cnt:0);
}
void pushdown(int rt){
    if(tree[rt].lazy){
        int lz=tree[rt].lazy;tree[rt].lazy=0;
        tree[lson].max+=lz;tree[lson].lazy+=lz;
        tree[rson].max+=lz;tree[rson].lazy+=lz;
    }
}
void build(int rt,int l,int r){
    tree[rt].l=l;tree[rt].r=r;tree[rt].cnt=r-l+1;
    tree[rt].lazy=tree[rt].max=0;
    if(l==r)return;
    int mid=(l+r)>>1;
    build(lson,l,mid);build(rson,mid+1,r);
}
void update(int rt,int l,int r,int val){
    if(l<=tree[rt].l&&tree[rt].r<=r){
        tree[rt].max+=val;tree[rt].lazy+=val;return;
    }
    pushdown(rt);
    int mid=(tree[rt].l+tree[rt].r)>>1;
    if(l<=mid)update(lson,l,r,val);
    if(mid<r)update(rson,l,r,val);
    pushup(rt);
}
struct stu{
    int p,l,r,val;
};
vector<stu>q[64];
long long read(){
    long long x=0;char ch=getchar();
    while(!isdigit(ch))ch=getchar();
    while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
    return x;
}
int main(){
    n=read();
    for(int i=1;i<=n;i++){
        a[i]=read();c[i]=__builtin_popcountll(a[i]);
        cnt[c[i]]++;
    }
    for(int i=1;i<=n;i++){
        while(top1&&a[maxx[top1]]<=a[i]){
            int p=maxx[top1--];
            q[c[p]].push_back({i,maxx[top1]+1,p,-1});
        }
        q[c[i]].push_back({i,maxx[top1]+1,i,1});
        maxx[++top1]=i;
        while(top2&&a[minn[top2]]>a[i]){
            int p=minn[top2--];
            q[c[p]].push_back({i,minn[top2]+1,p,-1});
        }
        q[c[i]].push_back({i,minn[top2]+1,i,1});
        minn[++top2]=i;
    }
    for(int i=0;i<=63;i++){
        if(!cnt[i])continue;
        build(1,1,n);
        int ret=0;
        for(int j=1;j<=n;j++){
            while(ret<q[i].size()&&q[i][ret].p<=j){
                update(1,q[i][ret].l,q[i][ret].r,q[i][ret].val);
                ret++;
            }
            if(tree[1].max==2)ans+=tree[1].cnt;
        }
    }
    printf("%lld\n",ans);
    return 0;
}

T4

怪异的构造题。
考虑通过一个栈把所有的操作建出一个操作森林。具体的说,我们从小到大扫,每扫出三个同一个人选的牌就建出一个点出栈,同时将这个点与栈顶连边。
然后是构造方案。先手取的时候随便找一个归先手的叶子即可,后手取的时候只要保证取完以后仍然有一个根归后手即可。
证明不会。

#include <cstdio>
#include <algorithm>
#include <iostream>
#include <vector>
#include <stack>
using namespace std;
const int N=2e3 + 10;
int n,a[1210];
bool vis[1210];
struct node{
    int cnt,a[4],fa,deg,player;
    bool jud;
}rt;
vector<node>v;
stack<int>s;
signed main() {
    scanf("%d",&n);
    for(int i=1;i<=3*n;i++){
        scanf("%d",&a[i]);
        vis[a[i]]=true;
    }
    rt.cnt=rt.deg=0;rt.player=-1;rt.jud=true;
    v.push_back(rt);s.push(0);
    for(int i=1;i<=6*n;i++){
        if(v[s.top()].player==vis[i]){
            v[s.top()].a[++v[s.top()].cnt]=i;
            if (v[s.top()].cnt==3)s.pop();
        }
        else{
            v.push_back({1,{0,i,0,0},s.top(),0,vis[i],false});
            v[s.top()].deg++;
            s.push(v.size()-1);
        }
    }
    for(int i=1;i<=2*n;i++){
        int ret=-1;
        for(int j=1;j<=2*n;j++){
            if(v[j].jud||v[j].player!=(i&1)||v[j].deg>0)continue;
            ret=j;
            if((i&1)||v[j].fa!=0)break;
        }
        printf("%d %d %d\n",v[ret].a[1],v[ret].a[2],v[ret].a[3]);
        v[ret].jud=true;
        v[v[ret].fa].deg--;
    }
    return 0;
}
posted @ 2022-09-27 17:54  gtm1514  阅读(28)  评论(0编辑  收藏  举报