ABC389

场上被 E 卡 50 min 结果赛后一分钟过 F!场上被 E 卡 50 min 结果赛后一分钟过 F!场上被 E 卡 50 min 结果赛后一分钟过 F!场上被 E 卡 50 min 结果赛后一分钟过 F!场上被 E 卡 50 min 结果赛后一分钟过 F!场上被 E 卡 50 min 结果赛后一分钟过 F!场上被 E 卡 50 min 结果赛后一分钟过 F!场上被 E 卡 50 min 结果赛后一分钟过 F!场上被 E 卡 50 min 结果赛后一分钟过 F!场上被 E 卡 50 min 结果赛后一分钟过 F!场上被 E 卡 50 min 结果赛后一分钟过 F!场上被 E 卡 50 min 结果赛后一分钟过 F!场上被 E 卡 50 min 结果赛后一分钟过 F!场上被 E 卡 50 min 结果赛后一分钟过 F!场上被 E 卡 50 min 结果赛后一分钟过 F!场上被 E 卡 50 min 结果赛后一分钟过 F!场上被 E 卡 50 min 结果赛后一分钟过 F!场上被 E 卡 50 min 结果赛后一分钟过 F!场上被 E 卡 50 min 结果赛后一分钟过 F!场上被 E 卡 50 min 结果赛后一分钟过 F!场上被 E 卡 50 min 结果赛后一分钟过 F!场上被 E 卡 50 min 结果赛后一分钟过 F!场上被 E 卡 50 min 结果赛后一分钟过 F!场上被 E 卡 50 min 结果赛后一分钟过 F!场上被 E 卡 50 min 结果赛后一分钟过 F!场上被 E 卡 50 min 结果赛后一分钟过 F!场上被 E 卡 50 min 结果赛后一分钟过 F!场上被 E 卡 50 min 结果赛后一分钟过 F!场上被 E 卡 50 min 结果赛后一分钟过 F!场上被 E 卡 50 min 结果赛后一分钟过 F!场上被 E 卡 50 min 结果赛后一分钟过 F!场上被 E 卡 50 min 结果赛后一分钟过 F!场上被 E 卡 50 min 结果赛后一分钟过 F!场上被 E 卡 50 min 结果赛后一分钟过 F!场上被 E 卡 50 min 结果赛后一分钟过 F!场上被 E 卡 50 min 结果赛后一分钟过 F!场上被 E 卡 50 min 结果赛后一分钟过 F!场上被 E 卡 50 min 结果赛后一分钟过 F!场上被 E 卡 50 min 结果赛后一分钟过 F!场上被 E 卡 50 min 结果赛后一分钟过 F!

A - 9x9

借助 scanf 可以轻松完成。

点击查看代码
#include <iostream>
#include <cstdio>
using namespace std;
int a,b;
int main(){
	scanf("%dx%d",&a,&b);
	cout<<a*b<<endl;
} 

B - tcaF

发现 X 值不超过 3×1018,证明 N 的值不超过 50,直接暴力枚举即可。

点击查看代码
#include <iostream>
#include <cstdio>
using namespace std;
long long a,fac=1;
int main(){
	cin>>a;
	for(long long i=1;;i++){
		fac*=i;
		if(fac==a){
			cout<<i<<endl;
			return 0;
		}
	}
	
} 

C - Snake Queue

出题人都让我们用队列了,那就用队列吧。

我们用队列维护没有离开的蛇。

每加入一条蛇,计算它的头部到第一条加入操作的蛇的头部的距离,记第 i 条蛇的这个值为 si,然后把它加进队尾。

每删除一条蛇,就让他从队头离开。

操作三实际上就是查询第 k 条没有离开的蛇到第 1 条没有离开的蛇之间的距离。如果队头的蛇是 l,则他们头部之间的距离即为 sl+k1sl

主要考察前缀和。

点击查看代码
#include <iostream>
#include <cstdio>
using namespace std;
const int N=3e5+10;
typedef long long ll;
ll s[N],l,r,n;
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		ll op,x;
		scanf("%lld",&op);
		if(op==1){
			scanf("%lld",&x);
			r++,s[r]=s[r-1]+x;
		}else if(op==2){
			l++;
		}else if(op==3){
			scanf("%lld",&x);
			printf("%lld\n",s[l+x-1]-s[l]);
		}
	}
	return 0;
} 

时间复杂度 O(n)

D - Snake Queue

我们将圆等分成四部分,容易发现这四部分包含的方格个数都一样。所以我们只计算右上部分的,最后乘 4 即可。

将圆心所在的格子用 (0,0) 表示,则格子 (i,j) 被圆包含必须满足 (i+0.5)2+(j+0.5)2 不超过 r,前提是 i0,j0,即这个格子右上角到圆心的距离不超过 r,这里会有算重,但是先不管。

假设 i=0 的所有格子中,满足上述条件且最靠上的格子是 (0,j0),同理定义 (1,j1),(2,j2),容易发现,j0j1j2,可以用双指针快速计算。

将答案乘四后,我们发现 (0,y),(0,y),(x,0),(x,0) 这样的格子都算了两遍,所以要减去。最后再将原点对答案的贡献加上,即可得出正确答案。

好像有通项公式。

点击查看代码
#include <iostream>
#include <cstdio>
using namespace std;
const int N=3e5+10;
long long ans,r,res;
double dist(double x,double y){
	return x*x+y*y;
}
int main(){
	cin>>r;
	for(int i=0,j=r-1;i<r;i++){
		while(dist(i+0.5,j+0.5)>r*r)j--;
		if(i==0)res=j+1;
		ans+=j+1;
	}
	cout<<ans*4-4*res+1<<endl; 
	return 0;
} 

时间复杂度 O(R)

E - Square Price

又被 E 创思了。

本题笔者场上没写出来,所以没代码。思路由赛后热心谷民提供。

10100 是为了告诉我们商品无穷多。

先将所有商品按照 Pi 排序,记第 i 种商品最优方案下会买 Si 种。

我们思考这样一个买商品的过程:

每个商品用二元组 (k,p) 表示,初始时第 i 个商品为 (1,Pi),表示这个商品是第 k 次买,价格p,则买下这个商品的费用[(k+1)2k2]p=(2k+1)p

我们把所有决策插入堆中,然后取出当前费用最小的商品,将总费用加上 (2k+1)p 后,将 (k,p) 删除,插入 (k+1,p),直到总费用超过 m。此时买下的商品数目即为最优方案,该算法时间复杂度 O(mlogn),无法通过本题。

回顾 Si,Pi 的定义,容易发现一个性质,S1S2S3

同时,最优方案下,无论何时,都满足对于任意 in1(Si,Pi) 的费用小于 (Si+1+1,Pi+1) 的费用,如果不是,那就让 i 少买一个,i+1 多买一个。

我们惊喜的发现,在这种约束下,只要确定了 S1,剩下的 n 个商品买多少唯一确定。且 S1 越大,最后确定的购买方案的费用总和也越大。

这样做,只需二分 S1,然后确定剩下的序列,再判断这个方案总费用是否超标。

时间复杂度 O(nlogm)

F - Rated Range

因为 E 写了 50 min,导致这道题没有时间调试,最后赛后光速过题。

fi 表示初始时评分为 i,打完 n 轮后评分为 fi

以第一个样例为例,初始时:

i 1 2 3 4 5 6 7 8
fi 1 2 3 4 5 6 7 8

评分超过 8 的部分不展示。

其中,评分在 15 的可以加评分,则:

i 1 2 3 4 5 6 7 8
fi 2 3 4 5 6 6 7 8

第二轮:评分 13 的可以加评分:

i 1 2 3 4 5 6 7 8
fi 3 4 4 5 6 6 7 8

第三轮:评分 36 的可以加评分:

i 1 2 3 4 5 6 7 8
fi 4 5 5 6 7 6 7 8

剩下几轮可以自己手摸。

容易发现,不管怎么打公开赛,f 数组始终单调不下降。

证明很简单,假设存在某个位置 ifi>fi+1,则一定存在某一时刻:fi=fi+1,然后打了一场比赛,fi 评分增加,fi+1 没有变化,但是 fi=fi+1,两者评分应当同时增加,所以这种情况不存在。

这样就好办多了。用线段树维护 f,假设某一场公开赛,等级分在 [l,r] 内的可以加评分,因为 f 单调不减,用线段树二分二分出第一个满足 fil 的位置 L,和最后一个满足 fir 的位置 R,实际上后者就相当于第一个满足 fir 的位置的前一个位置。

加评分就是区间加,用懒标记维护。最后处理询问 x 时,直接查询 fx 的值即可。

点击查看代码
#include <iostream>
#define ls (p<<1)
#define rs (p<<1|1)
using namespace std;
const int M=5e5+10,inf=0x3f3f3f3f;
int mx[M<<2],tag[M<<2],n,q,x;
void push_up(int p){
	mx[p]=max(mx[ls],mx[rs]);
}
void push_down(int p){
	if(!tag[p])return;
	tag[ls]+=tag[p],mx[ls]+=tag[p];
	tag[rs]+=tag[p],mx[rs]+=tag[p];
	tag[p]=0;return;
}
void add(int p,int l,int r,int L,int R){
	if(L<=l&&r<=R){
		tag[p]++,mx[p]++;
	}else{
		push_down(p);int mid=(l+r)>>1;
		if(L<=mid)add(ls,l,mid,L,R);
		if(R>mid)add(rs,mid+1,r,L,R);
		push_up(p);
	}
}
int ask(int p,int l,int r,int x){
	if(l==r)return l;
	push_down(p);int mid=(l+r)>>1,ans;
	if(mx[ls]>=x)ans=ask(ls,l,mid,x);
	else if(mx[rs]>=x)ans=ask(rs,mid+1,r,x);
	push_up(p);return ans; 
}
void build(int p,int l,int r){
	mx[p]=r;if(l==r)return;
	int mid=(l+r)>>1;
	build(ls,l,mid),build(rs,mid+1,r);
	push_up(p);return;
}
int qry(int p,int l,int r,int x){
	if(l==r)return mx[p];
	push_down(p);int mid=(l+r)>>1;
	if(x<=mid)return qry(ls,l,mid,x);
	if(x>mid)return qry(rs,mid+1,r,x);
}
int main(){
	scanf("%d",&n);
	build(1,1,500001);//因为查询r+1可能超过 500000,所以要加1,因为这一点场上被创思了
	for(int i=1;i<=n;i++){
		int l,r;
		scanf("%d %d",&l,&r);
		int L=ask(1,1,500001,l);
		int R=ask(1,1,500001,r+1)-1;
		if(L<=R)add(1,1,500001,L,R);
	}
	scanf("%d",&q);
	for(int i=1;i<=q;i++){
		int x;
		scanf("%d",&x);
		printf("%d\n",qry(1,1,500001,x));
	}
	return 0;
}

时间复杂度 O(nlogn)

G - Odd Even Graph

看 都 没 看。

还是太菜了呜呜呜。

posted @   zuoqingyuan111  阅读(34)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示