ABC389

C

link

手写队列存下开始位置和长度,模拟即可。

点击查看代码
#include<bits/stdc++.h>

#define int long long

using namespace std;

int q;
struct nd{
	int st,len;
}a[300005];
int h = 1,t;
int shao;

signed main(){
	
	cin >> q;
	while(q--){
		int tp,x;
		cin >> tp;
		if(tp == 1){
			cin >> x;
			t++;
			a[t] = {a[t-1].st+a[t-1].len,x};
		}
		else if(tp == 2){
			shao += a[h].len;
			h++;
		}
		else if(tp == 3){
			cin >> x;
			cout << a[h+x-1].st-shao << endl;
		}
	}
	
	return 0;
	
}

D

link


没写原点和x轴y轴,扣分!
这是一个半径为\(3\)的圆。
先看这个图,我们可以发现一个圆被两条坐标轴分成了四份,显然,四份都是一样的。
那么我们先计算坐标轴上的正方形。我们可以看做原点把两条坐标轴分为了四段,那么一段上就有\(R-1\)个正方形,那么四段再加上原点就是\(4(R-1)+1\)
那么既然四半都一样,那我们就只考虑第一象限的那一半。
考虑一个正方形\((i,j)\),如果它在圆内,当且仅当\((i+0.5,j+0.5)\)这个点在圆里,也就是说\(\sqrt{(i+0.5)^2+(j+0.5)^2} \leq R\),即\((i+0.5)^2+(j+0.5)^2 \leq R^2\),两边同乘以\(4\),就是\((2i+1)^2+(2j+1)^2 \leq 4R^2\),那么我们枚举\(i\),二分查找对应的最大的\(j\),那么当\(x=i\)时,\(y=1……j\)都可以满足条件(在圆内),就都可以加到答案里(答案加上\(j\)),都做完再变成四倍即可。

点击查看代码
#include<bits/stdc++.h>

#define int long long

using namespace std;

int R;
int ans;

int suan(int i,int j){
	return (2*i+1)*(2*i+1)+(2*j+1)*(2*j+1);
}

int finj(int x){
	int l = 0,r = R;
	while(l < r){
		int mid = (l+r+1)/2;
		if(suan(x,mid) <= 4*R*R) l = mid;
		else r = mid-1;
	}
	return l;
}

signed main(){
	
	cin >> R;
	
	for(int i = 1;i < R;++ i){
		int j = finj(i);
		ans += j;
	}
	ans *= 4;
	ans += 4*(R-1)+1;
	cout << ans;
	
	return 0;
	
}

E

link

首先我们考虑一个事情:我们买一个\(i\)东西是\(p_i\)元,买两个是\(4p_i\)元,相当于第二个用了\(3p_i\)元。
那么我们可以把每个物品拆开,拆成很多个,然后从小到大一个一个选。

点击查看代码
#include<bits/stdc++.h>

#define int unsigned long long

using namespace std;

int n,m;
int p[200005];

bool check(int x){
	int ans = 0;
	for(int i = 1;i <= n;++ i){
		int w = (x/p[i]+1)/2;
		if(w == 0) continue;
		int ww = w;
		if(ww > m) return false;
		if(ww > m/w) return false;
		ww *= w;
		if(ww > m/p[i]) return false;
		ww *= p[i];
		if(ans > m-ww) return false;
		ans += ww;
	}
	return true;
}

signed main(){
	
	cin >> n >> m;
	for(int i = 1;i <= n;++ i)
		cin >> p[i];
	
	int l = 0,r = m,mid;
	while(l < r){
		mid = (l+r+1)/2;
		if(check(mid)) l = mid;
		else r = mid-1;
	}
	
	int ans = 0,sum = 0;
	for(int i = 1;i <= n;++ i){
		int w = (l/p[i]+1)/2;
		ans += w;
		sum += w*w*p[i];
	}
	cout << ans+(m-sum)/(l+1);
	
	return 0;
	
}

F

link

首先我们考虑一个事情:初始是\(x\)的最终结果一定不会比初始是\(x+1\)的大,因为如果大的话,一定会在某个位置相等,如果相等了,后面就都一样了,一定会小于等于。这个现在用不到,待会会用。
我们考虑\(f_{i,j}\)表示已经进行到了第\(i\)局,初始分数是\(j\)最终分数。
我们可以很容易的写出转移方程:\(f_{i,j} = f_{i-1,j}\)(\(f_{i-1,j}\)不属于\(L_i\)\(R_i\)之间),\(f_{i,j} = f_{i-1,j}+1\)(\(f_{i-1,j}\)属于\(L_i\)\(R_i\)之间)。
那么我们现在只需要找到\(f_{i-1,j}\)属于\(L_i\)\(R_i\)之间那些位置,由于我们推出来的第一条性质,我们可以知道,\(f_i\)是单调不降的,那么数值在\(L_i\)\(R_i\)之间一定是一个区间,那么我们就可以二分查找这个区间,再用树状数组把这个区间加一即可。
这里我们的\(dp\)数组是一个二维的,但是由于可以直接在原来的基础上加\(1\),所以我们用一维即可。

点击查看代码
#include<bits/stdc++.h>

using namespace std;

const int m = 5e5;

int lb(int x){return x&(-x);}

int n;
int l[200005],r[200005];
int ans[500005];

void add(int d,int x){
	for(int i = d;i <= m;i += lb(i))
		ans[i] += x;
}

int sum(int x){
	int res = 0;
	for(int i = x;i >= 1;i -= lb(i))
		res += ans[i];
	return res;
}

int findl(int x){
	int l = 1,r = m,mid;
	while(l < r){
		mid = (l+r)/2;
		if(sum(mid) >= x) r = mid;
		else l = mid+1;
	}
	return r;
}

int findr(int x){
	int l = 1,r = m,mid;
	while(l < r){
		mid = (l+r+1)/2;
		if(sum(mid) <= x) l = mid;
		else r = mid-1;
	}
	return l;
}

void init(){
	for(int i = 1;i <= m;++ i) add(i,1);
}

signed main(){
	
	init();
	
	cin >> n;
	for(int i = 1;i <= n;++ i){
		cin >> l[i] >> r[i];
		int wl = findl(l[i]);
		int wr = findr(r[i]);
		add(wl,1);
		add(wr+1,-1);
	}
	
	int q;
	cin >> q;
	while(q--){
		int x;
		cin >> x;
		cout << sum(x) << endl;
	}
	
	return 0;
	
}
posted @ 2025-01-22 20:34  我的晴语表  阅读(40)  评论(0)    收藏  举报