Codeforces round339 div2

本文总结了五道编程竞赛题目,包括枚举优化、字符串处理、几何计算、动态规划及组合优化等方面,分享了解题思路与代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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据说有一道虚树的题,看来要学学虚树了)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值