【TEST190408】 斯坦纳树 + 分块 + 线段树分治&并查集

paths

斯坦纳树板题

呃,居然忘了自己会斯坦纳树,没看出来这是个板题?
乱搞拿了70…

#include<bits/stdc++.h> 
using namespace std;
const int N=102,M=2005,inf=0x7f7f7f7f;

int n,m,K,ans,S,s,f[N][(1<<10)+10],g[(1<<10)+10];
int head[N],to[M],nxt[M],w[M],tot;
bool cn[(1<<10)+10];

char cp;
template<class T>inline void rd(T &x)
{
	cp=getchar();x=0;int f=0;
	for(;!isdigit(cp);cp=getchar()) if(cp=='-') f=1;
	for(;isdigit(cp);cp=getchar()) x=x*10+(cp^48);
	if(f) x=-x;
}

inline void lk(int u,int v,int vv)
{to[++tot]=v;nxt[tot]=head[u];head[u]=tot;w[tot]=vv;}

inline void dn(int &x,int y){if(y<x) x=y;}

struct pr{
	int x,v;
	pr(int x_=0,int v_=0):x(x_),v(v_){};
	bool operator <(const pr&ky)const{return v>ky.v;}
}tp;
priority_queue<pr>que;

inline void dij()
{
	int i,j,x;
	for(;!que.empty();){
		tp=que.top();que.pop();x=tp.x;
		if(f[x][s]!=tp.v) continue;
		for(i=head[x];i;i=nxt[i]){
			j=to[i];if(f[j][s]<=f[x][s]+w[i]) continue;
			f[j][s]=f[x][s]+w[i];que.push(pr(j,f[j][s]));
		}
	}
}


int main(){
	freopen("paths.in","r",stdin);
	freopen("paths.out","w",stdout);
	int i,j,x,y,z,t;memset(f,0x7f,sizeof(f));
	rd(n);rd(m);rd(K);S=1<<K;
	for(i=1;i<=m;++i){
		scanf("%d%d%d",&x,&y,&z);
		x--;y--;lk(x,y,z);lk(y,x,z);
    }
    for(i=0;i<K;++i) f[i][1<<i]=f[i][0]=0;
	for(s=1;s<S;++s){
	    for(i=0;i<n;++i){
	    	for(t=s&(s-1);t;t=(t-1)&s) if(f[i][t]<inf && f[i][s^t]<inf)
	    	 dn(f[i][s],f[i][t]+f[i][s^t]);
	    	if(f[i][s]<inf) que.push(pr(i,f[i][s]));
		}
		dij();
    }
	for(i=0;i<S;++i){
		for(j=0;j<K;j+=2) if(((i>>j)&1)^((i>>(j+1))&1)) break;
		if(j>=K) cn[i]=true;
		for(j=1;j<n;++j) 
		  dn(f[0][i],f[j][i]);
	}
	memset(g,0x7f,sizeof(g));g[0]=0;
	for(s=1;s<S;++s) if(cn[s])
	 for(t=s;t;t=(t-1)&s) 
	  if(cn[t] && f[0][t]<inf && g[s^t]<inf)
	  dn(g[s],g[s^t]+f[0][t]);
	printf("%d",g[S-1]);
	fclose(stdin);fclose(stdout);
	return 0;
} 

*quiz

分块。

按下标分块,预处理 f i , j f_{i,j} fi,j表示点 i i i在第 j j j块的 s s s中被计算次数, s u m i sum_i sumi维护第 i i i块的 ∑ s \sum s s n \sqrt n n 维护修改后的 s u m i sum_i sumi

发现散块的单点计算依旧是 O ( n ) O(n) O(n)的,考虑再按dfs序分块维护 w w w的前缀和(整块/块内), n \sqrt n n 维护修改后的前缀和。

在询问时,整块可以 s u m sum sum求,散块暴力遍历,并用dfs序的分块 O ( 1 ) O(1) O(1)回答 s s s值。

总复杂度 O ( n n ) O(n\sqrt n) O(nn )

#include<bits/stdc++.h> 
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N=1e5+10,M=322;

int n,m,rt,val[N],c[N];
int df[N],ot[N],dfn,rv[N];
int head[N],to[N<<1],nxt[N<<1],tot;

struct qr{int op,l,r;}q[N];

char cp;
template<class T>inline void rd(T &x)
{
	cp=getchar();x=0;int f=0;
	for(;!isdigit(cp);cp=getchar()) if(cp=='-') f=1;
	for(;isdigit(cp);cp=getchar()) x=x*10+(cp^48);
	if(f) x=-x;
}

template<class T>inline void prit(T x)
{if(x>9) prit(x/10);putchar('0'+(x%10));}

inline void lk(int u,int v)
{to[++tot]=v;nxt[tot]=head[u];head[u]=tot;}

void dfs(int x,int fr)
{
	int i,j,k;df[x]=++dfn;rv[dfn]=x;
	for(i=head[x];i;i=nxt[i]){
		j=to[i];if(j==fr) continue;
		dfs(j,x);
	}
	ot[x]=dfn;
}

namespace FK{

int blk,f[N][M],bel[N],cot;
ull sum[M],ss[M][M];

inline void chg(int x,int y)
{
	int i,j,k,l,r,dlt=y-val[x];
	for(i=1;i<=cot;++i) sum[i]+=(ll)f[x][i]*dlt;

	i=bel[df[x]];l=(i-1)*blk+1;r=min(i*blk,n);
	for(j=df[x]-l+1;l+j-1<=r;++j) ss[i][j]+=dlt;
	for(j=i;j<=cot;++j) ss[j][0]+=dlt;
	val[x]=y;
}

inline ull query(int l,int r)
{
	int L=bel[l],R=bel[r],i,j;ull re=0;
	if(L+1<R) re+=(ss[R-1][0]-ss[L][0]);
	i=(L-1)*blk;j=min(n,L*blk);re+=ss[L][min(r,j)-i];
	if(l!=i+1) re-=ss[L][l-i-1];
	if(L!=R) re+=ss[R][r-(R-1)*blk];
	return re;
}

inline ull ask(int l,int r)
{
	int i,j,L=bel[l],R=bel[r];ull re=0;
	for(i=L+1;i<R;++i) re+=sum[i];
	j=min(L*blk,min(n,r));
	for(i=l;i<=j;++i) re+=query(df[i],ot[i]);
	if(L!=R){
		j=(R-1)*blk;
		for(i=r;i>j;--i) re+=query(df[i],ot[i]); 
	}
	return re;
}

}

using namespace FK;
int main(){
	freopen("quiz.in","r",stdin);
	freopen("quiz.out","w",stdout);
	int i,j,x,y,l,r;
	rd(n);rd(m);
	for(i=1;i<=n;++i) rd(val[i]);
	for(i=1;i<=n;++i){
		rd(x);rd(y);if(x>y) swap(x,y);
		if(!x) rt=y;else {lk(x,y);lk(y,x);}
	}
	dfs(rt,0);
	
	blk=(int)sqrt(n);cot=(n-1)/blk+1;
	for(i=1;i<=n;++i) bel[i]=(i-1)/blk+1;
	for(i=1;i<=cot;++i){
		l=(i-1)*blk+1;r=min(n,i*blk);
		for(j=l;j<=r;++j) c[df[j]]++,c[ot[j]+1]--;
		for(j=1;j<=n;++j){c[j]+=c[j-1];f[rv[j]][i]=c[j];sum[i]+=(ll)c[j]*val[rv[j]];}
		memset(c,0,(n+2)<<2);
	}
	for(i=1;i<=cot;++i){
		l=(i-1)*blk+1;r=min(n,i*blk);x=r-l+1;
		for(j=1;j<=x;++j) ss[i][j]=ss[i][j-1]+val[rv[l+j-1]];
		ss[i][0]=ss[i-1][0]+ss[i][x];
	}
	
	for(;m;--m){
		rd(x);rd(l);rd(r);
		if(x==1) chg(l,r);
		else{prit(ask(l,r));putchar('\n');}
	}
	fclose(stdin);fclose(stdout);
	return 0;
} 

racing

线段树分治+按秩合并的并查集

#include<bits/stdc++.h>
#define pb push_back
using namespace std;
const int N=7e4+10;

int n,m,lnv[N<<1],dep[N],bin[30];
int st[19][N<<1],df[N],rv[N<<1],dfn;
int ans[N],cnt[N],q[N],cur;
int head[N],to[N<<1],nxt[N<<1],tot;

struct pr{
	int u,v;
	pr(int u_=0,int v_=0):u(u_),v(v_){};
}le[N];

char cp;
template<class T>inline void rd(T &x)
{
	cp=getchar();x=0;int f=0;
	for(;!isdigit(cp);cp=getchar()) if(cp=='-') f=1;
	for(;isdigit(cp);cp=getchar()) x=x*10+(cp^48);
	if(f) x=-x;
}

inline void prit(int x)
{if(x>9) prit(x/10);putchar('0'+(x%10));}

inline void lk(int u,int v)
{to[++tot]=v;nxt[tot]=head[u];head[u]=tot;}

void dfs(int x,int fr)
{
	int i,j;dep[x]=dep[fr]+1;
	rv[++dfn]=x;st[0][dfn]=x;df[x]=dfn;
	for(i=head[x];i;i=nxt[i]){
		j=to[i];if(j==fr) continue;
		dfs(j,x);rv[++dfn]=x;st[0][dfn]=x;
	}
}

inline int LCA(int x,int y)
{
	x=df[x];y=df[y];if(x>y) swap(x,y);
	int bs=lnv[y-x+1],ix=st[bs][x],iy=st[bs][y-bin[bs]+1];
	return (dep[ix]<dep[iy])?ix:iy;
}

inline int dist(int x,int y)
{return dep[x]+dep[y]-(dep[LCA(x,y)]<<1);}

struct bcj{
    int fa,id,h,d[2];
    inline void ini(int x){fa=id=d[0]=d[1]=x;}
}b[N],stk[N<<3];
inline int getfa(int x){return x==b[x].fa?x:getfa(b[x].fa);}

namespace SGT{

#define mid ((l+r)>>1)
#define lc k<<1
#define rc k<<1|1
#define lcc lc,l,mid
#define rcc rc,mid+1,r

int top;
vector<int>hv[N<<2];

void ins(int k,int l,int r,int L,int R,int id)
{
	if(L>R) return;
	if(L<=l && r<=R) {hv[k].pb(id);return;}
	if(L<=mid) ins(lcc,L,R,id);
	if(R>mid) ins(rcc,L,R,id);
}

inline void merge(int x,int y)
{
	int i,j,k,s=dist(b[x].d[0],b[x].d[1]),ix=b[x].d[0],iy=b[x].d[1];
	j=dist(b[y].d[0],b[y].d[1]);if(j>s) s=j,ix=b[y].d[0],iy=b[y].d[1];
	for(i=0;i<2;++i)
	 for(j=0;j<2;++j){
	 	k=dist(b[x].d[i],b[y].d[j]);
	 	if(k>s) s=k,ix=b[x].d[i],iy=b[y].d[j];
	 }
	if(b[x].h<b[y].h) swap(x,y);stk[++top]=b[x];stk[++top]=b[y];
	b[x].h=max(b[x].h,b[y].h+1);b[y].fa=x;b[x].d[0]=ix;b[x].d[1]=iy;
	cur=max(cur,s);
}

void sol(int k,int l,int r)
{
	if(cnt[r]==cnt[l-1]) return;
	int pos=top,ori=cur,i,j,x,y;
	for(i=hv[k].size()-1;i>=0;--i){
		j=hv[k][i];
		x=getfa(le[j].u);y=getfa(le[j].v);
		merge(x,y);
	}
	if(l!=r){sol(lcc);sol(rcc);}
	else ans[l]=cur;
	
	//reback
	cur=ori;
	for(;top>pos;--top)	b[stk[top].id]=stk[top];
}

}

int main(){
	freopen("racing.in","r",stdin);
	freopen("racing.out","w",stdout);
	int i,j,lim,x,y,l,r;
	//init
	rd(n);rd(m);
	for(i=1;i<n;++i){
		rd(x);rd(y);rd(l);rd(r);lk(x,y);lk(y,x);
		le[i]=pr(x,y);l=max(l,1);r=min(r,n);
		SGT::ins(1,1,n,l,r,i);
	}
	
	//precal LCA
	dfs(1,0);
	bin[0]=1;for(i=1;i<25;++i) bin[i]=bin[i-1]<<1;
	for(i=2;i<=dfn;++i) lnv[i]=lnv[i>>1]+1;
	for(i=1;bin[i]<=dfn;++i)
	 for(j=1;j+bin[i]-1<=dfn;++j){
	    x=st[i-1][j];y=st[i-1][j+bin[i-1]];
	    st[i][j]=(dep[x]<dep[y])?x:y;
	 }
	//pre bcj
	for(i=1;i<=n;++i) b[i].ini(i);
	
	//solve
	for(i=1;i<=m;++i){rd(q[i]);cnt[q[i]]++;}
	for(i=2;i<=n;++i) cnt[i]+=cnt[i-1];
	SGT::sol(1,1,n);
	for(i=1;i<=m;++i){
		prit(ans[q[i]]);if(i<m) putchar('\n');
	}
	fclose(stdin);fclose(stdout);
	return 0;
} 

总结

STO 差点AK的LPA (跪

T1没有看出来是斯坦纳树是真的蠢!(不过乱搞骗了70pts算是进步
T2分块套分块就不会了,需要复习一下DS的基本套路
T3不解释

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值