CSP 模拟 6

T1 排序

基本是原题 CF1375E

好像是简单题,考虑这个排列 \(\pi\) 的逆排列 \(\pi^{-1}\)(如果排列是 \(a_i\),则逆排列为 \(b_{a_i}=i\)),因为逆序对的定义是序列编号和数值大小关系不一样,所以在逆排列中逆序对和原排列相同,对逆排列做冒泡排序,因为冒泡排序交换的是相邻值的位置,对其他值没有影响,所以对逆排列冒泡排序的所有逆序对即为答案

点击查看代码
#include<bits/stdc++.h>
#define N 1005
#define pii pair<int,int>
#define mp make_pair
using namespace std;

int n,a[N],b[N],tot,pos[N];
pii ans[N*N];
// 爆零就爆零,天天好心情
int main(){
    cin>>n;
    for(int i=1;i<=n;i++)
        scanf("%d",a+i),b[a[i]]=i;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n-i;j++)
            if(b[j]>b[j+1]){
                swap(b[j],b[j+1]);
                ans[++tot]=mp(b[j],b[j+1]);
            }
    cout<<tot<<endl;
    for(int i=1;i<=tot;i++)
        printf("%lld %lld\n",ans[i].first,ans[i].second);
}

T2 Xorum

原题 CF1427E

很有意思的构造题

对于这个奇数 \(x\) 而言,我们不断把它的最高位消去即可得到 1,所以考虑如何得到这个最高位

构造方案(建议模拟一遍):

  1. \(x\) 最高位和最低位对齐,设这个数为 \(y\)
  2. \(x\oplus y\) 得到一个数为 \(z\),则这个数中只有最高位被消掉
  3. \(z+y\) 构造出来一个 \(sum\),则最高位被补上,\(y\) 除最低位均左移一位
  4. \(sum\oplus x \oplus y*2\) 则只剩下最高位加一位
  5. 用最高位加一位消掉 \(y\) 其他的得到最高位
点击查看代码
#include<bits/stdc++.h>
#define N 1000005
#define int long long
using namespace std;

int x,a[N],vis[64],tot;
struct node{int typ,a,b;}ans[N];
unordered_map<int,bool> exi;
// 2500 2500 2800 3200
signed main(){
    cin>>x;
    assert(x!=262143);
    while(true){
        if(x==1) break;
        int highbit=0;
        for(int i=20;i>=0;i--)
            if(x&(1ll<<i)){
                highbit=i;
                break;
            }
        int y=x;
        for(int i=0;i<highbit;i++){
            ans[++tot]=(node){1,y,y};
            y<<=1;
        }
        if(!vis[highbit+1]){
            ans[++tot]=(node){2,y,x};
            int tmp=y^x;
            ans[++tot]=(node){1,tmp,y};
            int temp=tmp+y;
            ans[++tot]=(node){2,temp,x};
            temp^=x;
            ans[++tot]=(node){1,y,y};
            ans[++tot]=(node){2,y*2,temp};
            vis[highbit+1]=1;
        }
        for(int i=highbit+1;(1ll<<i)<=y;i++){
            if(!vis[i]){
                ans[++tot]=(node){1,(1ll<<(i-1)),(1ll<<(i-1))};
                vis[i]=true;
            }
            if(y&(1ll<<i)){
                ans[++tot]=(node){2,y,(1ll<<i)};
                y^=(1ll<<i);
            }
        }
        vis[highbit]=true;
        ans[++tot]=(node){2,x,(1ll<<highbit)};
        x^=(1ll<<highbit);
    }
    cout<<tot<<endl;
    for(int i=1;i<=tot;i++)
        printf("%lld %lld %lld\n",ans[i].typ,ans[i].a,ans[i].b);
}

T3 有趣的区间问题

原题 CF1609F

序列分治,将序列分成两个部分 \([l,mid],(mid,r]\),然后只考虑跨越中点的区间答案,递归下去

定义 \(max(l,r)=\max_{i=l}^ra_i\)\(min(l,r)=\min_{i=l}^ra_i\)

双指针,设当前走到的左端点为 \(L\),定义 \(p1\) 为满足 \(max(mid+1,R)\not=max(L,R)\and min(mid+1,R)\not=min(L,R)\)\(R\) 的最大值,\(p2\) 为满足 \(max(mid+1,R)\not=max(L,R)\or min(mid+1,R)\not=min(L,R)\)\(R\) 的最大值,对于以当前区间为左端点 \(L\) 的情况分类讨论:

  • \(R\in (mid,p1]\)\(\max\)\(\min\) 只和左区间相关,如果左区间 \(\max\)\(\min\) \(popcount\) 相等则统计区间长度
  • \(R\in (p1,p2]\),对于左侧的 \(\max\)\(\min\) 开桶维护即可
  • \(R\in (p2,r]\) 直接维护右区间对于 \(popcount\) 相等的后缀和即可
点击查看代码
#include<bits/stdc++.h>
#define N 1000005
#define int long long
#define bitcnt __builtin_popcountll
using namespace std;

int n,a[N],cnt[N],ans,mx[N],mn[N];
int buk1[64],buk2[64];
void solve(int l,int r){
    if(l==r){ans++;return;}
    int mid=(l+r)>>1;
    memset(buk1,0,sizeof(buk1));
    memset(buk2,0,sizeof(buk2));
    mx[mid]=mn[mid]=mid;mx[mid+1]=mn[mid+1]=mid+1;
    for(int i=mid+2;i<=r;i++) 
        mx[i]=a[i]>a[mx[i-1]]?i:mx[i-1],
        mn[i]=a[i]<a[mn[i-1]]?i:mn[i-1];
    for(int i=mid-1;i>=l;i--) 
        mx[i]=a[i]>a[mx[i+1]]?i:mx[i+1],
        mn[i]=a[i]<a[mn[i+1]]?i:mn[i+1];
    int p1=mid+1,p2=mid+1;
    for(int i=mid;i>=l;i--){
        while(p1<=r&&a[mx[i]]>=a[mx[p1]])
            buk1[bitcnt(a[mn[p1++]])]++;
        while(p2<=r&&a[mn[i]]<a[mn[p2]])
            buk2[bitcnt(a[mn[p2++]])]++;
        if(cnt[mx[i]]==cnt[mn[i]]) ans+=min(p1,p2)-mid-1;
        if(p2<p1) ans+=buk1[cnt[mx[i]]]-buk2[cnt[mx[i]]]; 
    }
    p1=p2=mid;
    memset(buk1,0,sizeof(buk1));
    memset(buk2,0,sizeof(buk2));
    for(int i=mid+1;i<=r;i++){
        while(p1>=l&&a[mx[i]]>a[mx[p1]])
            buk1[bitcnt(a[mn[p1--]])]++;
        while(p2>=l&&a[mn[i]]<=a[mn[p2]])
            buk2[bitcnt(a[mn[p2--]])]++;
        if(cnt[mx[i]]==cnt[mn[i]]) ans+=mid-max(p1,p2);
        if(p1<p2) ans+=buk1[cnt[mx[i]]]-buk2[cnt[mx[i]]]; 
    }
    solve(l,mid);solve(mid+1,r);
}

signed main(){
    cin>>n;
    for(int i=1;i<=n;i++)
        scanf("%lld",a+i),
        cnt[i]=bitcnt(a[i]);
    solve(1,n);
    cout<<ans;
}

T4 无聊的卡牌问题

原题 CF1427F

考虑贪心,塞进一个栈里,如果顶端有三个相同先后手的点,则弹出,将三个点压成一个点连上栈顶节点,表示比它后取,然后拓扑排序即可,但是需要注意的是,必须留一个后手的根,避免出现错误,正确性证明考虑每次拿一个先手或后手产生的影响

点击查看代码
#include<bits/stdc++.h>
#define N 1205
using namespace std;

int n,visf[N],stk[N],top,id[N],tot,Cnt[N];
int fuck[N][4],fa[N],typ[N],deg[N],vis[N];
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<<3)+(x<<1)+(ch^48);ch=getchar();}
	return x*f;
}

int main(){
    cin>>n;
    for(int i=1;i<=n*3;i++){int x=read();visf[x]=1;}
    for(int i=1;i<=n*6;i++){
        if(!top||typ[stk[top]]!=visf[i]){
            typ[++tot]=visf[i];
            stk[++top]=tot;
            fuck[tot][++Cnt[tot]]=i;
        }
        else{
            fuck[stk[top]][++Cnt[stk[top]]]=i;
            if(Cnt[stk[top]]==3) fa[stk[top]]=stk[top-1],top--;
        }
    }
    // for(int i=1;i<=tot;i++)
    //     printf("%d %d %d %d %d\n",fa[i],typ[i],fuck[i][1],fuck[i][2],fuck[i][3]);
    int cnt=0;
    for(int i=1;i<=tot;i++){
        if(!fa[i]&&!typ[i]) cnt++;
        deg[fa[i]]++;
    }
    int now=1;
    // cout<<tot<<endl;
    for(int i=1;i<=tot;i++){
        for(int j=1;j<=tot;j++)
            if(!deg[j]&&typ[j]==now&&!vis[j]){
                if(cnt==1&&i<tot&&(!fa[j])&&(!typ[j])) continue;
                vis[j]=1;deg[fa[j]]--;
                if(!typ[j]&&!fa[j]) cnt--;
                printf("%d %d %d\n",fuck[j][1],fuck[j][2],fuck[j][3]);
                break;
            }
        now^=1;
    }
}
posted @ 2023-07-27 20:01  Rolling_star  阅读(41)  评论(0编辑  收藏  举报