20180714练习赛 [CF]EDU ROUND3 EASY
A - USB Flash Drives CodeForces - 609A
B - The Best Gift CodeForces - 609B
C - Load Balancing CodeForces - 609C
D - Gadgets for dollars and pounds CodeForces - 609D
E - Minimum spanning tree for each edge CodeForces - 609E
A
水题 没什么好说的(也不用纠结)
B
有n本书分属于m类,每本都不同,现要选取两本种类不同的书,问有多少种选择方案
就是模拟题了 但实现有点考人 之前直接O(n^2)判断 T过一次
毕竟N是200000的
关于运行时间的判断:
时限1s时,10^6基本可以过,10^7有点儿悬,10^8就很有可能会T
那我这种暴力都10^10了,怎么可能会过
优化一下,注意到m的范围非常小,只有10,我们统计每一类书的数量然后枚举选哪一类书和哪一类书,根据乘法原理就可以出解了。
观察和预估能力很重要啊!!!
#include<cstdio>
#include<algorithm>
using namespace std;
#define MAXN 200005
int a[MAXN],num[20];
int N,M,ans=0;
int main()
{
scanf("%d %d",&N,&M);
for(int i=1;i<=N;i++)
{
scanf("%d",&a[i]);
num[a[i]]++;
}
for(int i=1;i<=M;i++)
for(int j=i+1;j<=M;j++)
ans+=num[i]*num[j];
printf("%d\n",ans);
}
C
有n台电脑,他们的性能分别是x1,x2…xn,高性能电脑可以每秒将自己的性能传递给低性能电脑,
即自己-1,低性能+1.问最少需要多少秒才能使它们达到平衡。(也就是它们之间的差距最大为1)
最后的序列每个数肯定是平均数或平均数+1
算出这两个数 依次遍历一遍,看与这两个数更近的那个还差多少
但这么做会WA 因为可能会“对不上数”,要保证变化后的序列总和仍然不变
sum%n 表示的就是每个数为“平均数”之后还剩了多少个1,也就是还有这么多个数转化后值为“平均数”+1,显然让更大的数去变成“平均数”+1
然而我在考场上没想到这个 做法是:让小的数变成“平均数”,让大的数变成“平均数”+1,然后去它们的代价的最大值
Like this:
int ans1=0,ans2=0;
for(int i=1;i<=N;i++)
{
if(a[i]<l) ans1+=l-a[i];
if(a[i]>r) ans2+=a[i]-r;
}
但我这么写其实没有上面那种好理解 它的思想其实就是:把所有低于平均值的数提升到平均值,需要总共加ans1,也就是大于平均数的值,要减去ans1才能满足。而让大于平均数的值符合调价(变成“平均数”+1)又至少要减去ans2才能满足。同时要满足两种情况,只能取最大值。而ans1和ans2的差,实际上就是不能去最优的数的个数(小于“平均数”的值变成“平均数”+1,大于平均数的值变成平均数)
#include<cstdio>
#define MAXN 100005
int N,a[MAXN],ans;
int Max(int x,int y)
{
if(x>y) return x;
else return y;
}
int main()
{
int sum=0,l=0,r=0;
scanf("%d",&N);
for(int i=1;i<=N;i++)
{
scanf("%d",&a[i]);
sum+=a[i];
}
l=sum/N;
r=l+1;
int ans1=0,ans2=0;
for(int i=1;i<=N;i++)
{
if(a[i]<l) ans1+=l-a[i];
if(a[i]>r) ans2+=a[i]-r;
}
ans=Max(ans1,ans2);
printf("%d\n",ans);
}
D
二分
细节有点多,想通了后也挺简单的
调了很久 发现二分的初始边界不够大。。。
#include<cstdio>
#include<algorithm>
using namespace std;
#define MAXN 200005
#define LL long long
#define INF 1000000000
struct node{
int type,id;
LL need,bur;
}g[MAXN];
int n,m,k;
LL s;
int a[MAXN],p[MAXN],d[MAXN];
struct no{
int id,day;
}ans[MAXN];
bool cmp(node x,node y)
{
return x.bur<y.bur;
}
bool check(int mid)
{
int pd=1,pp=1;
for(int i=2;i<=mid;i++)
{
if(d[i]<d[pd])
pd=i;
if(p[i]<p[pp])
pp=i;
}
for(int i=1;i<=m;i++)
{
if(g[i].type==1) g[i].bur=d[pd]*g[i].need;
else g[i].bur=p[pp]*g[i].need;
}
sort(g+1,g+m+1,cmp);
LL res=0;
for(int i=1;i<=k;i++)
res+=g[i].bur;
if(res>s) return 0;
for(int i=1;i<=k;i++)
{
ans[i].id=g[i].id;
if(g[i].type==1) ans[i].day=pd;
else ans[i].day=pp;
}
return 1;
}
int main()
{
scanf("%d %d %d %lld",&n,&m,&k,&s);
for(int i=1;i<=n;i++)
scanf("%d",&d[i]);
for(int i=1;i<=n;i++)
scanf("%d",&p[i]);
for(int i=1;i<=m;i++)
{
scanf("%d %lld",&g[i].type,&g[i].need);
g[i].id=i;
}
int l=0,r=n+1;
bool f=0;
while(l+1<r)
{
int mid=(r-l)/2+l;
if(check(mid))
r=mid,f=1;
else l=mid;
}
if(!f)
{
printf("-1\n");
return 0;
}
printf("%d\n",r);
for(int i=1;i<=k;i++)
printf("%d %d\n",ans[i].id,ans[i].day);
}
E
求含有每条边的最小生成树
考场上想出来了正解 但是不会打。。。
就是先求最小生成树,最小生成树上的边get√
然后非最小生成树上的边就在生成树上连起来,然后破圈。
但是我不会打呀啊啊啊,破圈从来没打过呀啊啊啊qwq