Codeforces Round #339 (Div. 2) (A,B,C,D)
tags : Codeforces
这场写得我心好累。昨天早上虚拟的时候两个小时才搞定两题,第三题就写了个漏洞百出的版本不过没交上去。昨天下午补了C题,结果被D题坑了快一天,今天下午才好不容易AC,感觉整个人都不好了。。。╮(╯_╰)╭所以我决定这份题解描述题意的时候尽量简化一下
A.Link/Cut Tree
题意
输出[l,r]范围内k的倍数
解析
暴力即可,只需注意防中间运算爆long long
代码
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <cassert>
#include <algorithm>
using namespace std;
#define MAXN 100000+100
long long l, r,k;
int main()
{
scanf("%lld%lld%lld",&l,&r,&k);
long long p = 1;
int first = 1;
while (p<=r)
{
if (p >= l)
{
if (!first)
printf(" ");
printf("%lld",p);
first = 0;
}
if ((r*1.0 / p) < k)
break;
p *= k;
}
if (first)
printf("-1\n");
else
printf("\n");
return 0;
}
B.Gena’s Code
题意
求n个数相乘的结果。
解析
数字超级大,不能直接相乘,需要以字符串方式处理。
题目讲得很明白了,最多只有一个不是10的倍数,只要找到这个数,然后作为前缀,其他数字只考虑是10的多少次(即,字符串长度-1),求和得到相乘后0的个数k(不考虑前缀中的0)。
输出时先输出前缀,再输出k个0即可。
注意,前缀可能为0,此时不用再输出0。
代码
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <cassert>
#include <algorithm>
using namespace std;
#define MAXN 100000+100
char str[MAXN];
int check(int l)
{
if (l == 1 && str[0] > '1')
return 1;
int cnt = 0;
for (int i = 0; i < l; i++)
{
if (str[i] > '1')
return 1;
else if (str[i] == '1')
{
cnt++;
if (cnt > 1)
return 1;
}
}
return 0;
}
int main()
{
int n;
scanf("%d",&n);
char head[MAXN]="1";
int len = 0;
for (int i = 0; i < n; i++)
{
int k;
scanf("%s",str);
int slen = strlen(str);
if (str[0] == '0' && slen == 1)
{
strcpy(head,str);
break;
}
if (check(slen))
{
strcpy(head, str);
}
else
{
len += slen-1;
}
}
printf("%s", head);
if (head[0] != '0')
{
for (int i = 0; i < len; i++)
printf("0");
}
printf("\n");
return 0;
}
C.Peter and Snow Blower
题意
求多边形绕点P旋转扫过的面积。
解析
主要就是求内半径和外半径
显而易见,外半径就是P点到多边形顶点的最大距离。遍历即可求出。
而内半径,是P点到多边形边上点的最小距离。抄一份点到线段的最短距离代码即可算出。
代码
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <cassert>
#include <algorithm>
using namespace std;
#define PI 3.1415926535897932384626
#define MAXN 100000+100
const double esp = 1e-7;
double dis(double x1, double y1, double x2, double y2)
{
return sqrt((x1 - x2*1.0)*(x1 - x2*1.0) + (y1 - y2*1.0)*(y1 - y2*1.0));
}
struct node
{
double x, y;
node(){};
node(double xx, double yy)
{
x = xx;
y = yy;
}
}p[MAXN];
double Dot(node a, node b) //点积
{
return a.x*b.x + a.y*b.y;
}
double Length(node a) { return sqrt(Dot(a, a)); }
double dis2line(node a, node b, node c)
{
node v1, v2, v3;
v1.x = c.x - b.x;
v1.y = c.y - b.y;
v2.x = a.x - b.x;
v2.y = a.y - b.y;
v3.x = a.x - c.x;
v3.y = a.y - c.y;
if (Dot(v1, v2)<0) return Length(v2);
else if (Dot(v1, v3)>0) return Length(v3);
else return fabs((v1.x*v2.y - v2.x*v1.y) / Length(v1));
}
int main()
{
int n;
double px, py;
double mindis = -1;
double maxdis = -1;
scanf("%d%lf%lf", &n, &px, &py);
for (int i = 0; i < n; i++)
{
double x, y;
scanf("%lf%lf", &x, &y);
p[i].x = x;
p[i].y = y;
double dist = dis(p[i].x, p[i].y, px*1.0, py*1.0);
if (maxdis < 0 || dist > maxdis)
maxdis = dist;
if (mindis <0 || dist < mindis)
mindis = dist;
if (i > 0)
{
dist = dis2line(node(px, py), node(x, y), node(p[i - 1].x, p[i - 1].y));
if (maxdis <0 || dist > maxdis)
maxdis = dist;
if (mindis <0 || dist < mindis)
mindis = dist;
}
}
double dist = dis2line(node(px, py),node(p[0].x, p[0].y), node(p[n - 1].x, p[n - 1].y));
if (maxdis < 0 || dist > maxdis)
maxdis = dist;
if (mindis < 0 || dist< mindis)
mindis = dist;
printf("%.8lf\n", (maxdis*maxdis*PI - mindis*mindis*PI));
return 0;
}
D.Skills
这题坑了我整整一天啊fuck,,,简直是知道做法还是做不出来的典范啊艹。用逗比的话来说就是,“明明知道做法但是姿势怎么摆都不对”
题意
一共n个技能,最大等级都为A,当前等级已给出。现在有m个技能点,需要通过这些技能点使“战斗力”最大化
战斗力=满级技能数*Cf + 最低技能等级*Cm
输出最大化的战斗力和对应的技能等级。
解析
先将所有技能位置和等级记录下来,然后按等级排序。然后进行预处理,求出后缀和sum[i]
。
显然能做的操作有两种:1.将当前等级最高的技能提升至满级,2.将当前等级最低的技能提升至一定等级。操作时先1后2相对简便很多。
我们可以枚举满级技能的数目full
,减去消耗掉的i*A - (sum[0] - sum[i])
后,剩下的技能点为mm
,然后每次迭代去计算使用mm
点的技能点能将技能最低等级提升至多少。如何计算呢?我们可以通过二分查找,找到[i,n-1]范围内第一个满足min(A, (sum[mid] + mm) / (n - mid)) >= arr[mid].v
的点(其值记为minv
)。也就是说,将mm
加到从当前位置mid开始到最后的技能上,使其等级全部提升至min(A, (sum[mid] + mm)
,假如这个值大于或等于arr[mid].v
,说明mm
够用;反之,若小于arr[mid].v
,说明mm
不够用,应当考虑将mid后移。
每次迭代得到一个full
和minv
从而计算出对应的战斗力fnow
,记下fnow
的最大值及对应的full
和minv
值。
得到最大时的full
和minv
值后,很容易就能将技能提升为最终的等级。最后重新按序号排序即可。
实际上有一种更快一点的方法,就是按满级技能数full从大到小进行处理,可以将复杂度压缩一点(虽然差距并不大)。
代码
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <cassert>
#include <algorithm>
using namespace std;
#define MAXN 100000+100
struct node
{
int index;
long long v;
}arr[MAXN];
bool cmpi(node a, node b)
{
return a.index < b.index;
}
bool cmpv(node a, node b)
{
return a.v > b.v;
}
long long sum[MAXN];
int main()
{
int n;
long long cf, cm, m, A;
scanf("%d%lld%lld%lld%lld", &n, &A, &cf, &cm, &m);
for (int i = 0; i < n; i++)
{
scanf("%lld", &arr[i].v);
arr[i].index = i;
}
sort(arr, arr + n, cmpv);
sum[n] = 0;
for (int i = n - 1; i >= 0; i--)
{
sum[i] = sum[i + 1] + arr[i].v;
}
m = min(A*n - sum[0], m);
long long s = 0;
long long full = 0;
long long minv = 0;
long long maxf = 0;
for (int i = 0; i < n; i++)
{
long long mm;
if (i == 0)
mm = m;
else
mm = m - (i*A - (sum[0] - sum[i]));
if (mm < 0)
break;
long long avg = min(A, (sum[i] + mm) / (n - i));
long long l = i,r=n-1;
while (l<r)
{
long long mid = (l + r) / 2;
avg = min(A, (sum[mid] + mm) / (n - mid));
if (avg >= arr[mid].v)
r = mid;
else
l = mid+1;
}
avg= min(A, (sum[r] + mm) / (n - r));
if (i == 0)
{
if (avg == A)
{
maxf = cf*n + cm*avg;
minv = avg;
full = n;
}
else
{
maxf = cm*avg;
minv = avg;
full = 0;
}
}
else
{
long long fnow = i*cf + avg*cm;
if (fnow > maxf)
{
maxf = fnow;
minv = avg;
full = i;
}
}
}
printf("%lld\n", maxf);
for (int i = 0; i < n; i++)
{
if (i < full)
arr[i].v = A;
if (arr[i].v < minv)
arr[i].v = minv;
}
sort(arr, arr + n, cmpi);
int first = 1;
for (int i = 0; i < n; i++)
{
if (!first)
printf(" ");
printf("%lld", arr[i].v);
first = 0;
}
printf("\n");
return 0;
}