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,di−ai)
很容易想到用最大流来解决,源点与每条边连上容量为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 是递增的。每个旅客在每个时刻都会根据当前自己 的位置、当前自己的速度和当前预定的起飞时间来决定行动:如 果赶得上飞机就继续移动,否则就原地停留。问最终有多少个旅 客赶得上飞机。文章来源:https://www.toymoban.com/news/detail-451257.html
注意到 pi 是递增的,因此一个旅客开始移动后就一定会到 达终点。 判断一个旅客 j 是否会开始移动,相当于判断是否存在一个 i,使得 sj ∗ (pi − ti) ≥ x。因此可以由最大的 pi − ti 判断。 找到最大的 pi − ti(令 t0 = 0),依次判断每一个旅客能否在 maxi (pi − ti) 的时间里走完 x 的距离即可。 时间复杂度 O(n + m)。文章来源地址https://www.toymoban.com/news/detail-451257.html
//#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;
}
到了这里,关于【超好懂的比赛题解】2021 年四川省大学生程序设计竞赛的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!