F- Xor sum (字典树)
我明白了,以后遇到异或就想字典树!
先求异或前缀和,然后从左到右遍历区间右端点,用字典树维护答案,求字典树中与当前右端点最近的、异或和大于k的那个位置!
假设当前右端点为i,i之前的值都插入了字典树,字典树每个结点存储最大的下标。在查询时,访问logk个结点就能得到答案,这是由于每一步都至多只有一个选择,共logk层。
详见代码实现
#include <bits/stdc++.h>
#define endl '\n'
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define mp make_pair
#define seteps(N) fixed << setprecision(N)
typedef long long ll;
using namespace std;
/*-----------------------------------------------------------------*/
ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
#define INF 0x3f3f3f3f
const int N = 1e7 + 10;
const double eps = 1e-5;
int tr[N][2];
int pos[N];
int si;
void init() {
tr[0][0] = tr[0][1] = 0;
for(int i = 0; i <= si; i++) {
pos[i] = 0;
}
si = 0;
}
int arr[N];
int n, k;
int mx;
void insert(int x, int p) {
int cur = 0;
for(int i = mx; i >= 0; i--) {
bool d =(x & (1 << i));
if(!tr[cur][d]) {
tr[cur][d] = ++si;
cur = si;
pos[cur] = p;
tr[cur][0] = tr[cur][1] = 0;
} else {
cur = tr[cur][d];
pos[cur] = p;
}
}
}
int get(int x) {
int cur = 0;
int mxp = -1;
int i;
for(i = mx; i >= 0; i--) {
bool d1 = (x & (1 << i)), d2 = (k & (1 << i));
int nt;
if(d1 == 0 && d2 == 0) {
nt = tr[cur][1];
if(nt) mxp = max(mxp, pos[nt]);
cur = tr[cur][0];
if(!cur) break;
} else if(d1 == 1 && d2 == 0) {
nt = tr[cur][0];
if(nt) mxp = max(mxp, pos[nt]);
cur = tr[cur][1];
if(!cur) break;
} else if(d1 == 0 && d2 == 1) {
cur = tr[cur][1];
if(!cur) break;
} else {
cur = tr[cur][0];
if(!cur) break;
}
}
if(i < 0) mxp = max(mxp, pos[cur]);
return mxp;
}
int main() {
IOS;
int t;
cin >> t;
while(t--) {
init();
cin >> n >> k;
int mxv = 0;
for(int i = 1; i <= n; i++) {
cin >> arr[i];
arr[i] = arr[i - 1] ^ arr[i];
mxv = max(mxv, arr[i]);
}
mx = 0;
do {
mx++;
mxv >>= 1;
} while(mxv);
insert(0, 0);
int len = INF;
int l, r;
for(int i = 1; i <= n; i++) {
int res = get(arr[i]);
if(res >= 0) {
if(i - res < len) {
len = i - res;
l = res + 1, r = i;
}
}
insert(arr[i], i);
}
if(len < INF)
cout << l << " " << r << endl;
else
cout << "-1" << endl;
}
}
G - Pass!(递推式求通项(特征根方法),离散对数)
由题容易得到dp方程(dp[0]=0, dp[1]=n-1)
使用线性代数的知识可以用特征根法求出通项公式,具体步骤如下。
最终可得通向公式为
分奇偶讨论,化为形如\(a^x\equiv b\)的形式,就可以用BSGS解决了,复杂度为\(O(\sqrt(P))\)。
注意要用unordered_map,不要有太多重复计算,不然会T。
#include <bits/stdc++.h>
#define endl '\n'
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define mp make_pair
#define seteps(N) fixed << setprecision(N)
typedef long long ll;
using namespace std;
/*-----------------------------------------------------------------*/
ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
#define INF 0x3f3f3f3f
const int N = 1e5 + 10;
const int M = 998244353;
const double eps = 1e-5;
unordered_map<int, int> vals;
int si;
vector<int> Bval[N];
inline ll qpow(ll a, ll b, ll m) {
ll res = 1;
while(b) {
if(b & 1) res = (res * a) % m;
a = (a * a) % m;
b = b >> 1;
}
return res;
}
ll dp[N];
int mx = 0;
int solve(int a, int b, bool odd) {
ll ra = qpow(a, M - 2, M);
ll val = b * qpow(a, mx, M) % M;
int res = INF;
for(int B = mx; B >= 0; B--) {
if(vals.count(val)) {
auto& tar = Bval[vals[val]];
for(auto &A : tar) {
if(((A * mx - B) & 1) == odd && A * mx - B < res) {
res = A * mx - B;
break;
}
}
}
val = val * ra % M;
}
return res;
}
int main() {
IOS;
// freopen("in.txt","r",stdin);
int t;
cin >> t;
while(1ll * mx * mx < M) mx++;
while(t--) {
int n, x;
cin >> n >> x;
if(x == 0) cout << 1 << endl;
else if(x == 1) cout << 0 << endl;
else if(x == n - 1) cout << 2 << endl;
else {
vals.clear();
for(int i = 1; i <= si; i++) Bval[i].clear();
si = 0;
ll val = 1;
ll mxp = qpow(n - 1, mx, M);
for(int i = 0; i <= mx; i++) {
if(vals.count(val))
Bval[vals[val]].push_back(i);
else {
Bval[++si].push_back(i);
vals[val] = si;
}
val = val * mxp % M;
}
int ans1 = solve(n - 1, (1ll * n * x - n + 1) % M, 0);
int ans2 = solve(n - 1, (1ll * n * x + n - 1) % M, 1);
int res = min(ans1, ans2);
if(res == INF) cout << -1 << endl;
else cout << res << endl;
}
}
}
I - KD-Graph
Update:二分方法很可能会T,之前能过实在是运气太好
正解是先对边按边权从小到大排序,然后每个阶段用并查集合并边权相同的边,只要某个阶段结束后连通块数为k,答案就是对应的边权;否则无解。
#include <bits/stdc++.h>
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define INF 0x3f3f3f3f
#define str first
#define blu second
using namespace std;
const int M = 1e6 + 10;
const int N = 1e5 + 10;
typedef long long ll;
typedef pair<int, int> PII;
struct edge {
int u, v, w;
};
bool cmp(const edge& a, const edge &b) {
return a.w < b.w;
}
edge ed[M];
int fa[N];
int find(int x) {
if(fa[x] == x) return x;
return fa[x] = find(fa[x]);
}
void init(int n) {
for(int i = 1; i <= n; i++) {
fa[i] = i;
}
}
int main() {
IOS;
int t;
cin >> t;
while(t--) {
int n, m, k;
cin >> n >> m >> k;
init(n);
for(int i = 1; i <= m; i++) {
int u, v, w;
cin >> u >> v >> w;
ed[i] = edge{u, v, w};
}
sort(ed + 1, ed + 1 + m, cmp);
int cnt = n;
if(n == k) {
cout << 0 << endl;
continue;
}
int D;
for(int i = 1; i <= m; i++) {
int u = ed[i].u, v = ed[i].v;
int fu = find(u), fv = find(v);
if(fu != fv) {
cnt--;
fa[fu] = fv;
}
if(i + 1 > m || ed[i].w != ed[i + 1].w) {
if(cnt <= k) {
D = ed[i].w;
break;
}
}
}
if(cnt == k) cout << D << endl;
else cout << -1 << endl;
}
}
/*
*/
二分D,将权重大于D的边删除,然后求连通块数量。连通块即满足条件的分组。D越大,分组数越少,满足单调性,可以二分。
#include <bits/stdc++.h>
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define INF 0x3f3f3f3f
#define str first
#define blu second
using namespace std;
#define endl '\n'
const int N = 1e6 + 10;
typedef long long ll;
typedef pair<int, int> PII;
vector<PII> np[N];
int tag = 0;
int vis[N];
int D;
void dfs(int p) {
vis[p] = tag;
for(auto nt : np[p]) {
if(vis[nt.first] != tag && nt.second <= D) {
dfs(nt.first);
}
}
}
int main() {
IOS;
int t;
cin >> t;
while(t--) {
int n, m, k;
cin >> n >> m >> k;
for(int i = 1; i <= n; i++) np[i].clear();
int l = 0, r = 0;
for(int i = 1; i <= m; i++) {
int u, v, w;
cin >> u >> v >> w;
np[u].push_back({v, w});
np[v].push_back({u, w});
r = max(w + 1, r);
}
while(l <= r) {
D = (l + r) / 2;
tag++;
int cnt = 0;
for(int i = 1; i <= n; i++) {
if(vis[i] != tag) {
cnt++;
dfs(i);
}
}
if(cnt <= k) {
r = D - 1;
} else {
l = D + 1;
}
}
D = l;
tag++;
int cnt = 0;
for(int i = 1; i <= n; i++) {
if(vis[i] != tag) {
cnt++;
dfs(i);
}
}
if(cnt == k) cout << D << endl;
else cout << -1 << endl;
}
}