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;
}

浙公网安备 33010602011771号