20200417【网格路径异或最大值,树上x到一段编号区间的最短距离,多个区间的不同权值种数】

7 篇文章 0 订阅
5 篇文章 0 订阅
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/wlog)的,所以选择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.8s1s
然鹅出题人还有奇思妙想。。
在这里插入图片描述
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());
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值