2025.05.30__jyu__GDCPC训练赛2(A,F,H,I)

A题题目链接

image

题目大意

给定一个数组,让我们求有多少个区间满足gcd(\(a_{l}\) , \(a_{l+1}\) , …… ,\(a_{r}\))=min(\(a_{l}\) , \(a_{l+1}\) , …… ,\(a_{r}\));

思路

我们可以先让每一个数作为这个gcd,然后往左右去延伸,保持gcd不变,找到最后满足的l,r;然后就可以根据l,r算出区间数量。
需要注意的是代码实现的时候有很多细节,要保证不会算重或算漏。

点击查看代码
#include <bits/stdc++.h>
#define PII pair<int,int>
#define int long long
#define pb push_back
#define fi first
#define se second
#define endl '\n'
using namespace std;
const int N=1e6+10,M=1010,mod=1e9+7;
const int INF=0x3f3f3f3f;
const int inf=0x3f3f3f3f3f3f3f3f;
int dx[]={0,0,1,-1},dy[]={1,-1,0,0};
int n,m,k;
int sum;
struct node{//因为可能有多个连续相同的值,可以合并去做,节约时间。 
	int a;
	int num;
}nod[N];
 
void solve(){
    cin>>n;
    int idx=1,x;
    for(int i=1;i<=n;i++){
    	cin>>x;
		if(i!=1&&nod[idx-1].a==x) nod[idx-1].num++;
		else nod[idx++]={x,1};
	}
	int ans=0;
	for(int i=1;i<idx;i++){
		int gcd=nod[i].a;
		int l=i,r=i,ll,rr;
		int cntl=0,cntr=0;
		
		/*
		要注意往前找的时候不能覆盖和gcd相同值的数,因为包含那个值的区间
		已经算过了,在找的话就会算重复了。 
		*/
		while(l-1>0&&nod[l-1].a>gcd&&__gcd(gcd,nod[l-1].a)==gcd)
			cntl+=nod[l-1].num,l--;
		while(r+1<idx&&__gcd(gcd,nod[r+1].a)==gcd)
			cntr+=nod[r+1].num,r++;
			
		for(int j=1;j<=nod[i].num;j++){ //相同的值每一个数都要计算区间。 
			if(j==1) ll=cntl;          //只有第一个数能算包含前面数的区间 
			else ll=0;                //后面的不能算前面的,会重复。 
			rr=cntr+nod[i].num-j;
			
			ans+=(ll+1)*(rr+1); //每个数选与不选,去算区间 
		}
	}
	cout<<ans<<endl;
}

signed main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	
	int _=1;
//	cin>>_;
	while(_--) solve();
	
	return 0;
}

赛后总结

其实这题思路很好想,是比较常见的做法,但我们队做的时候卡了好久,因为代码没有处理好重复的情况,答案一直有错误。

F题题目链接

image

题目大意

给定一个初始时间x0:y0,分别拨动时针和分针,时针、分针不会连锁移动。问将x0:y0变为x1:y1到x2:y2之间的哪一个时间的代价最小(代价为拨动的角度),如果有多个代价最小的,那么答案为最早的时间。

思路

直接枚举每一个合法的时间,算出代价就行。
注意题目要求的是合法的时间,要求时针和分针的指向符合现实规律(就是时针并不是总是指向整点,可能有偏移的角度),所以算时针代价的时候,要考虑到偏移的角度。解决方法就是把小时转化成角度去算,一圈\(360^\circ\),那么一小时就是\(30^\circ\)

点击查看代码
#include <bits/stdc++.h>
#define int long long 
#define PII pair<int,int>
#define pb push_back
#define fi first
#define se second
#define endl '\n'
using namespace std;
const int N=1e6+10,M=1010,mod=1e9+7;
const int INF=0x3f3f3f3f;
const int inf=0x3f3f3f3f3f3f3f3f;
int n,m,k;
int a[N]; 

void solve(){
    int x0,y0,x1,y1,x2,y2;
    cin>>x0>>y0>>x1>>y1>>x2>>y2;
    double du_x0=x0*30+(1.0*y0/60)*30;
    int hh,mm;
	double minv=INF;
    for(int i=x1*60+y1;i<=x2*60+y2;i++){
    	int h=i/60,m=i%60;
    	double res=0;
    	double du_h=h*30+(1.0*m/60)*30;
    	res+=min(abs(m-y0),60-abs(m-y0))*6;
    	res+=min(fabs(du_x0-du_h),360-fabs(du_x0-du_h));
    	if(res<minv){
    		hh=h,mm=m;
    		minv=res;
		}
	}
	cout<<hh<<" "<<mm<<endl;
}

signed main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	int _=1;
    cin>>_;
	while(_--) solve();
	return 0; 
} 

赛后总结

比赛快结束的时候才开始做这题,前面卡其它的题卡太久了,浪费了太多的时间。这题其实很简单,比赛时已经想到做法了,但没时间调了,所以没过。

H题题目链接

image

题目大意

有n组人,每组有 \(b_i\) 人,要将这些人分到n个城市去(可以拆分开来去分,不用整组一起)。每一组人都有一个不想去的城市 \(a_i\) 。一座城市有 \(c_i\) 的不满意指数,如果最后迁入了 \(d_i\) 人,那么 i 号城市的不满意度就是 f(i) = \(c_i\) × \(d_i\)
要求算出\(max_{i=1}^{n}\) f(i)的最小值。

思路

我们可以用二分来做这题。
check函数的写法也好想,假定一个最小值x,那么所有的城市的不满意度都不能超过x,我们记录不满意度不超过x的情况下最多可以容纳多少人,在与总人数sum比较就可以了。
要注意一座城市最多可以容纳的人数是有限制的,它永远也不能容纳那组住不想住进这座城市的人。

点击查看代码
#include <bits/stdc++.h>
#define PII pair<int,int>
#define int long long
#define pb push_back
#define fi first
#define se second
#define endl '\n'
using namespace std;
const int N=1e6+10,M=1010,mod=1e9+7;
const int INF=0x3f3f3f3f;
const int inf=0x3f3f3f3f3f3f3f3f;
int dx[]={0,0,1,-1},dy[]={1,-1,0,0};
int n,m,k;
int sum;
bool st[N];
int a[N],b[N],c[N],p[N];

bool check(int x){
	int res=0;
	for(int i=1;i<=n;i++){
		int d=x/c[i];
		if(d>p[i]) d=p[i];
		res+=d;
	}
	if(res<sum) return false;
	else return true;
}

void solve(){
	sum=0;
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=1;i<=n;i++) cin>>b[i],sum+=b[i];
	for(int i=1;i<=n;i++) cin>>c[i];
	for(int i=1;i<=n;i++){
		p[a[i]]=sum-b[i];
	}
	int l=0,r=1e18;
	while(l<r){
		int mid=l+r>>1;
		if(check(mid)) r=mid;
		else l=mid+1;
	}
	cout<<l<<endl;
}

signed main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	
	int _=1;
	cin>>_;
	while(_--) solve();
	
	return 0;
}

做后总结

赛后做的这题,其实也比较容易(也可能是因为我被lsy剧透了这题是二分做法)。

I题题目链接

image

题目大意

有两排城市,不同排城市之间两两都有路,但有的城市u与城市v因玄学问题不能在一条路径上都有出现,问能否从s到达t。

思路

首先如果s和t会因为玄学问题不能出现在同一条路径上,那么必然不能到达。
如果s=t,那么必然能到达。
然后其余的情况因为在点多的情况下,会存在很多的路径可能,所以都能彼此到达。
但要注意的是n=2的时候要特殊处理,因为此时点少,有的城市之间不能到达。

点击查看代码
#include <bits/stdc++.h>
#define int long long 
#define PII pair<int,int>
#define pb push_back
#define fi first
#define se second
#define endl '\n'
using namespace std;
const int N=1e6+10,M=1010,mod=1e9+7;
const int INF=0x3f3f3f3f;
const int inf=0x3f3f3f3f3f3f3f3f;
int n,m,k;
int a[N]; 

void solve(){
    int s,t;
    cin>>n>>s>>t;
    map<PII,int> mp;
    for(int i=1;i<=n;i++){
    	cin>>a[i];
    	mp[{a[i],i}]=mp[{i,a[i]}]=1;
	}
	if(s==t){
		cout<<"Yes"<<endl;
		return;
	}
	if(mp[{s,t}]){
		cout<<"No"<<endl;
		return;
	}
    if(n>2) cout<<"Yes"<<endl;
    else if(n==2){
    	if((s<=n&&t<=n)||(s>n&&t>n)) cout<<"No"<<endl;
    	else cout<<"Yes"<<endl;
	}
}

signed main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	int _=1;
    cin>>_;
	while(_--) solve();
	return 0; 
} 

赛后总结

比赛的时候理解错了题意,以为是u到v的直接路径不能走,所以一直调不出来。

posted @ 2025-05-31 17:18  _hu  阅读(20)  评论(0)    收藏  举报