A:大意:给出l,r,k,输出k在l到r之间的所有整数次幂,没有输出-1.(l,r,<=10^18)
我做过最坑的A题,没有之一,原本我根本没有想到枚举,在那里乱做除法(不过好像也是我除法写错了的问题),结果后来发现枚举是logk的复杂度….python好多啊..用c++直接乘会爆long long,所以判断是否乘过了r用除法判断,即r/now < k ?
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <string>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <ctime>
using namespace std;
long long l,r,k,val,last = 0;
bool F = false;
int main() {
cin>>l>>r>>k;
val = 1;
while(1) {
if(val >= l && val <= r) F = true,cout<<val<<' ';
if(r / val < k) break;
val = val * k;
}
if( ! F) cout<<"-1";
return 0;
}
B:大意:给出n个字符串,询问他们的乘积。其中几乎所有字符串都只有最多一个一以及多个零组成,但是最多有一个字符串可能会不符合要求,即有多个一或者有其他数字。
唯一过掉的题…这道题也很坑,首先正常做法找出那个不符合要求的字符串,在后面添0就可以了,但是这道题说的是最多一个不符合的字符串,意思是可以没有…坑啊。
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <string>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <ctime>
using namespace std;
int n,zero = 0,lenc;
char s[1000010],a[1000010];
int main()
{
scanf("%d\n",&n);
for(int i = 1;i <= n;i ++)
{
char c = 'd';
int len = 0;
bool F = false;
int count = 0;
while(c < '0' || c > '9') c = getchar();
while(c >= '0' && c <= '9')
{
s[ ++ len] = c;
if(len == 1 && c == '0') printf("0"),exit(0);
if(c != '0' && c != '1') F = true;
if(c == '1') count ++ ;
if(count >= 2) F = true;
c = getchar();
}
if(F) {
for(int i = 1;i <= len;i ++) {
lenc = len;
a[i] = s[i];
}
}
else {
for(int i = 1;i <= len;i ++) {
if(s[i] == '0') zero ++ ;
}
}
}
for(int i = 1;i <= lenc;i ++) printf("%c",a[i]);
if(lenc == 0) printf("1");
for(int i = 1;i <= zero;i ++) printf("0");
return 0;
}
C:大意:按顺序给出平面上n个点以及一个中心,询问这n个点构成的多边形绕中心旋转一圈之后扫过的面积,保证中心在多边形外。(n<=10^5)
扫过之后一定是一个圆..当然可能是一个大圆扣去一个小圆,那么大圆的半径就是中心到最远的顶点的距离,这个可以o(n)枚举得到。然而小圆的半径就不是到最近顶点的距离那么简单了,而是中心到n边形每一条边的最短距离中的最小值。这个可以用三分在logn的时间内做到,但是我用的是作垂线,虽然特判的情况比较多但是比较快就是了…
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <string>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <ctime>
#define eps 1e-4
#define pi 3.1415926535897932384626433
using namespace std;
double maxx = -100,minx = 1e50,a,b,A[1000010],B[1000010];
int n;
double calc(double x,double y,double c,double d) {
double sx = (c - x) * (c - x);
double sy = (d - y) * (d - y);
return sqrt(sx + sy);
}
double find_1st(double x,double y,double c,double d) {
if(fabs(c - x) < eps && b >= min (d,y) && b <= max (d,y)) return fabs(c - a);
else if (fabs(c - x) < eps) return 1e50;
if(fabs(d - y) < eps && a >= min (x,c) && a <= max (x,c)) return fabs(d - b);
else if (fabs(d - y) < eps) return 1e50;
double k = (d - y) / (c - x);
double q = y - k * x;
double l = 1 / k;l = - l;
double r = b - l * a;
double posx = (r - q) / (k - l);
double posy = posx * k + q;
if(min (x,c) - posx > eps || posx - max (x,c) > eps) return 1e50;
return calc(a,b,posx,posy);
}
double find(double x,double y,double c,double d) {
double ret1 = find_1st(x,y,c,d);
double ret2 = calc(a,b,x,y);
double ret3 = calc(a,b,c,d);
return min (ret1,min (ret2,ret3));
}
int main() {
scanf("%d",&n);
scanf("%lf%lf",&a,&b);
for(int i = 1;i <= n;i ++) {
scanf("%lf%lf",&A[i],&B[i]);
maxx = max(maxx,calc(a,b,A[i],B[i]));
}
A[n + 1] = A[1];
B[n + 1] = B[1];
for(int i = 1;i <= n;i ++) {
minx = min(minx,find(A[i],B[i],A[i + 1],B[i + 1]));
}
printf("%0.10f",pi * maxx * maxx - pi * minx * minx) ;
return 0;
}
D:大意:给出n(个数),A(上限),cf(利益1),cm(利益2),m(金钱),然后是n个整数。我们每次可以花费一个金钱使得n个数中的一个上升1,但是不能超过A。那么最后得到的利益=cf * num[A] + cm * min ,即达到上限的数的个数乘以cf加上所有数中的最小值乘以cm,使得利益最大.(n<=10^5,m<=10^13,A,cf,cm<=10^9).
我们先把n个数从大到小排序,那么设sum[x]为使得1-x都到达A所需的代价,pre[x]表示将a[x]-a[n]都加到a[x-1]那么大所需要的代价。那么每次我们枚举加到上限的数的个数i,再二分一个head使得pre[head]正好小于m-sum[i],然后再看剩下的m-sum[i]-pre[head]的金钱能够再把a[head-1]到a[n]提升多少(因为肯定不能提升到a[head-2]那么大了)。统计答案即可。复杂度n * logn.(注意有许多细节比较坑,必须i==n时特判,二分时的许多边界等).
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
struct node{long long v;long long id;long long Ans;
};node p[100010];
long long A,n,cf,cm,m,Ans = -1,k,minx,sum[100010],pre[100010];
bool comp(const node & x,const node & y) {return x.v > y.v;}
bool back(const node & x,const node & y) {return x.id < y.id;}
int main() {
cin>>n>>A>>cf>>cm>>m;
for(long long i = 1;i <= n;i ++) scanf("%I64d",&p[i].v),p[i].id = i,p[i].Ans = p[i].v;
sort(p + 1,p + n + 1,comp);
for(long long i = 1;i <= n;i ++) sum[i] = sum[i - 1] + A - p[i].v;
for(long long i = n;i >= 2;i --)
pre[i] = pre[i + 1] + (p[i - 1].v - p[i].v) * (n - i + 1);
for(long long i = 0;i <= n;i ++) {
long long x = m,ret = 0;
if(i == n) {
if(x >= sum[n]) {
ret = cf * n;
ret += cm * A;
}
if(ret > Ans) {
Ans = ret;
k = i;
minx = A;
}
break;
}
if(x < sum[i]) break;
x -= sum[i];
ret += cf * i;
long long head = max(2ll,i + 1),tail = n + 1;
while(head != tail) {
long long Mid = (head + tail) >> 1;
if(pre[Mid] <= x) tail = Mid;
else head = Mid + 1;
}
long long len = n - head + 1;
x -= pre[head];
if(head - 1 > i) len ++ ;
ret += min(A,p[head - 1].v + (x / len)) * cm;
if(ret > Ans) {
Ans = ret;
k = i;
minx = min(A,p[head - 1].v + x / len);
}
}
for(long long i = 1;i <= k;i ++) p[i].Ans = A;
for(long long i = k + 1;i <= n;i ++) p[i].Ans = max(p[i].Ans,minx);
if(Ans == -1) Ans = 0;
sort(p + 1,p + n + 1,back);
cout<<Ans<<endl;
for(long long i = 1;i <= n;i ++) printf("%I64d ",p[i].Ans);
return 0;
}
E:大意:给出字母数(<=26)和每个字母的次数限制A[i],要求把所有字母用完,拼成一个字符串s,定义f(s),即s中每存在一个点,使得字符串从这里作为起点到字符串结尾正好是一个回文串,那么f(s)+1,求出f(s)的最大值以及此时的s。
情况比较多,但是考虑清楚就没有问题。我们首先需要把所有字母均匀分在较多的字符集合当中,因此我们先求出fac=gcd(A[1],A[2]….A[n]),那么当前我们就拥有了fac个伪回文串,为什么是伪回文串呢?我们考虑这种情况,如果分成fac个子集合后,每个子集合中有超过一个的奇数个数的字母(比如同时出现3个a和5个b),那么一定就不能构成回文串…,所以这个时候如果fac是偶数,我们就从fac中提取一个2出来,使得所有子集合中的字母个数都变成偶数,要不然答案就是0了。我们再来考虑当前分为fac个子串,每个长度为x时,若x是奇数,那么最终的f(s)就是fac,即从每个子串相接处断开之后都是回文串,但是若x是偶数,那么f(s)就是fac*2,为什么呢?因为除了子串相接处,从每个子串的中心断开也是一个回文串,这是显而易见的.(于是这道题就做完了,只是情况多了一些罢了,其实也不是很难的思考题)。
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <string>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <ctime>
using namespace std;
int fac,n,len,num[10010],A[10010],Ans = 0;
char s[5000010];
int gcd(int x,int y) {
if(y == 0) return x;
return gcd(y,x % y);
}
void print() {
for(int i = 1;i <= n;i ++) len += num[i];
if(len % 2 == 1) printf("%d\n",fac);
else printf("%d\n",2 * fac);
for(int i = 1;i <= fac;i ++) {
int head = 0,tail = len + 1;
for(int j = 1;j <= n;j ++) {
for(int k = 1;k <= num[j];k ++) {
if(num[j] % 2 == 1 && k == num[j]) s[len / 2 + 1] = char('a' + (j - 1));
else if(k % 2 == 1) s[ ++ head] = char('a' + (j - 1));
else if(k % 2 == 0) s[ -- tail] = char('a' + (j - 1));
}
}
for(int i = 1;i <= len;i ++) printf("%c",s[i]);
}
exit(0);
}
void print_3rd() {
fac /= 2;
for(int i = 1;i <= n;i ++) num[i] *= 2;
print();
}
void print_4th() {
printf("0\n");
for(int i = 1;i <= n;i ++) {
for(int j = 1;j <= A[i];j ++) {
printf("%c",char('a' + (i - 1)));
}
}
}
int main() {
scanf("%d",&n);
for(int i = 1;i <= n;i ++) scanf("%d",&A[i]);
fac = A[1];
for(int i = 2;i <= n;i ++) fac = gcd(A[i],fac);
for(int i = 1;i <= n;i ++) num[i] = A[i] / fac;
for(int i = 1;i <= n;i ++) {
if(num[i] % 2 == 1) {
Ans ++;
}
}
if(Ans == 0) print();
if(Ans == 1) print();
if(Ans >= 2 && fac % 2 == 0) print_3rd();
if(Ans >= 2 && fac % 2 == 1) print_4th();
return 0;
}
总的来说,我实在没想到我打div2居然都有做不出A题的一天,虽然这场确实算是比较难了,但是也说明我的能力还差得远,不管再难,div 2顶多可能都只能算是noip难度,要为了省选做准备,这种程度是远远不够的。(div 1据说有一道虚树的题,看来要学学虚树了)。