来源

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)

\[dp[i]=(n-2)dp[i-1]+(n-1)dp[i-2] \]

使用线性代数的知识可以用特征根法求出通项公式,具体步骤如下。

2021航电多校 第一场_#define
最终可得通向公式为

\[f(t)=\frac{{(-1)}^{t-1}\cdot(n-1)+(n-1)^{t+1}}{n} \]

分奇偶讨论,化为形如\(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;
    }    
}