返回顶部

CSP模拟6

第一场就保龄了,开门红

A. 排序

题目给出的是一个排列,所以一定会通过有限次操作来使操作有序。 (话说这题上来就搞诈骗

由于数据范围很小,我们直接 O(n2) 暴力枚举即可。

而你需要操作逆序对个数次,所以每次交换需要让逆序对的个数减一,所以只需要每次交换值相邻的两个就可以了。

code
#include<bits/stdc++.h>
using namespace std;
const int M=1010;
int n,ans;
int a[M];
struct abc{
	int x,y;
}da[M*M];
bool cmp(abc x,abc y){
	if(x.x==y.x){
		return a[x.y]>a[y.y];
	}
	return x.x<y.x;
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
		for(int j=1;j<i;j++){
			if(a[j]>a[i]){
				ans++;
				da[ans].x=j;
				da[ans].y=i;
			}
		}
	}
	printf("%d\n",ans);
	sort(da+1,da+ans+1,cmp);
	for(int i=1;i<=ans;i++){
		printf("%d %d\n",da[i].x,da[i].y);
	}
	return 0;
}

B. Xorum

神奇的推式子

你已知一个奇数,需要通过两种操作来得到1。也就是说,要不断地消去该奇数每一位上的1。

设你现在的一个奇数为 x

x 的最后一位挪至与第一位对齐得到 y

x 异或 y 得到 z

z 加上 y 得到 n

y 加上 y 得到 p

n 异或 p 得到 m

m 异或 x 得到 q

然后你利用这个 q 就可以消掉 y 除最后一位上的1,也就是 x 最高位上的1,这样就可以消掉一个1,不断重复此过程即可。

11x110000000000000y101z=x+y1011n=z+y11000000000000000p=y+y111m=np100000000000000q=mx

code
#include<bits/stdc++.h>
using namespace std;
const long long M=1e5+10;
long long n,cnt;
struct abc{
	long long op,x,y;
}da[M];
long long solve(long long x){
	long long s=(x>>1),y=x;
	while(s){
		da[++cnt].op=1; da[cnt].x=y; da[cnt].y=y;
		y<<=1,s>>=1;
	}
	long long z=(x^y);
	da[++cnt].op=2; da[cnt].x=x; da[cnt].y=y;
	long long t=z+y;
	da[++cnt].op=1; da[cnt].x=z; da[cnt].y=y;
	long long p=y+y;
	da[++cnt].op=1; da[cnt].x=y; da[cnt].y=y;
	long long e=(t^p);
	da[++cnt].op=2; da[cnt].x=t; da[cnt].y=p;
	long long q=(e^x);
	da[++cnt].op=2; da[cnt].x=e; da[cnt].y=x;
	while(y!=(y&(-y))){
		if(y&q){
			da[++cnt].op=2; da[cnt].x=y; da[cnt].y=q;
			y=y^q;
		}
		da[++cnt].op=1; da[cnt].x=q; da[cnt].y=q;
		q=q<<1;
	}
	da[++cnt].op=2; da[cnt].x=y; da[cnt].y=x;
	return y^x;
}
int main(){
	scanf("%lld",&n);
	while(n!=1){
		n=solve(n);
	}
	printf("%lld\n",cnt);
	for(long long i=1;i<=cnt;i++){
		printf("%lld %lld %lld\n",da[i].op,da[i].x,da[i].y);
	}
	return 0;
}

C. 有趣的区间问题

一个类似 CDQ 分治的做法,对于每个区间,最大值和最小值的位置可以在中点左边也可以是右边,所以一共有四种情况。

  • 最大值最小值都在左边:用一个指针维护右区间的位置即可
  • 最大值最小值都在右边:同上
  • 最大值在左最小值在右:用一个桶存 popcount 的值,对于每一个最大值,找出右边最大值小于最小值的位置统计答案
  • 最大值在右最小值在左:扩展时一个用 ,一个用 <,其余同上
code
#include<bits/stdc++.h>
using namespace std;
const long long M=1e6+10;
struct abc{
    long long id1,id2;
}da[M];
long long n,ans;
long long a[M],num[M];
long long t[70];
inline long long read(){
    long long 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-'0';
        ch=getchar();
    }
    return x*f;
}
inline long long lowbit(long long x){
    return x&(-x);
}
inline long long getnum(long long x){
    long long now=x,num=0;
    while(now){
        now-=lowbit(now);
        num++;
    }
    return num;
}
inline void clear(){
    for(int i=0;i<=65;i++){
        t[i]=0;
    }
    return;
}
void solve(long long l,long long r){
    if(l==r){
        ans++;
        return;
    }
    long long mid=l+r>>1;
    solve(l,mid); solve(mid+1,r);
    da[mid].id1=da[mid].id2=mid;
    for(long long i=mid-1;i>=l;i--){
        da[i].id1=a[da[i+1].id1]>a[i]?da[i+1].id1:i;
        da[i].id2=a[da[i+1].id2]<a[i]?da[i+1].id2:i;
    }
    da[mid+1].id1=da[mid+1].id2=mid+1;
    for(long long i=mid+2;i<=r;i++){
        da[i].id1=a[da[i-1].id1]>a[i]?da[i-1].id1:i;
        da[i].id2=a[da[i-1].id2]<a[i]?da[i-1].id2:i;
    }
    long long j=mid+1;
    for(long long i=mid;i>=l;i--){
        while(j<=r&&a[da[j].id2]>=a[da[i].id2]&&a[da[j].id1]<=a[da[i].id1]){
            j++;
        }
        if(num[da[i].id1]==num[da[i].id2]){
            ans+=j-1-mid;
        }
    }
    long long i=mid;
    for(long long j=mid+1;j<=r;j++){
        while(i>=l&&a[da[i].id2]>=a[da[j].id2]&&a[da[i].id1]<=a[da[j].id1]){
            i--;
        }
        if(num[da[j].id1]==num[da[j].id2]){
            ans+=mid-i;
        }
    }
    clear();
    j=mid+1;
    long long k=mid+1;
    for(long long i=mid;i>=l;i--){
        while(j<=r&&a[da[j].id1]<=a[da[i].id1]){
            t[num[da[j].id2]]++;
            j++;
        }
        while(k<j&&a[da[k].id2]>=a[da[i].id2]){
            t[num[da[k].id2]]--;
            k++;
        }
        ans+=t[num[da[i].id1]];
    }
    clear();
    i=k=mid;
    for(long long j=mid+1;j<=r;j++){
        while(i>=l&&a[da[i].id1]<a[da[j].id1]){
            t[num[da[i].id2]]++;
            i--;
        }
        while(k>i&&a[da[k].id2]>a[da[j].id2]){
            t[num[da[k].id2]]--;
            k--;
        }
        ans+=t[num[da[j].id1]];
    }
}
int main(){
    n=read();
    for(long long i=1;i<=n;i++){
        a[i]=read();
        num[i]=getnum(a[i]);
    }
    solve(1,n);
    cout<<ans<<'\n';
	return 0;
}

D. 无聊的卡牌问题

由于一次拿三张牌,所以我们将三个一组进行捆绑,利用一个栈实现。

可以发现,一些牌的选择是有先后顺序的,所以可以进行拓扑排序,每次拿对其他牌限制多的即可。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int M=210;
struct abc{
    int x,y,z;
}da[M<<1];
int n,num,top,cnt1,cnt2,op;
int st[M<<3],in[M<<1],out[M<<1];
bool z[M<<3],zz[M<<1];
vector<int>h[M<<1];
priority_queue<pair<int,int>,vector<pair<int,int> >,less<pair<int,int> > >p,q;
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-'0';
        ch=getchar();
    }
    return x*f;
}
int main(){
    n=read(); num=n<<1;
    for(int i=1;i<=n*3;i++){
        int a=read();
        z[a]=1;
    }
    for(int i=1;i<=n*6;i++){
        if(top>=2&&z[i]&&z[st[top]]&&z[st[top-1]]){
            da[++cnt1]=(abc){st[top-1],st[top],i};
            top-=2;
        }
        else if(top>=2&&!z[i]&&!z[st[top]]&&!z[st[top-1]]){
            ++cnt2;
            da[n+cnt2]=(abc){st[top-1],st[top],i};
            top-=2;
        }
        else {
            st[++top]=i;
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=n+1;j<=n+n;j++){
            if(da[i].x<da[j].x&&da[j].z<da[i].z){
                h[j].push_back(i);
                in[i]++; out[j]++;
            }
            if(da[j].x<da[i].x&&da[i].z<da[j].z){
                h[i].push_back(j);
                in[j]++; out[i]++;
            }
        }
    }
    for(int i=1;i<=(n<<1);i++){
        if(in[i]==0){
            if(i<=n){
                p.push(make_pair(out[i],i));
            }
            else {
                q.push(make_pair(out[i],i));
            }
        }
    }
    while(num){
        pair<int,int> x;
        if(op==0){
            x=p.top();
            p.pop();
        }
        else {
            x=q.top();
            q.pop();
        }
        if(zz[x.second]){
            continue;
        }
        zz[x.second]=1;
        cout<<da[x.second].x<<' '<<da[x.second].y<<' '<<da[x.second].z<<'\n';
        num--;
        for(int i=0;i<h[x.second].size();i++){
            int y=h[x.second][i];
            in[y]--;
        }
        for(int i=1;i<=(n<<1);i++){
            if(zz[i]==0&&in[i]==0){
                if(i<=n){
                    p.push(make_pair(out[i],i));
                }
                else {
                    q.push(make_pair(out[i],i));
                }
            }
        }
        op=op^1;
    }
    return 0;
}
posted @   Airposs  阅读(30)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
点击右上角即可分享
微信分享提示