T1
这是一个很老的题了
想到肯定和LCA树剖啊有关,但当时我看到1e6的范围时果断放弃
结果它m的时限时2秒
说说我的分析:
最值问题?不是贪心就是dp
对于dp,如果转化为线性的,就是一个单调队列优化dp的模板
但是想了半天一棵树怎么都不能转化为线性的
对于贪心,考虑一个点如果能被较多的区间覆盖就肯定最优(能多白嫖)
这样选择了一个点之后,所有区间包含它的区间都要删掉,貌似树剖可以(懒得打)
正解:
考虑对于每一条路径的LCA求出来,将他们的LCA按照深度排序,从深度较大的删,能不删就不删,
这样保证结果最优
至于证明:
应该是个感性理解,画几个图就会明白的
code by std:
#include<bits/stdc++.h>
#define rep for(int s,i=lk[x];i;i=hd[i])if(f[x]^(s=to[i]))
const int N=1000006;
using namespace std;
int n,m,to[N*2],hd[N*2],lk[N],cnt,
f[N],sz[N],de[N],son[N],top[N],ans;
inline void add(int u,int v){
to[++cnt]=v,hd[cnt]=lk[u],lk[u]=cnt;
}int u,v;
void dfs(int x){
sz[x]=1,de[x]=de[f[x]]+1;
rep f[s]=x,dfs(s),sz[x]+=sz[s],
sz[son[x]]<sz[s]?son[x]=s:0;
}
void dfss(int x){
if(!top[x])top[x]=x;
top[son[x]]=top[x];
rep dfss(s);
}
void cov(int x){
top[x]=0;
rep if(top[s])cov(s);
}
struct dat{
int u,v,w;
inline void rd(){
scanf("%d%d",&u,&v);
int x=u,y=v;
for(;top[x]^top[y];x=f[top[x]])
if(de[top[x]]<de[top[y]])swap(x,y);
w=de[x]<de[y]?x:y;
}
}a[N];
bool cmp(dat x,dat y){return de[x.w]>de[y.w];}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<n;i++)
scanf("%d%d",&u,&v),
add(u,v),add(v,u);
dfs(1),dfss(1);
for(int i=0;i<m;i++)
a[i].rd();
sort(a,a+m,cmp);
for(int i=0;i<m;i++)
if(top[a[i].u]&&top[a[i].v])
ans++,cov(a[i].w);
printf("%d",ans);
}
T2压根没读懂它md要我求什么------跳过
T3
首先说说
线性基
线性基是一种擅长处理异或问题的数据结构.设值域为[1,N],就可以用一个长度为logN的数组来描述一个线性基。特别地,线性基第i位上的数二进制下最高位也为第i位。
一个线性基满足,对于它所表示的所有数的集合S,S中任意多个数异或所得的结果均能表示为线性基中的元素互相异或的结果,
即意,线性基能使用异或运算来表示原数集使用异或运算能表示的所有数。
运用这个性质,我们可以极大地缩小异或操作所需的查询次数。
插入和判断
我们考虑插入的操作,令插入的数为x,考虑x的二进制最高位i,
- 若线性基的第i位为0,则直接在该位插入x,退出;
- 若线性基的第i位已经有值ai,则x=x⊕ai,重复以上操作直到x=0。
如果退出时x=0,则此时线性基已经可以表示原先的x了;
反之,则说明为了表示x,往线性基中加入了一个新元素。
查询异或最值
查询最小值相对比较简单。
线性基中最小值异或上其他数,必定会增大。所以,直接输出线性基中的最小值即可。
考虑异或最大值,
从高到低遍历线性基,考虑到第i位时,如果当前的答案x第i位为0,就将x异或上ai;
否则不做任何操作。显然,每次操作后答案不会变劣,最终的x即为答案。
同样,我们考虑对于一个数x,它与原数列中的数异或的最值如何获得。
用与序列异或最大值类似的贪心即可解决
很容易证明这样复杂度为log2x,也可以用这种方法判断能否通过原数列异或得到一个数x。
有一个模板题:https://www.luogu.org/problem/P3812
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define ri register int
#define lowbit(x) x&(-x)
using namespace std;
const int maxn=55;
ll a[maxn],p[maxn<<1];
ll ans;
int n;
il void insert(ll x){
for(ri i=62;i>=0;i--){
if(!(x>>(ll)i))continue;
if(!p[i]){p[i]=x;break;}
else x^=p[i];
}
}
int main(){
scanf("%d",&n);
for(ri i=1;i<=n;i++)scanf("%lld",&a[i]),insert(a[i]);
for(ri i=62;i>=0;i--)
if((ans^p[i])>ans)ans^=p[i];
printf("%lld
",ans);
return 0;
}
回到正题:
这题唯一不同的就是有插入操作和强制在线
考虑线性基交换插入顺序结果不变的性质,对于同一个r ,强行搞一搞。
把新加的数 ax 连同坐标信息 x 按照正常过程插进线性基里。
特殊的是当线性基的这一位有值,且对应的坐标 y < x 时,需要把 ax 直接 放在这一位,并把这一位原来的数拿出来继续插入线性基
通俗点就是前一个 r,然后新插入的把对应位置上原来的往后面赶。
查询 bse[r]中位置大于等于 l的那一部分。
code by std
#include<bits/stdc++.h>
const int N=2000006,L=30;
using namespace std;
int tp,n,m,x,b[N][L],p[N][L],lans;
inline void ins(){
for(int i=0;i<L;i++)
b[n+1][i]=b[n][i],p[n+1][i]=p[n][i];
n++;
for(int i=L-1,loc=n;~i;i--)
if(x>>i&1){
if(!b[n][i]){b[n][i]=x,p[n][i]=loc;return;}
if(p[n][i]<loc)
swap(b[n][i],x),swap(p[n][i],loc);
x^=b[n][i];
}
}
int op,l,r;
int main(){
scanf("%d%d",&tp,&m);
for(;tp--;)
scanf("%d",&x),ins();
for(;m--;){
scanf("%d",&op);
if(op)scanf("%d",&x),x^=lans,ins();
else{
scanf("%d%d",&l,&r);
l=(l^lans)%n+1,r=(r^lans)%n+1;
if(l>r)swap(l,r);
x=0;
for(int i=L-1;~i;i--)
if(p[r][i]>=l&&!(x>>i&1))
x^=b[r][i];
printf("%d
",lans=x);
}
}
}