Codeforces Round 339 div2
通过数:3
Standing: 325 / 5845
比赛状态不是很好,尤其是第一题大数被叉掉后整个人都不好了。第三题计算几何问可为借的版,后来老耿说海伦公式就可以过……
补第四题的时候自己写的,结果WA了两天,最后发现思路有问题详见代码。
第五题问的Q神,再次感谢~
A:
问[L,R]区间
套的大数版,就不粘贴代码因为太占篇幅。
有判断下一次做乘法是否会溢出的做法,说完秒懂感觉智商低……
B:
问一堆数的乘积是多少。这些数比较特殊,最多只有一个“非法数”,其余都是合法数:十进制表示最多有一位是1,其余均是0。
刚好那天在手残期,而且好像那个非法数用long long读取的时候会爆掉还是怎么样,瞎改成字符串就过了。这点我想投诉,明明说的输入integer好嘛!
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <string>
using namespace std;
#define LL long long
int valid(char *s)
{
int t1 = 0;
while(*s){
int temp = *s - '0';
if(temp > 1) return 1;
if(temp == 1) t1++;
s++;
}
if(t1 <= 1) return 0;
else return 1;
}
char s[100000 + 4];
char t2[100000 + 4];
int main()
{
int n;
while(scanf("%d", &n) != EOF){
int ok = 1;
LL t1 = 0;
strcpy(t2, "1");
for(int i = 1 ; i <= n ; i++){
LL u; scanf("%s", s);
// printf("i = %d\n", i);
if(!strcmp(s, "0")) ok = 0;
else if(!strcmp(s, "1")) continue;
else{
int flag = valid(s);
if(flag){
strcpy(t2, s);
}
else{
for(int i = 0 ; i < (int)strlen(s) ; i++){
int u = s[i] - '0';
if(u == 0) t1++;
}
}
}
}
if(ok == 0) printf("0\n");
else{
printf("%s", t2);
for(int i = 0 ; i < t1 ; i++) printf("0");
printf("\n");
}
}
return 0;
}
C:
问一堆点围成的凸包,绕一个点旋转一圈形成的圆环面积。
想了想无非是找最近和最远点。然而WA了,以为是精度问题继续WA了8发。
就在我以为这次要掉成渣的时候,突然发现最近点不是每个点每个点来求,比如两个点形成的线段上可能就有最近点。
然后到处找版,要了个点到线段的距离版过了。
int dcmp(double x) {
if(fabs(x)<eps) return 0;else return x < 0 ? -1 : 1;
}
struct Point
{
double x,y;
Point(){}
Point(double _x,double _y){
x = _x;y = _y;
}
};
bool operator < (const Point& a,const Point& b) {
return a.x<b.x||(a.x==b.x&&a.y<b.y);
}
bool operator == (const Point& a,const Point &b) {
return dcmp(a.x-b.x) == 0 && dcmp(a.y-b.y) == 0;
}
double dis(Point a,Point b){
return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
typedef Point Vec; //оРа©
Vec operator + (Vec A,Vec B) {return Vec(A.x+B.x,A.y+B.y); }
Vec operator - (Point A,Point B) {return Vec(A.x-B.x,A.y-B.y); }
Vec operator * (Vec A,double p) {return Vec(A.x*p,A.y*p); }
Vec operator / (Vec A,double p) {return Vec(A.x/p,A.y/p); }
double Dot (Vec A,Vec B) { return A.x*B.x+A.y*B.y; }
double Length(Vec A) { return sqrt(Dot(A,A)); }
double Angle(Vec A,Vec B) { return acos(Dot(A,B)/Length(A)/Length(B)); }
double Cross(Vec A,Vec B) { return A.x*B.y-A.y*B.x; }
double Dis_to_Seg(Point P,Point A,Point B){
if(A==B) return Length(P-A);
Vec v1=B-A,v2=P-A,v3=P-B;
if(dcmp(Dot(v1,v2))<0) return Length(v2);
else if(dcmp(Dot(v1,v3))>0) return Length(v3);
else return fabs(Cross(v1,v2))/Length(v1);
}
D:
一个数字序列,长度(1e5)。每个数字最大A,以后的操作中也只能把它最大变成A。
现在有一个函数g = cm * mmin + cf * numOfValueEqualToA。
现在可以对一个数加1加m次,问这个函数的最大值是多少。
显然扫描线,还有一种叫法叫two pointer吧,然而不断WA。后面开的随机数大法才找到问题,有一个小细节没处理到。还是太菜。
AC版:
/* 主要错误是思路和逻辑的错误。当属于最低水平的一个数划归给最高水平时,最低水平有全部提升的可能。 写法上试图开一个全局变量来维护,结果是分类讨论过多导致细节繁琐。更好的办法是尽可能的封装起每一个步骤,让每个模块之间只有输入输出互相接触。 */
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <string>
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
#define LL long long
#define MAXN (200000+5)
struct D
{
LL data;
int mark;
}d[MAXN];
LL sum[MAXN];
bool cmp1(D a, D b){return a.data < b.data;}
bool cmp2(D a, D b){return a.mark < b.mark;}
int main()
{
LL n, a, cf, cm, m;
while(scanf("%I64d%I64d%I64d%I64d%I64d", &n, &a, &cf, &cm, &m) != EOF){
for(int i = 1 ; i <= n ; i++){
scanf("%I64d", &d[i].data);
d[i].mark = i;
}
sort(d + 1, d + 1 + n, cmp1);
for(int i = 1 ; i <= n ; i++){
sum[i] = sum[i - 1] + d[i].data;
}
LL ans = 0;
int t1, t2, t3;
t1 = 0, t2 = n;
int re = n;
for(int i = n ; i >= 0 ; i--){
LL cost2 = a * (n - i) - (sum[n] - sum[i]);
LL cost1 = m - cost2;
if(cost1 < 0) break;
re = min(re, i);
while(re && d[re].data * re - sum[re] > cost1) re--;
LL data = (cost1 - d[re].data * re + sum[re]) / re + d[re].data;
data = min(data, a);
if(i == 0) data = a;
LL tans = data * cm + cf * (n - i);
if(ans < tans){
// printf("tans = %I64d, data = %I64d\n", tans, tdata)
ans = tans;
t1 = re, t2 = i, t3 = data;
}
}
for(int i = 1; i <= t1 ; i++) d[i].data = t3;
for(int i = t2 + 1 ; i <= n ; i++) d[i].data = a;
sort(d + 1, d + 1 + n, cmp2);
printf("%I64d\n", ans);
for(int i = 1 ; i <= n ; i++){
printf("%I64d", d[i].data);
if(i == n) printf("\n");
else printf(" ");
}
}
return 0;
}
WA版:
#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <string>
#include <algorithm>
#include <iostream>
using namespace std;
#define LL long long
const int MAXN = 100000 + 5;
struct D
{
LL data;
int mark;
}d[MAXN];
LL sum[MAXN];
bool cmp1(D a, D b){return a.data < b.data;}
bool cmp2(D a, D b){return a.mark < b.mark;}
int main()
{
// freopen("data.txt", "r", stdin);
// freopen("data1.txt", "w", stdout);
LL a, n, cf, cm, m;
while(scanf("%I64d%I64d%I64d%I64d%I64d", &n, &a, &cf, &cm, &m) != EOF){
d[0].data = 0;
for(int i = 1 ; i <= n ; i++) {
scanf("%I64d", &d[i].data); d[i].mark = i;
}
sort(d + 1, d + n + 1, cmp1);
for(int i = 1 ; i <= n ; i++) sum[i] = sum[i - 1] + d[i].data;
// d[0].data = d[1].data;
LL now = 1;
LL data = d[1].data;
LL cost1 = 0;
while(now < n && cost1 <= m){
if((d[now + 1].data - data) * now + cost1 > m){
LL tdata = (m - cost1) / now + data;
cost1 = cost1 + (tdata - data) * now;
data = tdata;
break;
}
else{
cost1 += (d[now + 1].data - data) * now;
data = d[++now].data;
}
// if(sum[now] < m)
}
if(now == n && cost1 <= m){
LL tdata = min(a, (m - cost1) / now + data);
cost1 = cost1 + (tdata - data) * now;
data = tdata;
}
LL ans = cm * data;
if(data == a) ans += cf * n;
else{
LL temp = n;
while(temp >= 1 && d[temp].data == a) ans += cf, temp--;
}
LL t1, t2, t3;
t1 = now, t2 = n + 1, t3 = data;
// printf("now = %d, data = %I64d, ans = %I64d\n", now, data, ans);
LL cost2 = 0;
for(LL i = n ; i >= 1 ; i--){
cost2 += (a - d[i].data);
if(cost2 > m) break;
// if(i == 81) printf("cost1 = %I64d, cost2 = %I64d, m = %I64d\n", cost1, cost2, m);
while(now >= i || cost1 + cost2 > m){
if(now == 0) break;
while(d[now].data == data && now >= 1) now--;
// if(cost1 + cost2 <= m){
// if(i == 1982) printf("first\n");
// if(i == 1982){
// printf("cost1 = %I64d, data = %I64d, d[now].data = %I64d\n", cost1, data, d[now].data);
// }
// cost1 -= (data - d[now].data) * now; data = d[now--].data;
// }
// else{
// if(i == 1982) printf("second\n");
LL temp = (data - d[now].data) * now;
// printf("temp = %I64d\n", temp);
if(cost1 + cost2 - temp > m){cost1 -= temp, data = d[now--].data;}
else if(cost1 + cost2 <= m)
else{
// printf("second\n");
LL tdata = data - ceil(1.0 * (cost1 + cost2 - m) / now);
if(tdata >= data) tdata = data - 1;
// printf("tdata = %I64d, cost1 + cost2 - m = %I64d, now = %I64d\n", tdata, cost1 + cost2 - m, now);
// printf("data = %I64d, tdata1 = %I64d, ", data, tdata);
tdata = max(tdata, d[now].data);
// printf("tdata2 = %I64d\n", tdata);
cost1 -= (data - tdata) * now;
data = tdata;
// }
}
// else{
// LL temp = (data - d[now].data) * now;
// if(cost1 + cost2 - temp >= m) cost1 -= temp, data = d[now--].data;
// else{
// LL tdata = data - floor(1.0 * (cost1 + cost2 - m) / now);
//// tdata = max(tdata, d[now].data);
// cost1 = cost1 - (data - tdata) * now;
// data = tdata;
// if(tdata == d[now].data) now--;
// }
// }
// printf("now = %I64d, cost1 = %I64d, cost2 = %I64d, m = %I64d, data = %I64d, i = %I64d\n", now, cost1, cost2, m, data, i);
// system("pause");
}
// printf("i = %I64d, data = %I64d, cost1 = %I64d, cost2 = %I64d, now = %d\n", i, data, cost1, cost2, now);
// if(n == 2054 && data == 5674) printf("i = %d\n", i);
// printf("i = %d, data = %I64d\n", i, data);
if(cost1 + cost2 > m) break;
if(i == 1) data = a;
LL tans = cm * data + cf * (LL)(n - i + 1);
if(ans < tans){
ans = tans;
t1 = now, t2 = i, t3 = now <= 0 ? a : data;
}
}
for(int i = 1 ; i <= t1 ; i++) d[i].data = t3;
for(int i = t2 ; i <= n ; i++) d[i].data = a;
sort(d + 1 , d + 1 + n, cmp2);
printf("%I64d\n", ans);
for(int i = 1 ; i <= n ; i++){
printf("%I64d", d[i].data);
if(i == n) printf("\n");
else printf(" ");
}
}
}
E:
参考了TankEnginer大神代码,听了q神思路。
26个字母分别有多少多少个。保证所有字母的个数和<=1e6.
然后问这样一种把他们排列的组合,使得他们构成一个环。这个环上从两个字母间分开后能得到一个回文串,然后这种分开的地方最多……好绕,文字功力有限。
分类讨论。首先把最简单的只有一种字母的情况去掉。然后发现字母数量为奇数个时对答案有影响所以分开讨论。两个以上无解。一个和0个分开讨论。这时可以把这个环看成几个几个的小等分,小等分们或对称(0个),或相同(1个)。小等分的长度是所有字母数量的最大公约数。然后不断不断的reverse,或者equalto。
然后你看看代码?
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
#define gcd(a,b) __gcd(a,b)
const int MAXN = 1e6 + 5;
int num[50];
char str[MAXN];
int main()
{
int n;
while(scanf("%d", &n) != EOF){
int ok = 2;
int mmin = MAXN, re;
int sum = 0;
for(int i = 0 ; i < n ; i++){
scanf("%d", &num[i]);
sum += num[i];
if(num[i] % 2 == 1) ok--, re = i;
if(mmin > num[i]) mmin = num[i];
}
int ans = 0;
if(n == 1){
// printf("%d\n", sum);
ans = num[0];
for(int i = 0 ; i < num[0] ; i++) str[i] = 'a';
str[num[0]] = '\0';
}
else if(ok <= 0){
ans = 0;
int now = 0;
for(int i = 0 ; i < n ; i++) for(int j = 0 ; j < num[i] ; j++) str[now++] = 'a' + i;
str[now] = '\0';
}
else if(ok == 1){
// printf("first\n");
int g = 0;
for(int i = 0 ; i < n ; i++){
if(num[i] % 2 == 0){
g = g == 0 ? num[i] : gcd(g, num[i]);
}
}
g /= 2;
g = gcd(g, num[re]);
ans = g;
int now = 0;
for(int i = 0 ; i < g ; i++){
for(int j = 0 ; j < num[re] / g ; j++) str[now++] = re + 'a';
for(int j = 0 ; j < n ; j++){
if(j == re) continue;
for(int k = 0 ; k < num[j] / g / 2 ; k++)
str[now++] = j + 'a';
}
for(int j = n - 1 ; j >= 0 ; j--){
if(j == re) continue;
for(int k = 0 ; k < num[j] / g / 2 ; k++)
str[now++] = j + 'a';
}
}
str[now] = '\0';
}
else{
int g = 0;
for(int i = 0 ; i < n ; i++)
g = g == 0 ? num[i] : gcd(g, num[i]);
ans = g;
int now = 0;
for(int i = 0 ; i < n ; i++){
for(int j = 0 ; j < num[i] / g ; j++)
str[now++] = 'a' + i;
}
int p = now;
for(int temp = 0 ; temp < g - 1 ; temp++){
int cur = now;
for(int i = 0 ; i < n ; i++){
for(int j = 0 ; j < num[i] / g ; j++){
// printf("now = %d, cur * 2 - 1 - now = %d, cur = %d\n", now, cur * 2 - 1 - now, cur);
str[now] = str[cur * 2 - 1 - now];
now++;
}
}
}
// for(int temp = 0 ; temp < g - 1 ; temp++){
// int cur = now;
// for(int j = 0 ; j < p ; j++)
// str[now++] = str[cur - 1 - j];
// }
str[sum] = '\0';
}
printf("%d\n", ans);
puts(str);
}
return 0;
}