B
题目大意
给一张点数为n,边数为m的图,多次询问k个点的子图,求其最小生成树权值
题解
可以发现,对求子图影响最大的就是子图的边数,如果采用朴素的暴力,考虑到存在一个点,其连接的边数比 1 e 5 1e5 1e5少一点,如果多次询问这个点,再加上任意点来保证询问不相同,会导致其时间复杂度近似 O ( q 1 e 5 l o g 1 e 5 ) O(q1e5log{1e5}) O(q1e5log1e5),其中 q q q可以接近于 1 e 5 1e5 1e5,这个时间复杂度不可以接受。
考虑分治,可以发现,点的个数和询问次数是息息相关的,而对于一张完全图,其边的个数最多为 n ∗ ( n − 1 ) n*(n-1) n∗(n−1),其中 n n n是这张图的点数,那么如果存在一个点连接了特别多的边,我们可以考虑删除其无用的边
不妨采用 s q r t ( n ) sqrt(n) sqrt(n)作为点分界线,那么对于小于 s q r t ( n ) sqrt(n) sqrt(n)的询问,我们可以暴力地求出其每对点之间是否存在边,但需要注意的是,不可以直接遍历邻接表,这样时间复杂度仍是爆炸,应采用其他方法,我是采用了二分来查找两点之间是否存在边,在这一步骤上应该不是正解,此时时间复杂度为 O ( k 2 ∗ ( ? l o g 1 e 5 ) + k ∗ l o g k 2 ) O(k^2*(?log1e5) + k*logk^2) O(k2∗(?log1e5)+k∗logk2)。
对于大于 s q r t ( n ) sqrt(n) sqrt(n)的询问,此时可以发现,其最大的询问应该为 s q r t ( n ) sqrt(n) sqrt(n)次,此时我们可以直接暴力地遍历邻接表去查找询问的点之间连接的边,此时就算存在一个特别大的边数,时间复杂度也不会爆炸,此时时间复杂度极限为 O ( s q r t ( n ) 1 e 5 l o g 1 e 5 ) O(sqrt(n)1e5log1e5) O(sqrt(n)1e5log1e5)
可以发现,在上面的时间复杂度还是比较大的,基本有1e9,虽然比较难接近这个极限。
代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int INF = 1e18;
const int mod = 998244353;
const int N = 1e5+5;
struct node{
int x,y,z;
node (int _x,int _y,int _z){
x=_x;y=_y;z=_z;
}
bool operator<(const node m)const{
return y<m.y;
}
};
vector<vector<node>> G(N);
vector<vector<int>> H(N);
int fa[N];
int n,m;
bool cmp(node a,node b){
return a.z<b.z;
}
int find(int x){
return x== fa[x] ? x : fa[x]=find(fa[x]);
}
int kruskal(vector<int> nodes,vector<bool> is,vector<node> edges){
int sum=0;
for(auto i : edges){
if(!is[i.x] || !is[i.y]) continue;
int x = find(i.x);
int y = find(i.y);
if(x == y) continue;
fa[y] = x;
sum += i.z;
}
int ans=0;
for(auto i : nodes){
if(i== fa[i]){
ans++;
}
}
if(ans==2) return sum;
else return -1;
}
void solve(){
int q;
cin >> n >> m >> q;
for(int i=1;i<=m;i++){
int x,y,z;
cin >> x >> y >> z;
G[x].push_back(node(x,y,z));
G[y].push_back(node(y,x,z));
}
for(int i = 1;i<=n;i++){
sort(G[i].begin(),G[i].end());
}
for(int i=1;i<=q;i++){
int k;
cin >> k;
vector<int> a(k+1);
vector<node> edges;
vector<bool> isAppear(n+1,false);
for(int j=1;j<=k;j++){
cin >> a[j];
isAppear[a[j]]=true;
fa[a[j]]=a[j];
}
if (k < sqrt(n)){
for (int j = 1;j<=k;j++){
for(int o = j + 1 ;o<=k;o++) {
int tt = upper_bound(G[a[j]].begin(),G[a[j]].end(),node(a[j],a[o],0)) - G[a[j]].begin();
if (tt-1 >= 0 && G[a[j]][tt-1].y == a[o]) {
edges.push_back(G[a[j]][tt-1]);
}
}
}
}else{
for (int j = 1;j<=k;j++){
for(auto t : G[a[j]]){
if (isAppear[t.y]){
edges.push_back(t);
}
}
}
}
sort(edges.begin(),edges.end(),cmp);
int ans = kruskal(a,isAppear,edges);
cout << ans << '\n';
}
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
t=1;
while(t--){
solve();
}
}
/*
5 10 4
1 2 1
1 3 1
1 4 1
1 5 1
2 3 2
2 4 2
2 5 2
3 4 3
3 5 3
4 5 4
4 2 3 4 5
3 3 4 5
2 4 5
1 5
*/
C
题目大意
有一个大小为 2 ∗ n 2 * n 2∗n的矩阵,由 R R R和 W W W组成,可以选择任意R作为起点,可以上下左右移动,不可移动到 W W W,移动后原位置变为 W W W不可移动,询问最多可以移动的次数。
题解
直接递推就行,每个位置可以移动的值都由前面的位置转移得到,然后取最大值即可,可以直接看代码
代码
n = int(input())
a = ['W' + input(), 'W' + input()]
b = [[0] * (n + 1), [0] * (n + 1)]
ans = 0
for i in range(1, n + 1):
if a[0][i] == 'R':
b[0][i] = b[0][i - 1] + 1
if a[1][i] == 'R':
b[1][i] = b[1][i - 1] + 1
if a[0][i] == a[1][i] == 'R':
b[0][i], b[1][i] = max(b[0][i], b[1][i] + 1), max(b[1][i], b[0][i] + 1)
ans = max(ans, b[0][i], b[1][i])
ans = max(0, ans-1)
print(ans)
E
题目大意
找到一个严格小于 x x x的整数 y y y,使得 g c d ( x , y ) = x ⨁ y gcd(x,y) = x \bigoplus y gcd(x,y)=x⨁y
题解
一般来说,应该先打表,然后就没有然后了
考虑异或的定义,显然,异或后的值是必须要小于 x x x的(来自 g c d gcd gcd),可以发现,如果去凑一个y值满足其是 x x x的一个因子,这非常艰难,但显然这答案是很多的,考虑异或的性质 x ⨁ y = t ⇔ x ⨁ t = y x \bigoplus y = t \Leftrightarrow x \bigoplus t = y x⨁y=t⇔x⨁t=y,此时我们可以构造一个 x x x的因子 t t t,使其满足 t ⨁ x = y t \bigoplus x = y t⨁x=y,那么就找到了所要求得的 y y y,此时应取 x x x的最小因子,也就是 x x x的二进制最后一个1之后的部分,判断合法即可。
代码
import math
t = int(input())
for _ in range(t):
x = int(input())
a = bin(x)
b = ''
for i in a[::-1]:
b = i + b
if i == '1':
break
ans = int(b, 2) ^ x
print(ans if 0 < ans < x else -1)
H
题目大意
给一个由方向组成的序列,取出其方向子串,从(0,0)位置出发,按照方向子串的指示移动,求在移动过程中经过题目要求点的子串个数
题解
考虑选择的子串恰好能到达需要到达的位置,那么在这子串之后的位置均能被选择
如何求出这个子串,考虑后缀和,两个后缀和相减的结果就是这个子串到达的点的镜像,即需要取负,记录后缀和到达的位置的贡献,每次更新后缀和时就统计一次答案即可。
代码
# from collections import defaultdict
n, x, y = map(int, input().split())
a = input()
d = {(0, 0): n-1}
xk = yk = 0
ans = 0
if x == y == 0:
print((n+1)*n//2)
else:
for i in range(n - 1, -1, -1):
if a[i] == 'W':
yk += 1
if a[i] == 'A':
xk -= 1
if a[i] == 'S':
yk -= 1
if a[i] == 'D':
xk += 1
d[(xk, yk)] = i - 1
fx, fy = xk - x, yk - y
k = d.get((fx, fy), n)
ans += n - k
# print(i,k,ans)
print(ans)