2025.05.30__jyu__GDCPC训练赛2(A,F,H,I)
A题题目链接

题目大意
给定一个数组,让我们求有多少个区间满足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题题目链接

题目大意
给定一个初始时间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题题目链接

题目大意
有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题题目链接

题目大意
有两排城市,不同排城市之间两两都有路,但有的城市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的直接路径不能走,所以一直调不出来。

浙公网安备 33010602011771号