2019中国大学生程序设计竞赛(CCPC) - 网络选拔赛(部分)

^ & ^(规律)

题意: 给出A,B ,找出C 使得式子(A^ C)&(B^ C)最小,如果有多个C符合,输出最小的C , 式子最小就是等于0,规律为下表格

ABCans
1110
101/00
011/00
0000

中间两行C可以等于1或0,这里选择0 , 那么答案C就是 A&B

#include <cstdio>
#include <queue>
#include <algorithm>
using namespace std ;
typedef long long ll ;  
const int N=1010 ; 
int main(){
	int t; scanf("%d",&t) ;
	while(t--){
		ll a,b; scanf("%lld%lld",&a,&b) ; 
		ll c=a&b ; 
		printf ("%lld\n",c?c:1) ; 
	}
	return 0 ; 
} 

array(权值线段树)

题意:
有一个长度为n的数组,给出m个操作
有两种操作:

  1. (1,pos) - a[pos] += 10,000,000
  2. (2,r,k) , 询问数组中最小的数,不等于a[1~r] 并且 大于等于k的数是多少。
    记上一次询问的答案为ans(第一次的ans默认为0)
    输入(1,t1) (2,t2,t3) pos=t1^ ans , r=t2^ ans , k=t3^ans ;

题解: 因为本题问的是不属于a[1~r]的数,所以用到权值线段树,线段树 的节点的值转化为数的值,然后询问的时候就是询问[k,n]之间大于r的数

#include <cstdio>
#include <queue>
#include <string.h>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N=1e5+10 ; 
const int inf=1e9+7 ; 
int a[N],s[N] ; //数组的数 s[i]表示值为i的数最大的下标 
struct node{
	int l,r ; 
	int id,v ; //id为节点的值,v为该值的最大下标 
}tree[N*4] ; 
void push_up(int rt){
	if(tree[rt<<1].id>tree[rt<<1|1].id){
		tree[rt].id = tree[rt<<1].id ;
		tree[rt].v = tree[rt<<1].v ;  
	}
	else{
		tree[rt].id = tree[rt<<1|1].id ;
		tree[rt].v = tree[rt<<1|1].v ;  
	}
}
void built(int rt,int l,int r){
	tree[rt].l=l , tree[rt].r=r ; 
	if(l==r){
		tree[rt].id=s[l] , tree[rt].v=l ; 
		return ; 
	}
	int mid=(l+r)>>1 ; 
	built(rt<<1,l,mid) ; 
	built(rt<<1|1,mid+1,r) ;
	push_up(rt) ; 
}
void update(int rt,int idx,int val){
	if(tree[rt].l==tree[rt].r)	{
		tree[rt].id=val ; 
		return ; 
	}
	int mid=(tree[rt].l+tree[rt].r)>>1 ; 
	if(idx<=mid)	update(rt<<1,idx,val) ; 
	else	update(rt<<1|1,idx,val) ; 
	push_up(rt) ; 
}
int query(int rt,int l,int r,int val){
	if(tree[rt].l>r || tree[rt].r<l)	return -1 ; 
	if(tree[rt].l==tree[rt].r){
		if(tree[rt].id>val)	return tree[rt].v ; 
		else	return -1 ;  
	}
	int ans=-1 ; 
	if(tree[rt<<1].id>val)		ans=query(rt<<1,l,r,val) ; 
	if(ans==-1&&tree[rt<<1|1].id>val)	ans=query(rt<<1|1,l,r,val) ;
	return ans ; 
}
int main(){
	int t; scanf("%d",&t) ; 
	ll add=10000000 ; 
	while(t--){
		int n,m; scanf("%d%d",&n,&m) ;
//		memset(s,0,sizeof(0)) ; 
		for(int i=1 ; i<=n ; ++i)	scanf("%d",&a[i]),s[a[i]]=i ;  
		built(1,1,n) ;
		int ans=0 ;
		while(m--){
			int x; scanf("%d",&x) ; 
			if(x==1){
				int pos; scanf("%d",&pos) ; 
				pos = pos^ans ; 
				update(1,a[pos],inf) ; 		
			}
			else{
				int r,k ; scanf("%d%d",&r,&k) ;
				r=r^ans , k=k^ans ;
				if(k>n)	ans=k ;
				else{ 
					ans = query(1,k,n,r) ; //询问在[k,n]之间大于r的下标 
					ans = ans==-1?n+1:ans ; 
				}  
				printf ("%d\n",ans) ; 
			}
		}
	}
	return 0 ; 
}

Windows Of CCPC(模拟打表)

题意:
在这里插入图片描述
题意: 按照规律打表即可

#include <cstdio>
using namespace std;
typedef long long ll;
const int N=1e4+10 ; 
char g[N][N] ;  
int main(){
    g[1][1]=g[1][2]=g[2][2]='C' ; 
    g[2][1]='P'; 
    for(int i=2 ; i<=10; ++i){
        int a=1<<(i-2) , b=1<<(i-1), c=1<<i ; 
        for(int j=1 ; j<=b ; ++j)
            for(int k=b+1 ; k<=c ; ++k)
                g[j][k] = g[j][k-b] ; 
        for(int j=b+1 ; j<=c ; ++j)
            for(int k=1 ; k<=b ; ++k){
                if(g[j-b][k]=='P')    g[j][k]='C' ; 
                else    g[j][k]='P' ; 
            }                
        for(int j=b+1 ; j<=c ; ++j)
            for(int k=b+1 ; k<=c ; ++k)
                g[j][k] = g[j-b][k-b] ; 
    }
    int t ; scanf("%d",&t) ; 
    while(t--){
        int k; scanf("%d",&k) ; 
        int pos=1<<k ;  
        for(int i=1 ; i<=pos; ++i){
            for(int j=1 ; j<=pos ; ++j)
                printf ("%c",g[i][j]) ; 
            printf ("\n") ; 
        }
    }
    return 0 ; 
}

Shuffle Card(模拟+队列)

题意: 1~n张牌按顺序排列,m个操作,每次操作将编号为a的牌抽出来放在最上面,问m轮操作之后牌从上到下的编号。
题意: 记录m个操作,然后从后面放入队列,如果已经放入队列就不用再放,然后按队列弹出,最后没有抽出的牌按顺序输出即可。

#include <cstdio>
#include <queue>
using namespace std;
typedef long long ll;
const int N=1e5+10 ; 
int n,m ; 
bool vis[N] ;
int s[N] ;  
int main(){
	while(~scanf("%d%d",&n,&m)){
		int x ; 
		queue<int> q ; 
		for(int i=1 ; i<=n ; ++i)	scanf("%d",&x),vis[i]=false ; 
		for(int i=1 ; i<=m ; ++i)	scanf("%d",&s[i]) ; 
		for(int i=m ; i>0 ; --i)
			if(!vis[s[i]])		q.push(s[i]),vis[s[i]]=true ; 	
		while(!q.empty())	printf ("%d ",q.front()),q.pop() ; 
		for(int i=1 ; i<=n ; ++i)
			if(!vis[i])	printf ("%d ",i),vis[i]=true ; 
	}
	return 0 ; 
}

钓鱼(思维+优先队列)

题意: 现在有n条鱼,第i条鱼至少需要煮t[i]分钟,每钓一条鱼需要耗费k分钟,钓鱼的时候不能做其他操作,一口锅只能煮一条鱼,问钓完和煮完所有的鱼所耗费的时间。
题解: 在煮第i条鱼的时候,可以再抓t[i]/k条鱼上来,剩下的t[i]%k的时间有两种方案:

  1. 等鱼煮好放第二条鱼
  2. 去抓下一条鱼
    第一个方案是最佳方案,不会浪费多余时间,但是前提是有鱼可以煮,当没有鱼可以煮的时候只能选择第二个方案,是有消耗的,消耗的时间为k-t[i]%k,为了减少消耗,应该选择t[i]%k尽量大的时候去抓下一条鱼,所以使用优先队列,每次鱼不够的时候从队列弹出t[i]%k最大的部分,加上该消耗。
    参考博客
#include <cstdio>
#include <algorithm>
#include <queue>
using namespace std ; 
typedef long long ll ; 
const int N=1e5+10 ; 
int t[N] ; 
priority_queue<int> q ; 
bool cmp(int x,int y){
	return x>y ; 
}
int main(){
	int T ; scanf("%d",&T) ; 
	while(T--){
		int n,k ; scanf("%d%d",&n,&k) ; 
		for(int i=1 ; i<=n ; ++i)	scanf("%d",&t[i]) ; 
		sort(t+1,t+1+n,cmp) ; 
		while(!q.empty())	q.pop() ; 
		ll ans=k , pos=1 ; 
		for(int i=1 ; i<=n ; ++i){
			ans += t[i] ; 
			pos += t[i]/k ;
			if(pos<i){
				ans += k-q.top() ; 
				q.pop() ; 
			}
			q.push(t[i]%k) ; 
		}
		printf ("%lld\n",ans) ; 
	}
	return 0 ; 
}

Path(思维+优先队列)

题意: 给出一个n个点,m条边带权的有向图,每条路可以无限次走,给出q个询问,每次问第k小的路长是多少(路长走过路的权值之和)。
题解: 可以用优先队列实现,但是由于可能存在菊花图,队列可能会爆和超时,所以先将每个点所连接的边按权值从小到大排列,先所以点最小的边进队(队列存元素path[id-点的id,w该条路径的长度,i该点的第几条变]),然后弹出最小的路径,先有两个操作

  1. 若路径最后一条边连接的点还有下一条边,将路径的最后一条边换成该点连接的下一条边然后再次进入队伍,
  2. 若该点连接的下一个点的出度大于0,就将下一个点的最小一条边加入到队列当中。
    还有个小优化,用mx记录询问的最大k,然后当答案积累到了mx的时候就可以退出搜索。
    参考博文1参考博文2
#include <cstdio>
#include <queue>
#include <vector>
#include <algorithm>
using namespace std ; 
const int N=5e4+10 ;
struct node{
	int to,w ; 
	node(int a,int b){to=a,w=b;};
};
struct path{
	int id,w,i ; //w为路径的长度,是id的第i条路径 
	path(int a,int b,int c){id=a,w=b,i=c;} ;
	bool operator < (const path & d) const{return w>d.w;} 
};
vector<node> g[N];
int ans[N] ,query[N]; 
bool cmp(node x,node y){
	return x.w<y.w ; 
}
int main(){
	int T; scanf("%d",&T) ; 
	while(T--){
		int n,m,q ; scanf("%d%d%d",&n,&m,&q) ; 
		for(int i=1 ; i<=n ; ++i)	g[i].clear() ; 
		priority_queue<path> que ; 
		while(m--){
			int u,v,w; scanf("%d%d%d",&u,&v,&w) ; 
			g[u].push_back(node(v,w)) ; 
		}
		for(int i=1 ; i<=n ; ++i){
			sort(g[i].begin(),g[i].end(),cmp) ; 
			if(g[i].size())		que.push(path(i,g[i][0].w,0)) ; 
		}	
		int mx=0 ,pos=0 ; 
		for(int i=0 ; i<q ; ++i){
			scanf("%d",&query[i]) ; 
			mx = max(mx,query[i]) ; 
		}
		while(!que.empty()){
			path x=que.top() ; que.pop() ; 
			ans[pos++]=x.w ; 
			if(pos>mx)		break ; 
			if(x.i<g[x.id].size()-1)
				que.push(path(x.id,x.w-g[x.id][x.i].w+g[x.id][x.i+1].w,x.i+1)) ; 
			node nx = g[x.id][x.i] ; 
			if(g[nx.to].size()>0)	que.push(path(nx.to,x.w+g[nx.to][0].w,0)) ; 
		}
		for(int i=0 ; i<q ; ++i)	printf ("%d\n",ans[query[i]-1]) ; 
	}
	return 0 ; 
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值