Codeforces Round #552 Div. 3
题目链接:戳我
前两题是littlesun_wl小可爱写的qwqwq
A
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<vector>
#define MAXN 100010
#define INF 0x3f3f3f3f
using namespace std;
int a[MAXN];
int main()
{
for(int i=1;i<=4;i++)
{
scanf("%d",&a[i]);
}
sort(a,a+5);
int x=(a[1]+a[2]-a[3])/2;
int y=(a[2]+a[3]-a[1])/2;
int z=(a[1]+a[3]-a[2])/2;
printf("%d %d %d",x,y,z);
return 0;
}
B
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<vector>
#define MAXN 1000
#define INF 0x3f3f3f3f
using namespace std;
int G[MAXN];
int sign[MAXN];
int cnt;
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&G[i]);
}
int mx=0;int mn=INF;
for(int i=1;i<=n;i++)
{
sign[G[i]]++;
mx=max(mx,G[i]);
mn=min(mn,G[i]);
}
int k1=(mx-mn)/2;
int k2=mx-mn;
// printf("mn=%d,mx=%d,k=%d",mn,mx,k);
int num;
for(int i=1;i<=100;i++)
{
if(sign[i]!=0) cnt++;
if(G[i]!=mx&&G[i]!=mn&&G[i]!=0) num=G[i];
}
// printf("%d",cnt);
if(n==1||cnt==1)
{
printf("0");
return 0;
}
if(cnt!=2&&cnt!=3)
{
printf("-1");
return 0;
}
else if(cnt==2)
{
if(k2%2==0)
{
printf("%d",k1);
return 0;
}
else
{
printf("%d",k2);
return 0;
}
}
else if(cnt==3)
{
int a=mn+k1;
// printf("a=%d",a);
if(a==num&&k2%2==0)
{
printf("%d",k1);
return 0;
}
else
{
printf("-1");
return 0;
}
}
return 0;
}
C
算出来有多少循环,然后处理一下边上的特殊情况即可。
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdio>
#define MAXN 1000010
using namespace std;
typedef long long ll;
const int INF=1e9+71017;
int n,ans,a[3];
int p[7]={0,0,1,2,0,2,1};
int c[3]={3,2,2};
int g[3];
int getans(int k)
{
int t=INF;
for(int i=0;i<3;i++) g[i]=a[i],t=min(t,a[i]/c[i]);
for(int i=0;i<3;i++) g[i]-=c[i]*t;
t=t*7;
for(int i=k;i!=(k+6)%7;i=(i+1)%7)
{
if(!g[p[i]]) break;
else t++,g[p[i]]--;
}
return t;
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("ce.in","r",stdin);
#endif
scanf("%d%d%d",&a[0],&a[1],&a[2]);
for(int i=0;i<7;i++)
ans=max(ans,getans(i));
printf("%d\n",ans);
return 0;
}
D
贪心
如果没有日光,显然是要先用能被恢复的。
如果有日光,分类讨论一下能被恢复的是否到达上限即可。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define MAXN 200010
int n,a,b,ans;
int p[MAXN];
int main()
{
#ifndef ONLINE_JUDGE
freopen("ce.in","r",stdin);
#endif
scanf("%d%d%d",&n,&a,&b);
int aa=a,bb=b;
for(int i=1;i<=n;i++) scanf("%d",&p[i]);
for(int i=1;i<=n;i++)
{
if(a==0&&b==0) break;
if(p[i]==0)
{
if(b) b--;
else a--;
}
else
{
if(b<bb)
{
if(a) a--,b++;
else b--;
}
else
{
if(b) b--;
else a--;
}
}
ans=i;
}
printf("%d\n",ans);
return 0;
}
E
两个人在一个序列中交替选择,每次从值最大的位置开始选,左右分别拓展,拓展K位(如果没有的话就停止)。每次选择完之后序列会去掉选择的人。问最后原序列上每个位置的人是被谁选择的。
emmmm 上届为\(n^2\)的暴力当然是很好打的啦,如何优化复杂度呢?每次选择完之后,记录一下l,r两个数组,表示选择的人的左右区间,最右边的位置的l为最左边的位置,最左边的位置的r为最右边的位置。这样左右拓展K位的时候就可以避免重复访问已经选择过的人啦!
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define MAXN 400010
using namespace std;
int n,cnt,maxx,k,op=1;
int a[MAXN],l[MAXN],r[MAXN],c[MAXN];
struct Node
{
int pos,sum;
friend bool operator < (Node x,Node y){return x.sum<y.sum;}
};
priority_queue<Node>q;
int main()
{
#ifndef ONLINE_JUDGE
freopen("ce.in","r",stdin);
#endif
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]),l[i]=r[i]=i,q.push((Node){i,a[i]});
maxx=n;
while(!q.empty())
{
int cur_pos=q.top().pos;
c[cur_pos]=op;
int ll=cur_pos-1;
for(int i=1;i<=k;i++)
{
while(c[ll]&&ll>0) ll=l[ll]-1;
if(ll<=0) break;
c[ll]=op;
}
int rr=cur_pos+1;
for(int i=1;i<=k;i++)
{
while(c[rr]&&rr<=n) rr=r[rr]+1;
if(rr<=0) break;
c[rr]=op;
}
r[ll]=rr;
l[rr]=ll;
op=3-op;
while(!q.empty())
{
if(c[q.top().pos])q.pop();
else break;
}
}
for(int i=1;i<=n;i++) printf("%d",c[i]);puts("");
return 0;
}
F
题意是给你一些东西,每个东西都有价值。还有一些优惠条件,形式为“每买n个物品,前m便宜的物品免费”。问如果买k个东西,最少需要多少钱?
因为涉及到前m便宜的东西免费,所以肯定要先从小到大排序。然后我们从小到大取,用优惠条件去凑k个。
这时候。。。就想到了动态规划。
我们设\(dp[i]\)表示排序之后,买i件物品最少需要多少钱。然后记录一个前缀和。
因为k比较小,所以我们只要开一个桶,\(pay[i]\)表示买i个东西,最多可以免费的物品个数。
然后\(k^2\)转移即可。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
using namespace std;
#define MAXN 200010
int n,m,k;
int sum[MAXN],cost[MAXN],pay[MAXN],dp[MAXN];
int main()
{
#ifndef ONLINE_JUDGE
freopen("ce.in","r",stdin);
#endif
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=n;i++) scanf("%d",&cost[i]);
for(int i=1;i<=m;i++)
{
int cur1,cur2;
scanf("%d%d",&cur1,&cur2);
if(cur1>k) continue;
if(pay[cur1]&&cur2>pay[cur1]) pay[cur1]=cur2;
if(pay[cur1]==0) pay[cur1]=cur2;
}
sort(&cost[1],&cost[n+1]);
for(int i=1;i<=n;i++) sum[i]=sum[i-1]+cost[i];
memset(dp,0x3f,sizeof(dp));
dp[0]=0;
for(int i=0;i<=k;i++)
{
for(int j=1;j<=k;j++)
{
if(pay[j]==0) continue;
dp[i+j]=min(dp[i+j],dp[i]+sum[i+j]-sum[i+pay[j]]);
}
dp[i+1]=min(dp[i+1],dp[i]+sum[i+1]-sum[i]);
}
printf("%d\n",dp[k]);
return 0;
}
G
题意是给你一些数,求两个位置,使得这两个位置上面的数的LCM全局最小。
因为LCM是两个数的乘积除以GCD,所以我们考虑枚举GCD,开一个桶记录每个数出现的位置,从GCD向上一路查询,找最小的前两个即可。
怎么说呢,一般这个数据范围都是枚举约数的做法吧!
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdio>
#define MAXN 1000010
using namespace std;
int n,pos1,pos2;
int a[MAXN],pos[10000010];
long long ans=0x3f3f3f3f3f3f3f3f;
int main()
{
#ifndef ONLINE_JUDGE
freopen("ce.in","r",stdin);
#endif
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
if(pos[a[i]])
{
if(a[i]<ans)
ans=1ll*a[i],pos1=pos[a[i]],pos2=i;
}
else pos[a[i]]=i;
}
for(int i=1;i<=10000000;i++)
{
if(i>ans) break;
int ll=0;
for(int j=i;j<=10000000;j+=i)
{
if(pos[j]==0) continue;
if(!ll) ll=j;
else
{
long long cur_ans=1ll*j*ll/i;
if(cur_ans<ans)
{ans=cur_ans,pos1=pos[ll],pos2=pos[j];break;}
}
}
}
if(pos1>pos2) swap(pos1,pos2);
printf("%d %d\n",pos1,pos2);
return 0;
}