0
点赞
收藏
分享

微信扫一扫

CSP-S 模拟 19/10/07

​​梦回幼儿园​​​ 考虑到出现偶数次的异或出来为 0
我们可以先将原序列异或一遍,剩下的就是两个数的异或和
注意到如果有一位为1,那么两个数一个这一位为1,一个为0
我们将这一位为1 的全部异或起来,最后剩下的就是第一个数
然后将这个数与我们第一次得到的结果异或即可
ps:换了 CSP-S 模拟 19/10/07_子树 的读优才过…

#include<bits/stdc++.h>
#define N 10000050
using namespace std;
namespace IO{
inline char gc(){
static const int Rlen=1<<22|1;
static char buf[Rlen],*p1,*p2;
return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
}

template<typename T>
inline T get(){
char c;T num;
while(!isdigit(c=gc()));num=c^48;
while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
return num;
}
inline int read(){return get<int>();}
}
using namespace IO;
int n, a[N], x;
int main(){
n = read();
for(int i = 1; i <= n; i++) a[i] = read(), x ^= a[i];
int p = 0; for(;p < 31; p++) if((1 << p) & x) break; p = 1 << p;
int A = 0, B = 0;
for(int i = 1; i <= n; i++) if(a[i] & p) A ^= a[i];
x ^= A; B = x; if(A > B) swap(A, B); cout << A << " " << B; return 0;
}

​​树的问题​​​ 题意:
给定一棵树,在树上加一条边,问有多少种加边方案,使树上任意两点间距离 CSP-S 模拟 19/10/07_git_02,加的边权值为0
首先暴力是枚举每一条边,然后在基环树上 CSP-S 模拟 19/10/07_git_03 找直径
我们考虑枚举一个端点,然后以它为根 CSP-S 模拟 19/10/07_git_04,这样一条路径就是根到子树的一条路径
考虑不经过传送门,和经过传送门的情况
不经过传送门:从路径的一个点的一个子树中到另一个子树中,或直接在与路径无关的一个子树中走
为了支持这个判断,我们需要维护直径,最长链,次长链,和第三长链
经过传送门:假设当前CSP-S 模拟 19/10/07_git_04 到了CSP-S 模拟 19/10/07_i++_06, 有一个传送门在深度为 CSP-S 模拟 19/10/07_git_07 的地方
CSP-S 模拟 19/10/07_git_08 为 u 往下走且不经过传送门的最长链
那么需要满足,对于任意祖先CSP-S 模拟 19/10/07_git_09
CSP-S 模拟 19/10/07_git_10
或者
CSP-S 模拟 19/10/07_i++_11
也就是说,对于不满足第一个条件的所有 CSP-S 模拟 19/10/07_git_09,需要满足第二个式子
CSP-S 模拟 19/10/07_子树_13
发现是求CSP-S 模拟 19/10/07_git_14,还是一段区间的 CSP-S 模拟 19/10/07_i++_15,用线段树维护即可
然后对于 CSP-S 模拟 19/10/07_i++_06 子树中的每个点,如果它的深度 CSP-S 模拟 19/10/07_git_17 那么已经不可能作为答案
我们在CSP-S 模拟 19/10/07_git_04 的时候记录一下当前能走到的最大深度就可以满足这个条件
考虑 CSP-S 模拟 19/10/07_i++_06 作为传送门的条件:
直径 CSP-S 模拟 19/10/07_git_20CSP-S 模拟 19/10/07_git_21
最后,线段树维护的是当前到根一条链的点
退栈的时候需要回退
ps:一道不像CSP-S 模拟 19/10/07_i++_22CSP-S 模拟 19/10/07_i++_22
主要学习一下思想:对于一条边横过来插来插去不好处理
我们可以考虑简化问题,也就是以一个端点为根,这条边就是子树中一个点到根的路径
考察理性分析经过传送门和不经过传送门的条件,并通过条件来选择需要维护的东西
不简单!

#include<bits/stdc++.h>
#define N 2050
using namespace std;
int read(){
int x = 0; char ch = 0;
while(!isdigit(ch)) ch = getchar();
while(isdigit(ch)) x = (x + (x << 2) << 1) + (ch ^ 48), ch = getchar();
return x;
}
void Mx(int &a, int b){ if(a < b) a = b; }
int n, k;
vector<int> v[N];
int f[N]; // longest_dis
int l1[N], l2[N], l3[N];
int t1[N], t2[N]; // from
int s1[N], s2[N];
int ans;
void dfs(int u, int fa){
f[u] = l1[u] = l2[u] = l3[u] = t1[u] = t2[u] = s1[u] = s2[u] = 0;
for(int i = 0; i < v[u].size(); i++){
int t = v[u][i]; if(t == fa) continue;
dfs(t, u);
Mx(f[u], f[t]);
if(f[t] > f[s1[u]]) s2[u] = s1[u], s1[u] = t;
else if(f[t] > f[s2[u]]) s2[u] = t;
int l = l1[t] + 1;
if(l > l1[u]){
l3[u] = l2[u], l2[u] = l1[u], l1[u] = l;
t2[u] = t1[u]; t1[u] = t;
}
else if(l > l2[u]){
l3[u] = l2[u], l2[u] = l, t2[u] = t;
} else if(l > l3[u]) l3[u] = l;
} Mx(f[u], l1[u] + l2[u]);
}
const int len = 2000, inf = 0x3fffffff;
struct Segmentree{
int mx[N << 4];
void pushup(int x){ mx[x] = max(mx[x<<1], mx[x<<1|1]); }
int modify(int x, int l, int r, int p, int v){
if(l == r){ int pre = mx[x]; mx[x] = v; return pre;}
int mid = (l+r) >> 1, ans = 0;
if(p <= mid) ans = modify(x<<1, l, mid, p, v);
else ans = modify(x<<1|1, mid+1, r, p, v);
pushup(x); return ans;
}
int query(int x, int l, int r, int L, int R){
if(L<=l && r<=R) return mx[x]; int mid = (l+r) >> 1, ans = 0;
if(L<=mid) ans = max(ans, query(x<<1, l, mid, L, R));
if(R>mid) ans = max(ans, query(x<<1|1, mid+1, r, L, R));
return ans;
}
int mo(int x, int v){ x += len; return modify(1, 1, n + len, x, v);}
int ask(int l, int r){ l += len, r += len; return query(1, 1, n + len, l, r);}
}Seg;
void solve(int u, int fa, int d, int lim){
if(d > lim) return;
int tmp = Seg.ask(k - l1[u] - d + 1, n);
if(f[u] <= k && 0 <= k - l1[u] - tmp + 1) ++ans;
for(int i = 0; i < v[u].size(); i++){
int t = v[u][i]; if(t == fa) continue;
int mxl = t == s1[u] ? f[s2[u]] : f[s1[u]];
if(t == t1[u]) Mx(mxl, l2[u] + l3[u]);
else if(t == t2[u]) Mx(mxl, l1[u] + l3[u]);
else Mx(mxl, l1[u] + l2[u]);
if(mxl > k) continue;
int l = (t == t1[u]) ? l2[u] : l1[u];
int tmp = Seg.ask(k - l - d + 1, n);
if(tmp == 0) tmp = -inf;
tmp = k + d + 1 - l - tmp;
int pre = Seg.mo(l-d, l+d);
solve(t, u, d + 1, min(lim, tmp));
Seg.mo(l-d, pre);
}
}
int main(){
n = read(); k = read();
for(int i = 2; i <= n; i++){
int x = read() + 1; v[x].push_back(i); v[i].push_back(x);
} dfs(1, 0); if(f[1] <= k) { cout << n * (n+1) / 2; return 0; }
for(int i = 1; i <= n; i++){
if(i^1) dfs(i, 0); solve(i, 0, 1, n);
} cout << (ans >> 1); return 0;
}

​​序列​​​ 法1:拆开发现需要维护
CSP-S 模拟 19/10/07_子树_24
然后开两颗线段树,一棵维护CSP-S 模拟 19/10/07_子树_25, 一棵维护 CSP-S 模拟 19/10/07_子树_26 的系数,发现修改 CSP-S 模拟 19/10/07_git_27CSP-S 模拟 19/10/07_子树_26 的系数是一个前缀区间的影响,打 CSP-S 模拟 19/10/07_i++_29CSP-S 模拟 19/10/07_i++_30即可,其余的都是单点修改
法2:猜它是不是有结合律?
类似CSP-S 模拟 19/10/07_i++_31 的套路,想到了一个叫矩阵乘的东西
CSP-S 模拟 19/10/07_i++_32
然后发现0和1没有什么用,维护两个系数就可以了
CSP-S 模拟 19/10/07_子树_33

#include<bits/stdc++.h>
#define N 500050
using namespace std;
int read(){
int x = 0; char ch = 0;
while(!isdigit(ch)) ch = getchar();
while(isdigit(ch)) x = (x + (x << 2) << 1) + (ch ^ 48), ch = getchar();
return x;
}
const int Mod = 1e9 + 7;
int add(int a, int b){ return a + b >= Mod ? a + b - Mod : a + b;}
int mul(int a, int b){ return 1ll * a * b % Mod;}
int n, m, a[N], b[N];

struct Node{
int A, B;
Node operator * (const Node &a){ return (Node){mul(A, a.A), add(mul(B, a.A), a.B)}; }
}sum[N << 2];
void pushup(int x){sum[x] = sum[x<<1] * sum[x<<1|1];}
void build(int x, int l, int r){
if(l == r){ sum[x] = (Node){a[l], b[l]}; return; }
int mid = (l+r) >> 1; build(x<<1, l, mid); build(x<<1|1, mid+1, r);
pushup(x);
}
void modify(int x, int l, int r, int p){
if(l == r){ sum[x] = (Node){a[l], b[l]}; return; }
int mid = (l+r) >> 1;
if(p <= mid) modify(x<<1, l, mid, p);
else modify(x<<1|1, mid+1, r, p);
pushup(x);
}
Node query(int x, int l, int r, int L, int R){
if(L<=l && r<=R) return sum[x]; int mid = (l+r) >> 1;
if(L>mid) return query(x<<1|1, mid+1, r, L, R);
else if(R<=mid) return query(x<<1, l, mid, L, R);
else return query(x<<1, l, mid, L, R) * query(x<<1|1, mid+1, r, L, R);
}
int main(){
n = read(), m = read();
for(int i = 1; i <= n; i++) a[i] = read(), b[i] = read();
build(1, 1, n);
while(m--){
char op[3]; scanf("%s", op);
if(op[0] == 'Q'){
int k = read(); Node ans = query(1, 1, n, 1, k);
cout << add(ans.A, ans.B) << '\n';
}
if(op[0] == 'C'){
int k = read(), A = read(), B = read();
a[k] = A; b[k] = B; modify(1, 1, n, k);
}
} return 0;
}


举报

相关推荐

0 条评论