ABC365
A
link
题目已经说的很明白了,判断即可。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
int y;
signed main(){
cin >> y;
if(y%4 != 0) cout << 365;
else if(y%4 == 0&&y%100 != 0) cout << 366;
else if(y%100 == 0&&y%400 != 0) cout << 365;
else if(y%400 == 0) cout << 366;
return 0;
}
B
link
emmm,排序找哪个位置是第二大。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
int a[105],b[105];
signed main(){
int n;
cin >> n;
for(int i = 1;i <= n;++ i)
cin >> a[i],b[i] = a[i];
sort(a+1,a+1+n);
for(int i = 1;i <= n;++ i)
if(b[i] == a[n-1]) cout << i;
return 0;
}
C
link
首先判断一下,如果\(A_i\)的总和还不到\(m\),补贴就可以无限大,因为和\(A_i\)取\(min\),最后不会超过\(A_i\)的和。
剩下的二分答案。对于一个答案\(x\),算出所需付的补贴金额,判断是否\(>m\),如果在\(m\)范围内,可以让\(x\)变大,否则就要变小。
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m;
int a[200005];
int sum;
int check(int x){
int res = 0;
for(int i = 1;i <= n;++ i)
res += min(x,a[i]);
return res;
}
signed main(){
cin >> n >> m;
for(int i = 1;i <= n;++ i)
cin >> a[i],sum += a[i];
if(sum <= m){
cout << "infinite";
return 0;
}
int l = 0,r = sum,md;
while(l<r){
md = (l+r+1)/2;
int t = check(md);
if(t > m) r = md-1;
else l = md;
}
cout << l;
return 0;
}
D
link
首先明确一下题意,他说高桥没输过,代表他赢了或平了,在他每次都和前一次出的不一样的情况下,找到赢了的最大数。
考虑\(DP\),设\(f_{i,0/1}\)中\(f_{i,1}\)代表到第\(i\)局中第\(i\)局赢了的赢的最大局数,\(f_{i,0}\)代表到第\(i\)局中第\(i\)局平了的赢的最大局数。
考虑转移。每一个状态都可以从\(i-1\)的所有状态(\(0/1\))转移过来,只要判断这个状态高桥该出什么和前一个的哪一个状态高桥该出什么不一样即可。
if(这一个赢的状态!=上一个赢/平的状态)
f[i][1] = max(f[i][1],f[i-1][1/0]+1);
大概这样即可。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
int n;
char s[200005];
char t[200005][2];
int f[200005][2];
char ying(char x){
if(x == 'R') return 'P';
else if(x == 'P') return 'S';
else return 'R';
}
signed main(){
cin >> n >> s+1;
for(int i = 1;i <= n;++ i){
t[i][1] = ying(s[i]);
t[i][0] = s[i];
}
int ans = 0,q = 0;
for(int i = 1;i <= n;++ i){
if(t[i-1][0] != t[i][1])
f[i][1] = max(f[i][1],f[i-1][0]+1);
if(t[i-1][1] != t[i][0])
f[i][0] = max(f[i][0],f[i-1][1]);
if(t[i-1][0] != t[i][0])
f[i][0] = max(f[i][0],f[i-1][0]);
if(t[i-1][1] != t[i][1])
f[i][1] = max(f[i][1],f[i-1][1]+1);
}
cout << max(f[n][1],f[n][0]);
return 0;
}
E
link
这时一个比较有意思的题目。
首先明确一个概念,这个题可以一位一位做\(\sum_{i=1}^{N-1}\sum_{j=i+1}^N (A_i \oplus A_{i+1}\oplus \ldots \oplus A_j)\),最后再放到那一位上(左移几个),把每一位加起来。证明一下。
设有三个数\(x_1+x_2<<1+x_3<<2\),\(y_1+y_2<<1+y_3<<2\),\(z_1+z_2<<1+z_3<<2\)。
要算第\(1\)个和第\(2\)个的异或值与第\(2\)个和第\(3\)个的异或值的和。那么如果按位算就是
\((x_1\oplus y_1+y_1\oplus z_1)+(x_2\oplus y_2+y_2\oplus z_2)<<1+(x_3\oplus y_3+y_3\oplus z_3)<<2\)
\(=x_1\oplus y_1+y_1\oplus z_1+x_2\oplus y_2<<1+y_2\oplus z_2<<1+x_3\oplus y_3<<2+y_3\oplus z_3<<2\)
\(=(x_1\oplus y_1+x_2\oplus y_2<<1+x_3\oplus y_3<<2)+(y_1\oplus z_1+y_2\oplus z_2<<1+y_3\oplus z_3<<2)\)
\(=(x_1+x_2<<1+x_3<<2)\oplus (y_1+y_2<<1+y_3<<2)+(y_1+y_2<<1+y_3<<2)\oplus (z_1+z_2<<1+z_3<<2)\)
前几步很好理解,最后一步呢?按位异或,按位,所以每一位每一位算就是。
那么考虑每一位(都是\(0\)或\(1\))的答案。
先考虑一个问题,如果一些\(0\)或\(1\),如果有奇数个\(1\),最后的结果就是\(1\),如果有偶数个\(1\),最后结果就是\(0\)。因为两个相同的\(1\)异或起来是\(0\),奇数还有一个配不上对,剩一个\(1\),而偶数就不剩了。
那么对于一位(上文的每一位)中的所有区间(\(1\) ~ \(2\),\(1\) ~ \(3\ldots1\) ~ \(n\),\(2\) ~ \(3\),\(2\) ~ \(4\ldots2\) ~ \(n\)……\(n-1\) ~ \(n\)),如果有奇数个\(1\)异或起来就是\(1\),加到这一位的答案中就有效,否则无效。
这时我们发现,我们并不关心\(1\)的个数,只关心\(1\)的个数的奇偶性。
我们把每一个数的当前位取出来,并算出前\(i\)位中\(1\)的个数的奇偶性,为\(s_i\)。那么我们考虑什么情况一个区间\(l+1\)到\(r\)的\(1\)的个数有奇数个,如果前\(r\)位有奇数个\(1\),那么前\(l\)位就得有偶数个\(1\);如果前\(r\)位偶数个,那么前\(l\)位就有奇数个,那么前\(r\)位有\(s_r\)个,前\(l\)位就得有\(s_r\oplus 1\)个。这样的区间是有效的,那么我们就要找有效的区间个数。所以对于一个结尾\(i\),就要找\(i-1\)之前的\(j\)中\(s_j=s_i\oplus 1\)的个数。为什么是\(i-1\)?刚刚有个小细节区间\(l+1\)到\(r\),且我们要让\(l+1 < r\)(因为题目说最少加\(2\)个的异或值,不加一个的),所以\(l < r-1\),所以是\(i-1\)之前的。那么怎么找呢?用两个\(cnt\)(可以开数组),分别记录前面为奇数和为偶数的个数,在每次的\(i\)前的\(j\)中\(s_j=s_i\oplus 1\)(两个\(cnt\)中代表\(s_i\oplus 1\)的奇偶性)的个数中加入答案后,把\(s_{i-1}\)的奇偶性计入,因为在\(i\)时要算\(<i-1\)的。
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n;
int a[200005];
int cnt[2];
int s[200005];
int calc(int x){
for(int i = 1;i <= n;++ i) s[i] = (a[i]>>x)&1;
for(int i = 1;i <= n;++ i) s[i] ^= s[i-1];
cnt[0] = cnt[1] = 0;
int ans = 0;
for(int i = 1;i <= n;++ i)
ans += cnt[s[i]^1],cnt[s[i-1]]++;
return ans;
}
signed main(){
cin >> n;
for(int i = 1;i <= n;++ i)
cin >> a[i];
int ans = 0;
for(int i = 0;i <= 30;++ i){
ans += calc(i)<<i;
}
cout << ans;
return 0;
}
F
link
线段树的奇妙用法。
我们考虑一种走法,我们一直往右走,直到右边是墙了,再往上/下走。可以想象一下正确性(类似贪心的感觉)。
那么我们走的时候有两种情况,一种是贯穿,就是一直走,不拐弯;另一种就是被迫走,就是被迫拐弯了(当然拐弯的时候也要能向右就向右),注意看下图,我们可以发现,被迫走一定是贴着边线在走。
那么我们可以用三元组\({l,r,c}\)来分别表示两种情况:贯穿时,\(l\)和\(r\)指的是一段区间在\(l\)~\(r\)这个范围内可以一直贯穿,不用拐弯,\(c=-1\),代表这个是贯穿;被迫走时,\(l\)和\(r\)指的是从\(l\)进入,从\(r\)出去,\(c\)指的是竖着走的数量(注意不是\(r-l\),因为如果拐了好几拐,那么每个拐竖着走的都需要加上)。
那么一段区间指的什么呢?指的是某些列,比如样例有图的那个,\(1\) ~ \(2\)列就是用一个\({3,3,-1}\)的三元组来表示;\(2\) ~ \(4\)列就是用一个\({3,5,2}\)的三元组来表示(这里我们把纵坐标改一下,改成从上到下\(1\)~什么什么)。
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n;
int a[200005],b[200005];
struct nd{
int l,r,c;
}tr[800005];
nd pushup(nd x,nd y){
nd ans;
if(x.c == -1){
if(y.c == -1){//贯穿 贯穿
if(max(x.l,y.l) <= min(x.r,y.r))
ans = {max(x.l,y.l),min(x.r,y.r),-1};
else if(x.l > y.r)
ans = {x.l,y.r,x.l-y.r};
else if(x.r < y.l)
ans = {x.r,y.l,y.l-x.r};
}
else{//贯穿 被迫走
if(x.l <= y.l&&y.l <= x.r)
ans = y;
else if(x.l > y.l)
ans = {x.l,y.r,y.c+x.l-y.l};
else if(x.r < y.l)
ans = {x.r,y.r,y.c+y.l-x.r};
}
}
else{
if(y.c == -1){//被迫走 贯穿
if(y.l <= x.r&&x.r <= y.r)
ans = x;
else if(x.r < y.l)
ans = {x.l,y.l,x.c+y.l-x.r};
else if(x.r > y.r)
ans = {x.l,y.r,x.c+x.r-y.r};
}
else{//被迫走 被迫走
ans = {x.l,y.r,x.c+y.c+abs(y.l-x.r)};
}
}
return ans;
}
void build(int x,int l,int r){
if(l == r){
tr[x] = {a[l],b[l],-1};
return;
}
int mid = (l+r)/2;
build(x*2,l,mid);build(x*2+1,mid+1,r);
tr[x] = pushup(tr[x*2],tr[x*2+1]);
}
nd query(int x,int l,int r,int xl,int xr){
if(xl <= l&&r <= xr) return tr[x];
int mid = (l+r)/2;
if(xr <= mid) return query(x*2,l,mid,xl,xr);
if(xl > mid) return query(x*2+1,mid+1,r,xl,xr);
return pushup(query(x*2,l,mid,xl,xr),
query(x*2+1,mid+1,r,xl,xr));
}
signed main(){
cin >> n;
for(int i = 1;i <= n;++ i)
cin >> a[i] >> b[i];
build(1,1,n);
/*for(int i = 1;i <= 20;++ i){
cout << i << " ";
cout << tr[i].l << " " << tr[i].r << " ";
cout << tr[i].c << endl;
}*/
int q;
cin >> q;
while(q--){
int sx,sy,tx,ty;
cin >> sx >> sy >> tx >> ty;
if(sx > tx) swap(sx,tx),swap(sy,ty);
if(sx == tx){
cout << abs(sy-ty) << endl;
continue;
}
nd a = query(1,1,n,sx,tx);
a = pushup({sy,sy,-1},a);
a = pushup(a,{ty,ty,-1});
cout << a.c+tx-sx << endl;
}
return 0;
}