【超好懂的比赛题解】2021 年四川省大学生程序设计竞赛

57 篇文章 0 订阅
12 篇文章 0 订阅

title : 2021 年四川省大学生程序设计竞赛
date : 2022-7-18
tags : ACM,练习记录
author : Linno


2021 年四川省大学生程序设计竞赛

题目链接:https://codeforces.com/gym/103117

进度:11/13

切题顺序:AKMBDHLJ

IF赛后补了,CG没看

A. Chuanpai

给定正整数 k,问有多少正整数对 (x, y) 满足 x + y = k 且 1 ≤ x ≤ y ≤ 6。

x 和 y 的可行范围很小,直接枚举即可。

//#pragma GCC optimize("Ofast", "inline", "-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define int long long
using namespace std;
const int N=2e5+7;
const int mod=1e9+7;

//int read(){	int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-') f=f*-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;}
//void write(int x){if(x>9) write(x/10);putchar(x%10+'0');}
void Solve(){
	int n,ans=0;
	cin>>n;
	for(int i=1;i<=6;++i){
		for(int j=i;j<=6;++j){
			if(i+j==n) ans++;
		}
	}
	cout<<ans<<"\n";
}

signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
//  freopen("in.cpp","r",stdin);
//  freopen("out.cpp","w",stdout);
	int T=1;
	cin>>T;
//	clock_t start,finish;
//	start=clock();
	while(T--){
		Solve();
	}
//	finish=clock();
//	cerr<<((double)finish-start)/CLOCKS_PER_SEC<<endl;
	return 0;
}

B. Hotpot

n 个人围成一圈吃火锅。从第一个人开始轮流行动。每个人有且 仅有一种喜欢的食物。轮到一个人行动时,如果锅里有他喜欢的 食物,他就全吃光,他的快乐值 +1。否则,他就会往锅里下一 些他喜欢吃的食物,并结束行动。问所有人共行动 m 次后每个 人的快乐值。

注意到两轮过后集合中的肯定是没有元素的,以2n为一个周期统计答案,最后再直接暴力跑剩下的次数即可。

//#pragma GCC optimize("Ofast", "inline", "-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define int long long
using namespace std;
const int N=2e5+7;
const int mod=1e9+7;

//int read(){	int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-') f=f*-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;}
//void write(int x){if(x>9) write(x/10);putchar(x%10+'0');}
int n,k,m,a[N],inq[N],cnt[N],ans[N];
void Solve(){
	memset(cnt,0,sizeof(cnt));
	memset(ans,0,sizeof(ans));
	memset(inq,0,sizeof(inq));
	cin>>n>>k>>m;
	for(int i=0;i<n;++i) cin>>a[i];
	for(int i=0;i<2*n;++i){  //计算两轮每个人的快乐数 
		if(inq[a[i%n]]){
			inq[a[i%n]]=0;
			++cnt[i%n];
		}else{
			inq[a[i%n]]=1;
		}
	}
	for(int i=0;i<n;++i){
		ans[i]+=m/(2*n)*cnt[i];
	}
	m%=(2*n);
	for(int i=0;i<m;++i){
		if(inq[a[i%n]]){
			inq[a[i%n]]=0;
			++ans[i%n];
		}else{
			inq[a[i%n]]=1;
		}	
	}
	for(int i=0;i<n;++i) cout<<ans[i]<<" \n"[i==n-1]; 
}

signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
//  freopen("in.cpp","r",stdin);
//  freopen("out.cpp","w",stdout);
	int T=1;
	cin>>T;
//	clock_t start,finish;
//	start=clock();
	while(T--){
		Solve();
	}
//	finish=clock();
//	cerr<<((double)finish-start)/CLOCKS_PER_SEC<<endl;
	return 0;
}

C. Triangle Pendant

有一个均匀的三角形薄片 ABC,三个顶点各有一条细线连到一 个点 D 上。给定三边的长度和三条细线的长度,问提起 D 让三 角形自然落下到稳定状态时,三个顶点距离 D 的垂直距离。

稳定状态下,三角形的重心位于 D 的正下方。 在三条线全拉直的情况下,计算三棱锥把重心旋转到 D 正 下方时 A, B, C 各自的坐标即可。 只能拉直一条线或两条线的答案比较好计算,但是要小心判 断什么时候会发生这两种情况。

不会写,没有代码。

D. Rock Paper Scissors

两个玩家各自有合计 n 张的剪刀、石头、布的牌,每一轮玩家 1 先打出一张剩余的牌,另一名玩家再打出一张剩余的牌,双方都 想最大化自己赢的局数减去输的局数。问最终后手的得分。

先手的决策没有意义,后手可以决定这 2n 张牌的匹配情况。 作为后手,只需要先尽量贪心匹配所有自己赢的 Pattern (如尽量把自己的石头匹配对方的剪刀),再匹配平局的 Pattern,再匹配输的 Pattern 即可。 具体证明可以借助费用流的思想。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
inline void read(ll &x) { //整型快读,注意这里是void有自变量
	x=0;
	char c=getchar();
	for (; !isdigit(c); c=getchar());
	for (; isdigit(c); c=getchar()) x=x*10+c-'0';
}
int main () {
	ll t;
	read(t);
	ll a[4],b[4];
	ll k[4][4];
	k[1][1]=0;
	k[1][2]=-1;
	k[1][3]=1;
	k[2][1]=1;
	k[2][2]=0;
	k[2][3]=-1;
	k[3][1]=-1;
	k[3][2]=1;
	k[3][3]=0;
	while(t--) {
		ll ans=0;
		for(int i=1; i<=3; ++i)
			read(a[i]);
		for(int i=1; i<=3; ++i)
			read(b[i]);
		if(b[1]>=a[3])
			b[1]-=a[3],ans+=a[3],a[3]=0;
		else a[3]-=b[1],ans+=b[1],b[1]=0;
		if(b[2]>=a[1])
			b[2]-=a[1],ans+=a[1],a[1]=0;
		else {
			a[1]-=b[2];
			ans+=b[2];
			b[2]=0;
		}
		if(b[3]>=a[2])
			b[3]-=a[2],ans+=a[2],a[2]=0;
		else {
			a[2]-=b[3];
			ans+=b[3];
			b[3]=0;
		}
		int cnt=0;
		int f=0;
		for(int i=1; i<=3; ++i) {
			if(a[i]==0)
				cnt++;
			else f=i;
		}
		if(cnt==3) {
			printf("%lld\n",ans);
		} else if(cnt==2) {
			for(int i=1; i<=3; ++i) {
				ans+=k[i][f]*b[i];
			}
			printf("%lld\n",ans);
		} else {
			for(int i=1; i<=3; ++i) {
				if(b[i]!=0) {
					f=i;
					break;
				}
			}
			for(int i=1; i<=3; ++i) {
				ans+=k[f][i]*a[i];
			}
			printf("%lld\n",ans);
		}
	}
	return 0;
}

E. Don’t Really Like How The Story Ends

给定一张无向图,问最少添加多少条边,可以使得 1, 2, · · · , n 是 这张图的一个合法 DFS 序列。

考虑什么时候不得不加边:如果在我们 dfs 的过程中走到了 i,并且不可能在下一步走到 i + 1,那么我们必须添加一条 边使得下一步能走到 i + 1。 这等价于在当前的 DFS 栈中,最深的一个有未访问邻居的 点与 i + 1 不相邻。 直接模拟这个过程即可。显然不按照这个过程加边不能使得 答案变得更优。

#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <vector>
using namespace std;
vector<int>f[1000005];
int T,n,m,num,x,y,ans,cnt,visit[1000005];
struct dat
{
	int x,y;
}a[1000005];
bool cmp(dat a,dat b)
{
	if (a.x==b.x) return a.y<b.y;
	else return a.x<b.x;
}
void dfs(int x,int fa)
{
	visit[x]=1;
	if (x==n) return;
	int len=f[x].size();
	for (int i=0;i<len;++i)
	 if (!visit[f[x][i]])
	  if (f[x][i]==cnt+1) { cnt++; dfs(cnt,x);}
	  else { ans++; cnt++; dfs(cnt,x); i--; }
	while (x==1&&cnt!=n) { ans++; cnt++; dfs(cnt,x); }
}
int main()
{
	cin>>T;
	while (T--)
	 {
	 	scanf("%d%d",&n,&m);
	 	for (int i=1;i<=n;++i) 	
		  { 
		   visit[i]=0;
		   f[i].clear();
	}
		num=0;
		for (int i=1;i<=m;++i)
		 {
		 	scanf("%d%d",&x,&y);
		 	if (x==y) continue;
		 	if (x>y) swap(x,y); 
		 	a[++num]={x,y};
		 }
		sort(a+1,a+1+num,cmp);
		for (int i=1;i<=num;++i)
		 if (a[i].x!=a[i-1].x||a[i].y!=a[i-1].y) 
		  {
		  	f[a[i].x].push_back(a[i].y);
		  	f[a[i].y].push_back(a[i].x); 
		  }
		//for (int i=1;i<=n;++i) sort(f[i].begin(),f[i].end());
		ans=0; cnt=1;
		dfs(1,0);
		printf("%d\n",ans);
	 }
	return 0; 
}

F. Direction Setting

一个 n 个点 m 条边的无向图,每个点有一个权值 ai。要求给所 有边定向,令 di 表示定向后 i 号点的入度,要求最小化 D = ∑ i = 1 n m a x ( 0 , d i − a i ) D = \sum^n_{i=1} max(0, d_i − a_i) D=i=1nmax(0,diai)

很容易想到用最大流来解决,源点与每条边连上容量为1的边,那些边向对应端点连上容量为1的边,这样就可以表示每条边选择一个点,最后每个点向汇点连容量为a[i]的边,那么边数减去跑出来的最大流就是最小的D,然后直接根据每条边的方向输出即可。

#include<bits/stdc++.h>
#define int long long
#define inf 0x3f3f3f3f
using namespace std;
const int N=2e5+7;

int read(){	int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-') f=f*-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;}

struct E{
	int u,v,w,nxt;
}e[N];
int head[N],cur[N],cnt=1;
inline void addedge(int f,int v,int w){
	e[++cnt]={f,v,w,head[f]};head[f]=cnt;
}

int n,m,s,t,a[N],mp[1005][1005],u[N],v[N],id[N],deg[N],ru[N];
int lv[N];

inline bool bfs(){ //BFS分层 
	memset(lv,-1,sizeof(lv)); 
	memcpy(cur,head,sizeof(head)); //初始化弧优化 
	queue<int>q;
	q.push(s);lv[s]=0;
	while(!q.empty()){
		int fro=q.front();
		q.pop();
		for(int i=head[fro];i;i=e[i].nxt){
			int to=e[i].v,dis=e[i].w;
			if(dis>0&&lv[to]==-1){
				lv[to]=lv[fro]+1;
				q.push(to);
				if(to==t) return true;
			}
		}
	}
	return false;
}

inline int dfs(int p=s,int flow=inf){
	if(p==t) return flow;
	int lft=flow; //剩余的流量 
	for(int i=cur[p];i&&lft;i=e[i].nxt){ //从当前弧开始出发 
		cur[p]=i; //更新当前弧
		int to=e[i].v,dis=e[i].w;
		if(dis>0&&lv[to]==lv[p]+1){ //向层数高的地方增广 
			int c=dfs(to,min(dis,lft)); //尽可能多地传递流量 
			lft-=c;  //更新剩余流量 
			e[i].w-=c; //更新残差流量 
			e[i^1].w+=c; // 
		}
	} 
	return flow-lft; //返回传递出去的流量大小 
}

int Dinic(){
	int ans=0;
	while(bfs()){
		ans+=dfs();
	}
	return ans;
}


void solve(){
	memset(mp,0,sizeof(mp));
	memset(ru,0,sizeof(ru));
	memset(head,0,sizeof(head));
	memset(cur,0,sizeof(cur));
	memset(id,0,sizeof(id));
	memset(deg,0,sizeof(deg));
	cnt=1;
	n=read();m=read();s=0;t=n+m+1;
	for(int i=1;i<=n;++i){  //读入 
		a[i]=read();
	}
	for(int i=1;i<=m;i++){
		u[i]=read();v[i]=read();
		addedge(s,n+i,1);
		addedge(n+i,s,0);
		addedge(n+i,u[i],1);
		addedge(u[i],n+i,0);
		addedge(n+i,v[i],1);
		id[i]=cnt; //记录id正向时的 
		addedge(v[i],n+i,0);
	}
	for(int i=1;i<=n;++i){
		addedge(i,t,a[i]);
		addedge(t,i,0);
	}
	printf("%lld\n",m-Dinic());  //总和-最大流 
	for(int i=1;i<=m;++i){
		int p=id[i];
		if(e[p].w<=0) putchar('0'); //说明这条边翻转了,直接用原边 
		else putchar('1');
	}
	putchar('\n');
}

signed main(){
	int T;
	T=read();
	while(T--){
		solve();
	}
	return 0;
}

G. Hourly Coding Problem

把一个长度为 n 的整数数组分成 k 个非空连续段。令各段内部 的和是 s1,s2, · · · ,sk,最小化 max(si)。要求输出最优方案中各段 的长度,如果有多组解,输出长度序列字典序最大者。

二分答案,二分值确定后合法的 k 是个区间,树状数组优化 dp 维护 [i,n] 最多最少能分成几块。 输出答案时前缀和值域线段树维护目前能够分成的块数合法 的下标(mini <= k <= maxi),每次是前缀查询最大值 + 新增/删除合法下标 (k 减小 1 后维护合法下标) 时间复杂度 O(n log n log( ∑ai))。

(不会写,没有代码)

H. Nihongo wa Muzukashii Desu

给定日语的一种动词变体规则,输出读入单词的变体

按照题意模拟即可。

//#pragma GCC optimize("Ofast", "inline", "-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define int long long
using namespace std;
const int N=2e5+7;
const int mod=1e9+7;

//int read(){	int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-') f=f*-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;}
//void write(int x){if(x>9) write(x/10);putchar(x%10+'0');}
void Solve(){
	string str;
	cin>>str;
	int len=str.length();
	int st1=str.find("chimasu");
	int st2=str.find("rimasu");
	int st4=str.find("mimasu");
	int st5=str.find("bimasu");
	int st6=str.find("nimasu");
	int st7=str.find("ikimasu");
	int st8=str.find("kimasu");
	int st9=str.find("gimasu");
	int st10=str.find("shimasu");
	int st3=str.find("imasu");
//	cout<<len<<" "<<st1<<" "<<st2<<" "<<st3<<"!!\n";
	if(st1>=0&&st1<=len){
		for(int i=0;i<st1;++i){
			cout<<str[i];
		}
		cout<<"tte\n";
	}else if(st2>=0&&st2<=len){
		for(int i=0;i<st2;++i){
			cout<<str[i];
		}
		cout<<"tte\n";
	}else if(st4>=0&&st4<=len){
		for(int i=0;i<st4;++i){
			cout<<str[i];
		}
		cout<<"nde\n";
	}else if(st5>=0&&st5<=len){
		for(int i=0;i<st5;++i){
			cout<<str[i];
		}
		cout<<"nde\n";
	}else if(st6>=0&&st6<=len){
		for(int i=0;i<st6;++i){
			cout<<str[i];
		}
		cout<<"nde\n";
	}else if(str=="ikimasu"){
		cout<<"itte\n";
	}else if(st8>=0&&st8<=len){
		for(int i=0;i<st8;++i){
			cout<<str[i];
		}
		cout<<"ite\n";
	}else if(st9>=0&&st9<=len){
		for(int i=0;i<st9;++i){
			cout<<str[i];
		}
		cout<<"ide\n";
	}else if(st10>=0&&st10<=len){
		for(int i=0;i<st10;++i){
			cout<<str[i];
		}
		cout<<"shite\n";
	}else if(st3>=0&&st3<=len){
		for(int i=0;i<st3;++i){
			cout<<str[i];
		}
		cout<<"tte\n";
	}
}

signed main(){
//	ios::sync_with_stdio(0);
//	cin.tie(0);cout.tie(0);
//  freopen("in.cpp","r",stdin);
//  freopen("out.cpp","w",stdout);
	int T=1;
	cin>>T;
//	clock_t start,finish;
//	start=clock();
	while(T--){
		Solve();
	}
//	finish=clock();
//	cerr<<((double)finish-start)/CLOCKS_PER_SEC<<endl;
	return 0;
}

I. Monster Hunter

玩家的攻击力序列是 a1, a2, · · · , an 无限循环,有 m 只怪物的血 量是 h1, h2, · · · , hm。问击败所有怪物最少需要几次攻击。 1 ≤ n, m ≤ 100000, 1 ≤ ai ≤ 3。

二分答案,转变为有若干个大小为 1, 2, 3 之一的物体,问能 不能填满大小为 h1, h2, · · · , hm 的空间(可以溢出)。 如果没有 3 是一个简单的贪心,只要在不溢出的情况下尽量 放 2,如果还有剩余的 2 再往剩余空间为 1 的格子里放,最 后再放 1 即可。 有 3 的情况思想类似,就是尽量避免当前的浪费和之后过程 的浪费。 具体过程如下:先往剩余空间为大于等于 3 的奇数的位置放 一个 3;再往所有大于等于 6 的格子尽量多地放两个一组的 3。(尽量保持偶数可以减小放 2 和 1 时的浪费)。此时如果 恰好只有一个 3 剩余,就把它放到剩余的最大的格子里。之 后依次往剩余空间为 4, 2, 1 的格子放 3 即可。之后就转化为 了只有 1, 2 的贪心。

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+7;
struct dat {
	int one,two,thr;
} cnt[N];
int T,n,m,sum,a[N],b[N],c[N],s;
bool cmp(int a,int b) {
	return a>b;
}

bool check(int x) {
	for(int i=1;i<=m;++i) b[i]=c[i];		
	int k=x/n;
	int cnt1=cnt[n].one*k;
	int cnt2=cnt[n].two*k;
	int cnt3=cnt[n].thr*k;
	cnt1+=cnt[x%n].one;
	cnt2+=cnt[x%n].two;
	cnt3+=cnt[x%n].thr;
	if(cnt1+cnt2*2+cnt3*3<s) return false;
	for(int i=1;cnt3&&i<=m;++i){
		if(b[i]>=3){
			if (b[i]%2){
				cnt3--;
				b[i]-=3;
			}
		}
	}
	for(int i=m;cnt3&&i>=1;--i){
		if(b[i]>=6){
			int t=b[i]/6;
			if(t*2<=cnt3){
				b[i]-=6*t;
				cnt3-=2*t;
			}else{
				b[i]-=3*(cnt3-(cnt3&1));
				cnt3&=1;
			}
		}
	}
	sort(b+1,b+1+m,cmp);
	for(int i=1;cnt3&&i<=m;++i){
		if(b[i]!=0){
			cnt3--;
			b[i]=max(0ll,b[i]-3ll);
		}
	}
	for(int i=1;cnt3&&i<=m;++i){
		if(b[i]==2){
			b[i]=0;
			--cnt3;
		}
	}
	for(int i=1;cnt3&&i<=m;++i){
		if(b[i]!=0){
			b[i]=0;
			--cnt3;
		}
	}
	for(int i=1;cnt2&&i<=m;++i){
		if(b[i]>=2){
			if(cnt2*2>=b[i]){
				cnt2-=b[i]/2;
				b[i]%=2;
			}else{
				b[i]-=cnt2*2;
				cnt2=0;
				break;
			}
		}
	}
	for(int i=1;cnt2&&i<=m;++i){
		if(b[i]){
			cnt2--;
			b[i]=max(0ll,b[i]-2);
		}
	}
	int hpsum=0;
	for(int i=1;i<=m;++i)
		if (b[i]>0) hpsum+=b[i];
	if(cnt1>=hpsum) return true;
	else return false;

}
signed main(){
	scanf("%d",&T);
	while (T--) {
		scanf("%d",&n);
		sum=0;
		for (int i=0; i<=n; ++i) cnt[i].one=cnt[i].two=cnt[i].thr=0; //初始化 
		for (int i=1; i<=n; ++i) {
			scanf("%lld",&a[i]); 
			sum+=a[i];
			cnt[i]=cnt[i-1];
			if (a[i]==1) cnt[i].one++;
			else if (a[i]==2) cnt[i].two++;
			else cnt[i].thr++;
		}
		scanf("%d",&m);
		s=0;
		for (int i=1; i<=m; ++i) {
			scanf("%lld",&b[i]);
			s=s+b[i];
			c[i]=b[i];
		}
		sort(c+1,c+1+m,cmp);
		int l=0,r=s+1,mid=0;
		while (r-l>1) {  //二分 
			mid=((l+r)>>1);
			if (check(mid)) r=mid;
			else l=mid;
		}
		printf("%lld\n",r);
	}
	return 0;
}

J. Ants

长度 L = (1e9 + 1) 的数轴上 N 只蚂蚁,蚂蚁互相碰到之后各自转身,数轴两端各有一块挡板,一只蚂蚁撞到之后直接转身,挡板被撞 K 次就消失,问所有蚂蚁走出去的时间。

在挡板消失之前,每隔 2L 的的时间所有蚂蚁会回到初始状 态,每块挡板被碰撞 n 次。因此可以直接模拟最后一轮的状 态,这一轮中至多只碰撞 n 次。 蚂蚁的相对顺序不变。同时在算下一次碰撞时间时可以认为 未发生碰撞,直接穿过。 两块挡板都维护一个队列,维护撞到这块挡板的蚂蚁的时间 序列,每次在两个队头中选择一个最小值,模拟之后塞进另 一个队列即可。

#include<bits/stdc++.h>
#define int __int128
using namespace std;
typedef long long ll;
int read(){	int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-') f=f*-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;}
void write(int x){if(x>9) write(x/10);putchar(x%10+'0');}

int a[1000005];
int d[10000005];
int tr[1000005];
int tl[1000005];

const int mod=1e9+1;
signed main (){
    int n,aa,bb;
    n=read();aa=read();bb=read();
    for(int i=1;i<=n;++i)
    {
        a[i]=read();
    }
    for(int i=1;i<=n;++i)
        d[i]=read();
    int cnt=0,cnt1=0;
    tl[0]=0;
    tr[0]=0;
    for(int i=1;i<=n;++i)
    {    
        if(d[i]==0)
         {
            tl[++cnt1]=a[i];
        }
    }
    for(int i=n;i>=1;--i)
    {
        if(d[i]==1)
        {
            tl[++cnt1]=(2ll*mod-a[i]);
            tr[++cnt]=(mod-a[i]);
        }
    }
    for(int i=1;i<=n;++i)
    {
        if(d[i]==0)
        {
            tr[++cnt]=a[i]+mod;
        }
    }
    int nn=n;
    int rallt,lallt;
    if(bb%nn!=0)
     rallt=(bb/(nn))*2ll*mod+tr[(bb-(bb/(nn))*nn)];
    else  rallt=((bb/(nn))-1)*2ll*mod+tr[nn];
        if(aa%nn!=0)
     lallt=(aa/(nn))*2ll*mod+tl[(aa-(aa/(nn))*nn)];
    else  lallt=((aa/(nn))-1)*2ll*mod+tl[nn];
    int ans=0;
    if(rallt>=lallt)
    {
        if(rallt>=lallt+mod)
        {
            ans=lallt+2*mod;
        }
        else {
            ans=rallt+mod;
        }
    }
    else {
        if(lallt>=rallt+mod)
            ans=rallt+2*mod;
        else ans=lallt+mod;
    }
    write(ans);putchar('\n');
    return 0;
}

K. K-skip Permutation

给定 n, k,构造一个 1 − n 的排列 p1, p2, · · · , pn,使得满足 pi + k = pi+1 的下标 i 最多。

将 MOD k 相同的值放在一起,从小到大依次输出。显然可 以达到理论最大值。

#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
int n,k,visit[1000005],f[1000006],num;
int main()
{
	cin>>n>>k;
	for (int i=1;i<=n;++i)
	 if (!visit[i]) 
	  {
	  	for (int j=i;j<=n;j+=k)
	  	 {
	  	 	f[++num]=j;
	  	 	visit[j]=1;
	  	 }
	  }
	printf("%d",f[1]);
	for (int i=2;i<=num;++i)
	 printf(" %d",f[i]);
	return 0;
}

L. Spicy Restaurant

无向图的每个顶点有一个属性 wi,Q 个询问,第 i 个询问给定顶 点 p 和阈值 a,问距离 p 最近的 wi ≤ a 的 i 距离 p 有多远。 1 ≤ wi ≤ 100。

w 的范围很小,直接做 100 次 BFS,计算出 d[i][j] 表示离 i 最近的属性值恰好为 j 的点的距离即可。时间复杂度 O(100(n + m) + Q)。

//#pragma GCC optimize("Ofast", "inline", "-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
//#define int long long
#define mk make_pair
#define pii pair<int,int>
using namespace std;
const int N=2e5+7;
const int mod=1e9+7;

//int read(){	int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-') f=f*-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;}
//void write(int x){if(x>9) write(x/10);putchar(x%10+'0');}
int n,m,q,w[N],dp[N][105],vis[N];
vector<int>G[N];
queue<pii>que[105];

void Solve(){
	cin>>n>>m>>q;
	memset(dp,inf,sizeof(dp));
	for(int i=1;i<=n;++i){
		cin>>w[i];
		dp[i][w[i]]=0;
		que[w[i]].push(mk(0,i));
	}
	for(int i=1,u,v;i<=m;++i){
		cin>>u>>v;
		G[u].emplace_back(v);
		G[v].emplace_back(u);
	}
	for(int i=0;i<=100;++i){
		memset(vis,0,sizeof(vis));
		while(que[i].size()){
			int fro=que[i].front().second;
			int w=que[i].front().first;
			que[i].pop();
			vis[fro]=0;
			for(auto to:G[fro]){
				if(dp[to][i]>w+1){
					dp[to][i]=w+1;
					if(!vis[to]){
						vis[to]=1;
						que[i].push(mk(dp[to][i],to));
					}
				}
			}
		}
	}
	for(int i=1;i<=n;++i){
		for(int j=0;j<=99;++j){
			dp[i][j+1]=min(dp[i][j+1],dp[i][j]);
		}
	}
	for(int i=1,p,a;i<=q;++i){
		cin>>p>>a;
		if(dp[p][a]>=inf) cout<<-1<<"\n";
		else cout<<dp[p][a]<<"\n";
	}
}

signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
//  freopen("in.cpp","r",stdin);
//  freopen("out.cpp","w",stdout);
	int T=1;
//	cin>>T;
//	clock_t start,finish;
//	start=clock();
	while(T--){
		Solve();
	}
//	finish=clock();
//	cerr<<((double)finish-start)/CLOCKS_PER_SEC<<endl;
	return 0;
}

M. True Story

有 n 个旅客在位置 0 同时出发赶飞机,飞机的位置是 x。第 i 个 人以 si 的速度匀速运动。初始时,飞机预定在时刻 p0 起飞。有 m 个广播,第 i 个广播在 ti 时刻告诉所有旅客飞机延误到了 pi, 保证 ti 和 pi 是递增的。每个旅客在每个时刻都会根据当前自己 的位置、当前自己的速度和当前预定的起飞时间来决定行动:如 果赶得上飞机就继续移动,否则就原地停留。问最终有多少个旅 客赶得上飞机。

注意到 pi 是递增的,因此一个旅客开始移动后就一定会到 达终点。 判断一个旅客 j 是否会开始移动,相当于判断是否存在一个 i,使得 sj ∗ (pi − ti) ≥ x。因此可以由最大的 pi − ti 判断。 找到最大的 pi − ti(令 t0 = 0),依次判断每一个旅客能否在 maxi (pi − ti) 的时间里走完 x 的距离即可。 时间复杂度 O(n + m)。

//#pragma GCC optimize("Ofast", "inline", "-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define int long long
using namespace std;
const int N=2e5+7;
const int mod=1e9+7;

//int read(){	int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-') f=f*-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;}
//void write(int x){if(x>9) write(x/10);putchar(x%10+'0');}
int n,k,x,s[N],t[N],p[N],mx,ans=0;
void Solve(){
	cin>>n>>k>>x>>mx;
	for(int i=1;i<=n;++i) cin>>s[i];
	for(int i=1;i<=k;++i) cin>>t[i];
	for(int i=1;i<=k;++i){
		cin>>p[i];
		p[i]-=t[i];
		mx=max(mx,p[i]);
	}
	for(int i=1;i<=n;++i) if(s[i]*mx>=x) ++ans;
	cout<<ans<<"\n"; 
}

signed main(){
//	ios::sync_with_stdio(0);
//	cin.tie(0);cout.tie(0);
//  freopen("in.cpp","r",stdin);
//  freopen("out.cpp","w",stdout);
	int T=1;
//	cin>>T;
//	clock_t start,finish;
//	start=clock();
	while(T--){
		Solve();
	}
//	finish=clock();
//	cerr<<((double)finish-start)/CLOCKS_PER_SEC<<endl;
	return 0;
}
  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
程序设计竞赛相关代码、设计文档、使用说明,供学习参考 程序设计竞赛相关代码、设计文档、使用说明,供学习参考 程序设计竞赛相关代码、设计文档、使用说明,供学习参考 程序设计竞赛相关代码、设计文档、使用说明,供学习参考 程序设计竞赛相关代码、设计文档、使用说明,供学习参考 程序设计竞赛相关代码、设计文档、使用说明,供学习参考 程序设计竞赛相关代码、设计文档、使用说明,供学习参考 程序设计竞赛相关代码、设计文档、使用说明,供学习参考 程序设计竞赛相关代码、设计文档、使用说明,供学习参考 程序设计竞赛相关代码、设计文档、使用说明,供学习参考 程序设计竞赛相关代码、设计文档、使用说明,供学习参考 程序设计竞赛相关代码、设计文档、使用说明,供学习参考 程序设计竞赛相关代码、设计文档、使用说明,供学习参考 程序设计竞赛相关代码、设计文档、使用说明,供学习参考 程序设计竞赛相关代码、设计文档、使用说明,供学习参考 程序设计竞赛相关代码、设计文档、使用说明,供学习参考 程序设计竞赛相关代码、设计文档、使用说明,供学习参考 程序设计竞赛相关代码、设计文档、使用说明,供学习参考 程序设计竞赛相关代码、设计文档、使用说明,供学习参考 程序设计竞赛相关代码、设计文档、使用说明,供学习参考 程序设计竞赛相关代码、设计文档、使用说明,供学习参考 程序设计竞赛相关代码、设计文档、使用说明,供学习参考 程序设计竞赛相关代码、设计文档、使用说明,供学习参考 程序设计竞赛相关代码、设计文档、使用说明,供学习参考

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

RWLinno

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值