“盛大游戏杯”第15届上海大学程序设计联赛夏季赛暨上海高校金马五校赛-专题

38 篇文章 0 订阅
34 篇文章 0 订阅

题目链接:点击打开链接


说明:前五题简单题就不写了。。。


F:这是裸nlogn的求最长上升序列算法,前后各求一下取小就行了。

#include<iostream>
#include<algorithm>
#include<stdio.h>
#include<string.h>
#include<queue>
using namespace std;
typedef long long ll;
const int mx = 5e5+10,mod = 1e9+7;
int n,m; 
int fron[mx],back[mx],a[mx],b[mx];
int main(){
	fron[0]=0;
	while(~scanf("%d",&n)){
		for(int i=0;i<n;i++) scanf("%d",a+i);
		int len=1;
		b[0] = a[0];
		for(int i=1;i<n;i++){
			int k = lower_bound(b,b+len,a[i])-b;
			if(k==len) len++;
			b[k]=a[i], fron[i]=k;
		}
		len = 1, back[n-1] = 0;
		b[0] = a[n-1];
		for(int i=n-2;i>=0;i--){
			int k = lower_bound(b,b+len,a[i])-b;
			if(k==len) len++;
			b[k]=a[i], back[i]=k;
		}
		int ans = 0; 
		for(int i=0;i<n;i++)  ans = max(ans,min(fron[i],back[i])*2+1);
		printf("%d\n",ans);
	}
	return 0;
}
G:贪心思路。按攻击力从大到下排序,相同的按血量从小到大排序。然后比较。

#include<iostream>
#include<algorithm>
#include<stdio.h>
#include<string.h>
#include<queue>
#include<set>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int mx = 20;
int n,m;
struct data{
	int v,a;
	data(){}
	data(int vv,int aa){ v = vv, a = aa ; }
	bool operator < (data A)const{
	     if(a==A.a) return v<A.v;
	     return a>A.a; 
	}
}s[mx],q[mx];
int main(){
	int t;
	scanf("%d",&t);
	while(t--){
		scanf("%d",&n);
		for(int i=0;i<n;i++) scanf("%d%d",&s[i].v,&s[i].a);
		for(int i=0;i<n;i++) scanf("%d%d",&q[i].v,&q[i].a);
		sort(q,q+n);
		int j = 0, k = 0;
		while(j<n&&k<n){
			int vj = (q[k].v-1)/s[j].a+1, vk = (s[j].v-1)/q[k].a+1;
			if( vj < vk ) s[j].v -= vj*q[k].a, k++;
			else if( vj > vk ) q[k].v -= vk*s[j].a, j++;
			else j++ ,k++;  
		}
		puts(k==n? "NO":"YES");
	}
	return 0;
}
H:考虑到n才2w,直接用vector全部存入容器中,直接暴力。
#include<iostream>
#include<algorithm>
#include<stdio.h>
#include<string.h>
#include<queue>
#include<set>
#include<vector>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int mx = 2e4+10;
int n,m,a[mx];
vector <int> st[mx/2];
int main(){
	int t;
	scanf("%d",&t);
	while(t--){
		scanf("%d%d",&n,&m);
		for(int i=0;i<n;i++) scanf("%d",a+i);
		for(int i=0;i<=n/2;i++) st[i].clear();
		for(int i=1;i<=n/2;i++){
			int j = 0;
			while(j*i<n){
				st[i].push_back(a[j*i]);
				j++;
			}
			sort(st[i].begin(),st[i].end());
		}
		while(m--){
			int x,y;
			scanf("%d%d",&x,&y);
			if(x>n/2&&x<n){
				if(y==1) printf("%d\n",max(a[x],a[0]));
				else if(y==2) printf("%d\n",min(a[x],a[0]));
				else puts("-1");
			}
			else if(x>=n) printf("%d\n",y==1? a[0]:-1);
			else printf("%d\n",y>st[x].size()? -1:st[x][st[x].size()-y]);
		}
	}

I:动态规划面积从小到大,已知n<=2&&m<=2的棋盘必为必败态,然后规划每个点是否能进入至少一个必败态,则其就是必胜态,反之必败。

#include<iostream>
#include<algorithm>
#include<stdio.h>
#include<string.h>
#include<queue>
#include<set>
#include<vector>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int mx = 5e2+100;
int n,m,pri[mx],top;
bool vis[mx],vic[mx][mx];
void init(){
	for(int i=2;i<mx;i++){
		if(!vis[i]) pri[top++] = i;
		for(int j=0;j<top&&pri[j]*i<mx;j++){
			vis[pri[j]*i] = 1;
			if(i%pri[j]==0) break;
		}
	}
}
int main(){
	int t,top = 0;
	init();
	memset(vic,0,sizeof(vic));
	for(int i=1;i<=500;i++){
		for(int j=1;j<=500;j++){
			if(i<=2&&j<=2) continue;
			int k = 0;
			while(!vic[i][j]&&pri[k]<i){
				if(!vic[i-pri[k]][j])
				vic[i][j] = 1;
				k++;
			}   k = 0;
			while(!vic[i][j]&&pri[k]<j){
				if(!vic[i][j-pri[k]])
				vic[i][j] = 1;
				k++;
			}   k = 0;
			while(!vic[i][j]&&pri[k]<j&&pri[k]<i){
				if(!vic[i-pri[k]][j-pri[k]])
				vic[i][j] = 1;
				k++;
			} 
		}
	}
	scanf("%d",&t);
	while(t--){
		scanf("%d%d",&n,&m);
		puts(vic[n][m]? "Sora":"Shiro");
	}
	return 0;
}
J:代码丢了(不想写了。。。)简单说你会发现要是没有233的限制,那么每个n累加的值应该是(n*n+n)/2,那么用这个值减去i的1-232和后面的n-232到n就是答案了。这两个分别用到O(n)的求1-n的欧拉函数和O(sqrt(n))的求1-n的欧拉函数。

K:二分求答案

#include<iostream>
#include<algorithm>
#include<stdio.h>
#include<string.h>
#include<queue>
#include<set>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int mx = 1e5+10;
int n,m;
struct data{
	int ai,bi;
}s[mx];
bool cmp_1(data A,data B){
	if(A.bi==B.bi)  return A.ai>B.ai;
	return A.bi<B.bi;
}
bool check(int x,int si){
	int sum = 0, cnt = 0;
	for(int i=0;i<n;i++){
		if(s[i].ai>=x){
			cnt++;
			sum += s[i].bi;
		}
		if(cnt==si) break;
	}
	return cnt==si&&sum<=m;
}
int main(){
	int t;
	scanf("%d",&t);
	while(t--){
		scanf("%d%d",&n,&m);
		for(int i=0;i<n;i++) scanf("%d%d",&s[i].ai,&s[i].bi);
		sort(s,s+n,cmp_1);
		int top = 0,sum = m;
		while(top<n&&sum>=s[top].bi){
			sum -= s[top].bi;
			top++;
		}
		int l = 1,r = 1e4;
		while(l<=r){
			int mid = (l+r)>>1;
			if(check(mid,top)) l = mid+1;
			else r = mid-1;
		}
		printf("%d %d\n",top,r);
	}
	return 0;
}

M:线段树维护一个点增加最多的值和减少最多的值。当然用到了懒人思想。

#include<iostream>
#include<algorithm>
#include<stdio.h>
#include<string.h>
#include<queue>
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int mx = 1e5+10,mod = 1e9+7;
int n,m,num[mx<<2],q,a[mx];
int just[mx<<2],nage[mx<<2]; 
struct data{
	int MAX,MIN;
	data(){}
	data(int maxx,int minn){ MIN = minn, MAX = maxx ; }
};
void Splite(int rt){
	just[rt<<1] = max(just[rt<<1],num[rt<<1]+just[rt]);
	just[rt<<1|1] = max(just[rt<<1|1],num[rt<<1|1]+just[rt]);;
	nage[rt<<1] = min(nage[rt<<1],num[rt<<1]+nage[rt]);
	nage[rt<<1|1] = min(nage[rt<<1|1],num[rt<<1|1]+nage[rt]);
	num[rt<<1] += num[rt];
	num[rt<<1|1] += num[rt];
	just[rt] = nage[rt] = num[rt] = 0;
}
void update(int L,int R,int l,int r,int rt,int v){
	if(L<=l&&R>=r){
		num[rt]+=v;
		just[rt] = max(just[rt],num[rt]);
	    nage[rt] = min(nage[rt],num[rt]);
		return ;
	}
	if(num[rt]||just[rt]||nage[rt])
	Splite(rt);
	int mid = (l+r)>>1;
	if(L<=mid) update(L,R,lson,v);
	if(R>mid)  update(L,R,rson,v); 
}
data query(int L,int l,int r,int rt){
	if(l==r) return data(just[rt],nage[rt]);
	if(just[rt]||nage[rt]||num[rt])
	Splite(rt);
	int mid = (l+r)>>1;
	if(L<=mid) return query(L,lson);
	return query(L,rson);
}
int main(){
	int t;
	scanf("%d",&t);
	while(t--){
		scanf("%d%d",&n,&q);
		for(int i=1;i<=n;i++) scanf("%d",a+i);
		memset(num,0,sizeof(num));
		memset(just,0,sizeof(just));
		memset(nage,0,sizeof(nage));
		while(q--){
			int d;
			scanf("%d",&d);
			if(d==1){
				int x,y,z;
				scanf("%d%d%d",&x,&y,&z);
				update(x,y,1,n,1,z);
			}else{
				scanf("%d",&d);
				data t = query(d,1,n,1);
				printf("%d\n",max(abs(a[d]+t.MAX),abs(a[d]+t.MIN)));
			}
		}
	}
	return 0;
}
O: 题目告诉我们当然主人公够聪明,但是不知道传送门是怎么对应的,那么此时可以看做只有两个操作。一:不进入传送门直接走。二:进入离出发点最近的传送门(毕竟不知道肯定是选择最近的啊)。然后用一个bfs套dfs将传送门出口作为新的出发点,看看是否能到达终点,若有一种不能则为-1.

#include<iostream>
#include<algorithm>
#include<stdio.h>
#include<string.h>
#include<queue>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int mx = 1e2+10,co[4][2]={1,0,0,1,-1,0,0,-1};
int n,m,cnt1,ans;
char map[mx][mx];
struct node{
	int x,y,k;
	node(){}
	node(int xx,int yy,int kk){ x = xx, y = yy, k = kk; }
}a[5],b[5];
bool vis2[5];
int bfs(int beg,int eng){
	queue<node> skt;
	bool vis[mx][mx],flag = 0;
	int sum = inf;
	memset(vis,0,sizeof(vis));
	skt.push(node(beg,eng,0));
	vis[beg][eng]=1;
	while(!skt.empty()){
		node no = skt.front();
		skt.pop();
		for(int i=0;i<4;i++){
			node po = node(no.x+co[i][0],no.y+co[i][1],no.k+1);
			if(po.x<0||po.y<0||po.x>=n||po.y>=m) continue; 
			if(vis[po.x][po.y]||map[po.x][po.y]=='#') continue;
			vis[po.x][po.y] = 1;
			if(map[po.x][po.y]=='T') return min(sum,po.k);
		    if(!flag&&map[po.x][po.y]=='i'){
		    	map[po.x][po.y] = '.';
		    	for(int j=0;j<cnt1;j++){
		    		if(vis2[j]) continue;
		    		vis2[j] = 1;
		    		if(sum==inf)  sum = bfs(b[j].x,b[j].y)+po.k+1;
		    		else sum = max(sum,bfs(b[j].x,b[j].y)+po.k+1);
		    		vis2[j] = 0;
				}
				map[po.x][po.y] = 'i';
				flag = 1;
			}
			skt.push(po);
		}
	}
	return sum;
}
int main(){
	int t,beg,eng;
	scanf("%d",&t);
	while(t--){
		ans = 0, cnt1 = 0;
		scanf("%d%d",&n,&m);
		for(int i=0;i<n;i++){
			scanf("%s",map[i]);
			for(int j=0;j<m;j++){
				if(map[i][j]=='S') beg = i, eng = j;
				else if(map[i][j]=='o') b[cnt1++]=node(i,j,0);
			}
		}
		ans = bfs(beg,eng); 
		printf("%d\n",ans>=inf? -1:ans);
	}
	return 0;
}







  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值