T1:签到
因为题解说的实在太好了所以就直接粘贴了。
在取奇/偶数个数使得异或和最大,给所有权值加上一个
2
N
2^N
2N后加入线性基,如果需要取奇数个数就带入0,取偶数个数就带入
2
N
2^N
2N到线性基中查询最大异或和就可以了。
T2:树
一段连续的编号不好用数据结构维护距离,但是用一次dfs/bfs可以很方便地求出每个点到一段连续编号的最近距离。
O
(
(
n
+
m
)
l
o
g
2
)
O((n+m)log^2)
O((n+m)log2)的做法还有点分治,每个点分中心开一棵动态开点线段树(卡空间的话用指针开siz大小的线段树然后build),查询就在点分树上跳父亲在对应的线段树中查询区间最小值。
离线的话更好写,直接把询问挂到每个点上,建出一个点分中心的线段树后直接遍历子树中的询问然后查询对应的区间最小值(甚至可以用ST表),每一层的线段树查询完之后就没用了所以可以共用一个空间,空间就是
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)的。
Code:
#include<bits/stdc++.h>
#define maxn 100005
using namespace std;
char cb[1<<20],*cs,*ct;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<20,stdin),cs==ct)?0:*cs++)
inline void read(int &a){
char c;while(!isdigit(c=getc()));
for(a=c-'0';isdigit(c=getc());a=a*10+c-'0');
}
int n,m,siz[maxn],S[maxn],top,dis[maxn],st[maxn][18],lg[maxn],ans[maxn];
bool vis[maxn];
int fir[maxn],nxt[maxn<<1],to[maxn<<1],w[maxn<<1],tot;
inline void line(int x,int y,int z){nxt[++tot]=fir[x],fir[x]=tot,to[tot]=y,w[tot]=z;}
struct node{int id,l,r;};
vector<node>Q[maxn];
void getroot(int u,int ff,int tsz,int &g){
siz[u]=1; bool flg=1;
for(int i=fir[u],v;i;i=nxt[i]) if(!vis[v=to[i]]&&v!=ff)
getroot(v,u,tsz,g),siz[u]+=siz[v],flg&=siz[v]<<1<=tsz;
if(flg&&(tsz-siz[u])<<1<=tsz) g=u;
}
void dfs(int u,int ff){
S[++top]=u;
for(int i=fir[u],v;i;i=nxt[i]) if(!vis[v=to[i]]&&v!=ff) dis[v]=dis[u]+w[i],dfs(v,u);
}
inline int RMQ(int x,int y){
int k=lg[y-x+1];
return min(st[x][k],st[y-(1<<k)+1][k]);
}
void solve(int u){
dis[u]=top=0,dfs(u,0),sort(S+1,S+1+top),lg[0]=-1;
for(int i=1;i<=top;i++) lg[i]=lg[i>>1]+1,st[i][0]=dis[S[i]];
for(int j=1;j<=lg[top];j++)
for(int i=1,l=1<<j;i+l-1<=top;i++)
st[i][j]=min(st[i][j-1],st[i+(l>>1)][j-1]);
for(int i=1,l,r;i<=top;i++)
for(int j=0,x=S[i],lim=Q[x].size();j<lim;j++)
if((l=lower_bound(S+1,S+1+top,Q[x][j].l)-S)<=(r=upper_bound(S+1,S+1+top,Q[x][j].r)-S-1))
ans[Q[x][j].id]=min(ans[Q[x][j].id],dis[x]+RMQ(l,r));
}
void TDC(int u,int tsz){
getroot(u,0,tsz,u),vis[u]=1,solve(u);
for(int i=fir[u],v;i;i=nxt[i]) if(!vis[v=to[i]]) TDC(v,siz[v]<siz[u]?siz[v]:tsz-siz[u]);
}
int main()
{
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
read(n); int l,r,x,y,z;
for(int i=1;i<n;i++) read(x),read(y),read(z),line(x,y,z),line(y,x,z);
read(m);
for(int i=1;i<=m;i++) read(l),read(r),read(x),ans[i]=x<l||x>r?Q[x].push_back((node){i,l,r}),1e9:0;
TDC(1,n);
for(int i=1;i<=m;i++) printf("%d\n",ans[i]);
}
线段树分治+虚树做法(by Freopen):
#include<bits/stdc++.h>
#define maxn 100005
#define lim 18
using namespace std;
int n,m,ans[maxn],lg[maxn<<1];
int info[maxn],Prev[maxn<<1],to[maxn<<1],cst[maxn<<1],cnt_e;
void Node(int u,int v,int c){ Prev[++cnt_e]=info[u],info[u]=cnt_e,to[cnt_e]=v,cst[cnt_e]=c; }
int fir[maxn],nxt[maxn<<1],tar[maxn<<1],cnte;
void add(int u,int v){ nxt[++cnte] = fir[u] , fir[u] = cnte , tar[cnte] = v; }
vector<pair<int,int> >G[maxn<<2];
int dep[maxn],st[lim][maxn<<1],St[maxn],Ed[maxn],tot;
void dfs(int u,int ff){
st[0][St[u] = ++tot] = u;
for(int i=info[u],v;i;i=Prev[i]) if((v=to[i])^ff){
dep[v] = dep[u] + cst[i];
dfs(v,u);
st[0][++tot] = u;
}
Ed[u] = tot;
}
int Lca(int u,int v){
u = St[u] , v = St[v];
if(u > v) swap(u,v);
int t = lg[v - u + 1];
return dep[st[t][u]] < dep[st[t][v-(1<<t)+1]] ? st[t][u] : st[t][v-(1<<t)+1];
}
#define lc u<<1
#define rc u<<1|1
void ins(int u,int l,int r,int ql,int qr,pair<int,int> x){
if(ql > r || l > qr) return;
if(ql <= l && r <= qr) return (void)(G[u].push_back(x));
int m = l+r>>1;
ins(lc,l,m,ql,qr,x),ins(rc,m+1,r,ql,qr,x);
}
bool cmp(const int &u,const int &v){
return St[u] < St[v];
}
int f[maxn];
void dfs1(int u,int ff,int l,int r){
f[u] = (l <= u && u <= r) ? 0 : 0x3f3f3f3f;
for(int i=fir[u],v;i;i=nxt[i]) if((v=tar[i])^ff){
dfs1(v,u,l,r);
f[u] = min(f[u] , f[v] - dep[u] + dep[v]);
}
}
void dfs2(int u,int ff){
for(int i=fir[u],v;i;i=nxt[i]) if((v=tar[i])^ff){
f[v] = min(f[v] , f[u] + dep[v] - dep[u]);
dfs2(v,u);
}
}
void Solve(int u,int l,int r){
if(l > r) return;
int m = l+r >> 1;
if(l != r) Solve(lc,l,m),Solve(rc,m+1,r);
static int ar[maxn<<1]={},cnt=0,q[maxn]={},R,pts[maxn]={},ptc=0;
cnt = R = cnte = ptc = 0;
for(int i=l;i<=r;i++) ar[cnt++] = i;
for(int i=0;i<G[u].size();i++) ar[cnt++] = G[u][i].first;
sort(ar,ar+cnt,cmp);
cnt = unique(ar,ar+cnt) - ar;
for(int i=0;i<cnt;i++){
if(R){
int t=Lca(ar[i] , q[R]) , p = 0;
for(;R && dep[q[R]] > dep[t];p = q[R--]) if(p)
add(q[R] , p);
if(q[R] ^ t) q[++R] = pts[++ptc] = t;
if(p) add(q[R] , p);
}
q[++R] = pts[++ptc] = ar[i];
}
for(int p=0;R;p=q[R--]) if(p)
add(q[R] , p);
dfs1(q[1],0,l,r),dfs2(q[1],0);
for(int i=0;i<G[u].size();i++)
ans[G[u][i].second] = min(ans[G[u][i].second] , f[G[u][i].first]);
for(;ptc;) fir[pts[ptc--]] = 0;
}
int main(){
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<n;i++){
int u,v,l;scanf("%d%d%d",&u,&v,&l);
Node(u,v,l),Node(v,u,l);
}
dfs(1,0);
for(int i=2;i<=tot;i++) lg[i] = lg[i>>1] + 1;
for(int j=1;j<lim;j++) for(int i=1;i<=tot-(1<<j)+1;i++)
st[j][i] = dep[st[j-1][i]] < dep[st[j-1][i + (1<<j-1)]] ? st[j-1][i] : st[j-1][i + (1<<j-1)];
scanf("%d",&m);
for(int i=1;i<=m;i++){
int l,r,x;
scanf("%d%d%d",&l,&r,&x);
ins(1,1,n,l,r,make_pair(x,i));
ans[i] = 0x3f3f3f3f;
}
Solve(1,1,n);
for(int i=1;i<=m;i++)
printf("%d\n",ans[i]);
}
T3:区间
不同的权值个数没什么好办法,强制在线多个区间,只有用bitset强上。
肯定是要分块的,但分块之后直接合并复杂度就是
O
(
n
2
n
/
w
)
O(n^2\sqrt n/w)
O(n2n/w)了,显然无法承受。
还要优化中间整块的合并过程,我们有线段树和ST表两种方式,但是线段树的合并仍然是
O
(
n
2
/
w
∗
l
o
g
)
O(n^2/w*log)
O(n2/w∗log)的,所以选择ST表。
建立的复杂度是
O
(
(
n
S
log
n
S
)
∗
n
w
)
O((\frac nS\log\frac nS)*\frac nw)
O((SnlogSn)∗wn),合并是
O
(
n
2
w
)
O(\frac {n^2}w)
O(wn2),散块暴力是
O
(
n
S
)
O(nS)
O(nS)。
空间复杂度和建立的复杂度相同,而空间限制只有
8
MB
8\text{MB}
8MB,所以
S
S
S要开大些,1700左右,甚至更大。
然后卡常,手写bitset会快于STL,实测
1.8
s
→
1
s
1.8s\rarr1s
1.8s→1s
然鹅出题人还有奇思妙想。。
Code(最后那个优化我没加,勉强卡过):
#include<bits/stdc++.h>
#define maxn 100005
using namespace std;
inline void read(int &a){
char c;while(!isdigit(c=getchar()));
for(a=c-'0';isdigit(c=getchar());a=a*10+c-'0');
}
const int S = 1700, all = (1<<16)-1;
int n,m,T,N,ans,a[maxn],b[maxn],lg[62]={-1},dt[1<<16],tot;
struct bitset{
unsigned int a[(maxn>>5)+3];
bitset operator | (const bitset &b)const{bitset c; for(int i=0;i<=tot;i++) c.a[i]=a[i]|b.a[i]; return c;}
void reset(){memset(a,0,(tot+1)<<2);}
void set(int x){a[x>>5]|=1u<<(x&31);}
int count(){int s=0;for(int i=0;i<=tot;i++) s+=dt[a[i]>>16]+dt[a[i]&all];return s;}
}st[6][58],now;
int main()
{
freopen("interval.in","r",stdin);
freopen("interval.out","w",stdout);
for(int i=1;i<=all;i++) for(int x=i;x;x&=(x-1)) ++dt[i];
read(n),read(m),read(T),N=(n-1)/S;//remove the last block.
for(int i=1;i<=n;i++) read(a[i]),b[i]=a[i];
sort(b+1,b+1+n),tot=unique(b+1,b+1+n)-b-1;
for(int i=1;i<=n;i++) a[i]=lower_bound(b+1,b+1+tot,a[i])-b-1; tot=(tot-1)>>5;
for(int i=1;i<=N;i++) lg[i]=lg[i>>1]+1;
for(int i=0;i<N;i++)
for(int j=i*S+1,r=i*S+S;j<=r;j++)
st[0][i].set(a[j]);
for(int j=1;j<=lg[N];j++)
for(int i=0,l=1<<j;i+l<=N;i++)
st[j][i]=st[j-1][i]|st[j-1][i+(l>>1)];
for(int Q=1,l,r,x,y,t,k;Q<=m;Q++){
read(k),now.reset();
while(k--){
read(l),read(r);
if(T&&Q>1) {l=(l^ans)%n+1,r=(r^ans)%n+1; if(l>r) swap(l,r);}
x=(l-1)/S,y=(r-1)/S;
if(y-x<=1) {for(int i=l;i<=r;i++) now.set(a[i]);continue;}
for(int i=l,lim=x*S+S;i<=lim;i++) now.set(a[i]);
for(int i=r,lim=y*S+1;i>=lim;i--) now.set(a[i]);
t=lg[y-x-1];
now=now|st[t][x+1]|st[t][y-(1<<t)];
}
printf("%d\n",ans=now.count());
}
}