ICPC 2019-2020 North-Western Russia Regional Contest补题

A-Accurate Movement

简单模拟题
但是还是因为少考虑了一点,wa了一发
给了两秒,暴力足够了

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
const int INF=0x3f3f3f3f;
typedef long long ll;
int main(){
	ll a,b,n;
	scanf("%lld%lld%lld",&a,&b,&n);
	if(b==n){//b无需再移动,只需要把a一次移过去就好
		printf("1\n");
		return 0;
	}
	ll c=b-a;
	ll ans=0;
	int f1=0;
	int f2=0;
	while(1){
		if(f1==0){
			a+=c;//移动a
			ans++;
		}
		if(a>=n) f1=1;
		if(f2==0){
			b+=c;//移动b
			ans++;
		}
		if(b>=n) f2=1;
		if(f1&&f2) break;
	} 
	printf("%lld\n",ans);
    return 0;
}

M-Managing Difficulties

给出一个长度为 n n n的数组, a 1 , a 2 , ⋯   , a n a_1,a_2,\cdots ,a_n a1,a2,,an ( 1 ≤ a i ≤ 1 0 9 ) (1\leq a_i\leq 10^9) (1ai109)
求满足以下条件的 i , j , k i,j,k i,j,k有多少对。
对于 1 ≤ i < j < k ≤ n 1\leq i<j<k\leq n 1i<j<kn a k − a j = a j − a i a_k-a_j=a_j-a_i akaj=ajai

思路:
三个变元,第一想法减少变元
时间给了两秒,可以先确定其中两个变元的取值,计算出第三个,然后去找该值有多少个即可。
枚举 a i , a j a_i,a_j ai,aj的值,我们需要知道下标在 j j j之后的元素中有多少个等于 2 a j − a i 2a_j-a_i 2ajai
正序遍历不太好处理,那就倒序遍历,每枚举一个 a j a_j aj,用map统计其个数即可(这时候map统计的都是出现在 j j j后面的值)。

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
const int INF=0x3f3f3f3f;
typedef long long ll;
typedef pair<int,int> P;
const int mod=1e9+7;
unordered_map<int,int>mp;
int a[maxn];
int main(){
	int t;
	scanf("%d",&t);
	while(t--){
		mp.clear();
		int n;
		scanf("%d",&n);
		for(int i=1;i<=n;i++){
			scanf("%d",&a[i]);
		}
		int ans=0;
		for(int j=n;j>=1;j--){
			for(int i=j-1;i>=1;i--){
				int ak=2*a[j]-a[i];
				ans+=mp[ak];
			}
			mp[a[j]]++;
		}
		printf("%d\n",ans);		
	}
    return 0;
}

I. Ideal Pyramid

金字塔底部有四条边,两条边从北指向南(平行于y轴),两条边从东指向西(平行于x轴)。
完美的平衡金字塔斜面和底面的夹角成45°。
现有 n n n个obelisk,给出其位置坐标 ( x i , y i ) (x_i,y_i) (xi,yi)和高度 h i h_i hi。试求能将这些obelisk全部包含进去的最小的金字塔,并给出其中心坐标。

思路:
“夹角为45°”—>四棱锥的高=其底面正方形边长的一半
可将高转化成底面上的边长

在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
const int INF=0x3f3f3f3f;
typedef long long ll;
typedef pair<int,int> P;
const int mod=1e9+7;
unordered_map<int,int>mp;
int a[maxn];
int main(){
	int n;
	cin>>n;
	int lx=INF,rx=-INF;
	int ly=INF,ry=-INF;
	for(int i=1;i<=n;i++){
		int x,y,h;
		cin>>x>>y>>h;
		lx=min(lx,x-h);
		rx=max(rx,x+h);
		ly=min(ly,y-h);
		ry=max(ry,y+h);
	}
	int ans_x=(lx+rx)/2;
	int ans_y=(ly+ry)/2;
	int ans_h=(max(rx-lx,ry-ly)+1)/2;
	cout<<ans_x<<" "<<ans_y<<" "<<ans_h<<endl;
    return 0;
}

E. Equidistant

给定一棵树和树上一些点,求任意一个树上到所有给定点距离相等的点。

多源BFS
bfs 按层维护
记录每个点在同一层被多少个点覆盖

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+10;
const int INF=0x3f3f3f3f;
typedef long long ll;
typedef pair<int,int> P;
typedef pair<P,int> PP;
const int mod=1e9+7;
int n,m;
int h[maxn],nxt[maxn<<1],e[maxn<<1],cnt;
int vis[maxn],lev[maxn],num[maxn];
void add(int a,int b){
	e[cnt]=b;
	nxt[cnt]=h[a];
	h[a]=cnt++;
}
void bfs(){
	queue<int>q;
	for(int i=1;i<=n;i++){
		if(vis[i]) q.push(i);
	}
	while(!q.empty()){
		int x=q.front();
		q.pop();
		for(int i=h[x];i!=-1;i=nxt[i]){
			int y=e[i];
			if(lev[y]==0||lev[y]==lev[x]+1){
				lev[y]=lev[x]+1;
				num[y]+=num[x];
				if(!vis[y]){
					vis[y]=1;
					q.push(y);
				}
			}
		}
	}
	for(int i=1;i<=n;i++){
		if(num[i]==m){
			puts("YES");
			printf("%d\n",i);
			return;
		}
	}
	puts("NO");
	return;
}
int main(){
	scanf("%d%d",&n,&m);
	memset(h,-1,sizeof(h));
	for(int i=1;i<n;i++){
		int u,v;
		scanf("%d%d",&u,&v);
		add(u,v);
		add(v,u);
	}
	for(int i=0;i<m;i++){
		int x;
		scanf("%d",&x);
		vis[x]=1;
		lev[x]=1;
		num[x]=1;
	}
	bfs();
    return 0;
}

J-Just the Last Digit

现有一张有向无环图,给出每个点到另一个点的方案数的个位数(且只能从下标小的点到下标大的点),需将该图还原(若两个点能直达输出1,否则输出0)

思路:
两个点 i , j ( i < j ) i,j(i<j) i,j(i<j)的关系只有两种:
1、可以直达
2、需要通过其他点才能到达
对于第二种情况,其实无需知道需要经过多少个点才能到达
i i i-> j j j的唯一路径为: i i i-> k k k-> a a a-> ⋯ \cdots -> b b b-> j j j
只需要找到 i i i能够直达的点 k k k,看从点 k k k到点 j j j的方案数有多少种,若其刚好等于从点 i i i到点 j j j的方案数,那么点 i i i肯定不能直达点 j j j,否则说明 i i i j j j之间存在一条直达路径。

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e3+10;
const int INF=0x3f3f3f3f;
typedef long long ll;
const int mod=1e9+7;
int a[550][550];
int mp[550][500];
int main(){
	int n;
	cin>>n;
	for(int i=1;i<=n;i++){
		string s;
		cin>>s;
		s=" "+s;
		for(int j=1;j<=n;j++){
			a[i][j]=s[j]-'0';
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			int sum=0;
			for(int k=i+1;k<j;k++){//先找出i能够直达的点
				if(mp[i][k]) sum+=a[k][j];
			}
			if(sum%10!=a[i][j]) mp[i][j]=1;
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			cout<<mp[i][j];
		}
		cout<<endl;
	}
    return 0;
}

H-High Load Database

给出长度为 n n n的数组 a 1 , a 2 , ⋯   , a n a_1,a_2,\cdots,a_n a1,a2,,an
给出 q q q个查询,每次查询给出一个 t t t值,需将数组分段为每一段的和都不超过 t t t,求最少能分成多少段?

思路:
前缀和+二分
先处理出数组的前缀和,再用二分确定分段区间

如果数组的最大值大于 t t t,则不可能将其完全分段

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+10;
const int INF=0x3f3f3f3f;
typedef long long ll;
const int mod=1e9+7;
int a[maxn],sum[maxn];
int ans[maxn];
int main(){
	int n;
	cin>>n;
	int mmax=0;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		mmax=max(mmax,a[i]);
		sum[i]=sum[i-1]+a[i];
	}	
	int q;
	cin>>q;
	for(int i=0;i<q;i++){
		int t;
		cin>>t;
		if(t<mmax) cout<<"Impossible\n";
		else if(ans[t]) cout<<ans[t]<<endl;
		else{
			int l=0;
			int cnt=0;
			while(l<n){
				int num=sum[l]+t;
				l=lower_bound(sum+1+l,sum+n+1,num)-sum;//二分寻找下一个区间
				if(sum[l]>num) l--;
				cnt++;
			}
			ans[t]=cnt;
			cout<<ans[t]<<endl;
		}
	}
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值