• 【考试总结】20220610


    Geometric Progressions

    将所有 \(a_i,b_i\) 中出现过的质因子取出,并从 \(1\)\(n\) 考察每对 \((a_i,b_i)\) 对这些因子取值的影响

    求出来 \((a,b)\) 在每个因子指数上的直线表达式 \(kx+b\) 并取 \(x\ge 0\) 时的交集,如果出现某个质因子交集为空那么不合法,否则最小答案中每个质因子指数就是将其对应直线的 \(x\) 带入 \(0\) 得到

    设之前已经合并的结果中每个因子的直线为 \(fk_ix+fb_i\),而当前待合并的直线表达式为 \(gk_i+gb_i\)

    \(fk_i,gk_i\) 两者为 \(0\) 则看 \(fb_i\) 是否等于 \(gb_i\);有其中一个为 \(0\) 那么可以得到 \(x\) 的具体值,不是整数显然不合法,否则合并结果中每个质因子的直线斜率都是 \(0\),对应的纵轴截距可以带入得到的 \(x\) 求出

    如果两者均不是 \(0\) 那么得到方程 \(fk_ix_1+fb_i=gk_ix_2+gb_i\) ,合并后的结果就是这个方程的解集

    由于每个质因子都可能得到这样一个方程,如果方程数量超过 \(1\) 那么可以得到 \(x\) 的具体值或者判定无解

    如果只有一个方程,剩下的本质相同那么可以使用 \(\text{exgcd}\) 得到新的解集,要求纵轴截距最小 和 求方程在两个变量都非负的情况下 \(x_1\) 最小一致,得到最小的 \(x_1\) 之后可以计算出来新的直线

    Code Display
    const int N=1e5+10;
    int pri[N],num;
    inline void Divide(int x){
    	for(int i=2;i*i<=x;++i){
    		if(x%i) continue;
    		while(x%i==0) x/=i;
    		pri[++num]=i;
    	}
    	if(x>1) pri[++num]=x;
    	return ;
    }
    inline void exgcd(int a,int b,int &x,int &y){
    	if(!b) return x=1,y=0,void();
    	exgcd(b,a%b,x,y);
    	int k=x; x=y; y=k-(a/b)*x;
    	return ;
    }
    struct line{int k,b;}f[N],g[N];
    struct Equation{
    	int A,B,C;
    	inline void adjust(){
    		int g=__gcd(myabs(A),__gcd(myabs(B),myabs(C)));
    		A/=g; B/=g; C/=g;
    		return ;
    	}
    };
    inline void solve(int K,int B,int id){
    	for(int i=1;i<=num;++i){
    		g[i].k=g[i].b=0;
    		while(K%pri[i]==0) g[i].k++,K/=pri[i];
    		while(B%pri[i]==0) g[i].b++,B/=pri[i];
    	}
    	if(id==1){
    		rep(i,1,num) f[i]=g[i];
    		return ;
    	}
    	int Turn=3;
    	while(Turn--){
    		for(int i=1;i<=num;++i){
    			if(!g[i].k&&!f[i].k){
    				if(g[i].b!=f[i].b) puts("-1"),exit(0);
    				continue;
    			}
    			if(!g[i].k){
    				if(g[i].b<f[i].b) puts("-1"),exit(0);
    				if((g[i].b-f[i].b)%f[i].k) puts("-1"),exit(0);
    				int x=(g[i].b-f[i].b)/f[i].k;
    				for(int j=1;j<=num;++j) f[j].b=f[j].k*x+f[j].b,f[j].k=0;
    				continue;
    			}
    			if(!f[i].k){
    				if(f[i].b<g[i].b) puts("-1"),exit(0);
    				if((f[i].b-g[i].b)%g[i].k) puts("-1"),exit(0);
    				int x=(f[i].b-g[i].b)/g[i].k;
    				for(int j=1;j<=num;++j) g[j].b=g[j].k*x+g[j].b,g[j].k=0;
    			}
    		}
    	}
    	
    	Equation F;
    	int mark=0,X=-1,Y=-1;
    	for(int i=1;i<=num;++i){
    		if(!(g[i].k*f[i].k)) continue;
    		if(!mark){
    			F.A=f[i].k; F.B=-g[i].k; F.C=g[i].b-f[i].b;
    			mark=1;
    			continue;
    		}
    		int A=f[i].k,B=-g[i].k,C=g[i].b-f[i].b;
    		if(mark==1){
    			int G=__gcd(myabs(A),myabs(F.A));
    			int dcur=A/G,dF=F.A/G;
    			F.A*=dcur; F.B*=dcur; F.C*=dcur;
    			A*=dF; B*=dF; C*=dF;
    			if(A==F.A&&B==F.B){
    				if(C!=F.C) puts("-1"),exit(0);
    				F.adjust();
    				continue;
    			}
    			C-=F.C; B-=F.B;
    			if(C*B<0) puts("-1"),exit(0);
    			if(C<0) C=-C,B=-B;
    			if(C%B) puts("-1"),exit(0);
    			Y=C/B;
    			if((F.C-Y*F.B)%F.A) puts("-1"),exit(0);
    			X=(F.C-Y*F.B)/F.A;
    			if(X<0) puts("-1"),exit(0);
    			mark=2;
    			continue;
    		}
    		if(A*X+B*Y!=C) puts("-1"),exit(0);
    	}
    	if(mark==2){
    		for(int i=1;i<=num;++i) f[i].b+=f[i].k*X,f[i].k=0;
    		return ;
    	}
    	if(!mark) return ;
    	F.B*=-1;
    	int G=__gcd(F.A,F.B);
    	if(F.C%G) puts("-1"),exit(0);
    	F.A/=G; F.B/=G; F.C/=G;
    	int x,y;
    	exgcd(F.A,F.B,x,y);
    	x*=F.C; y*=-F.C;
    	int dx=0,dy=0;
    	if(x<0) dx=-((-x+F.B-1)/F.B);
    	else dx=x/F.B;
    	if(y<0) dx=-((-y+F.A-1)/F.A);
    	else dy=y/F.A;
    	x-=F.B*min(dx,dy);
    	y-=F.A*min(dx,dy);
    	for(int i=1;i<=num;++i){
    		f[i].b+=f[i].k*x;
    		f[i].k*=F.B;
    	}
    	return ;
    }
    int a[110],b[110],n;
    signed main(){
    	freopen("a.in","r",stdin); freopen("a.out","w",stdout);
    	n=read();
    	for(int i=1;i<=n;++i){
    		a[i]=read(),b[i]=read();
    		Divide(a[i]);
    		Divide(b[i]);
    	}
    	sort(pri+1,pri+num+1);
    	num=unique(pri+1,pri+num+1)-pri-1;
    	for(int i=1;i<=n;++i) solve(b[i],a[i],i);
    	int ans=1;
    	for(int i=1;i<=num;++i) ckmul(ans,ksm(pri[i],f[i].b));
    	printf("%lld\n",(long long)ans);
    	return 0;
    }
    
    

    Bichrome Spanning Tree

    求出任一 \(\rm MST\),设其权值总和为 \(S\),如果 \(x<S\) 无解

    先强制 \(\rm MST\) 上所有边的颜色都一样,这个 \(\text{case}\) 方案数要乘 \(2\)

    那么对于一条非树边,如果将其加入后带来的最小增量小于 \(x-S\) ,那么它的颜色和 \(\rm MST\) 上边的颜色保持一致;记录增量等于 \(x-S\) 的边的数量 \(e_1\) 和增量大于 \(x-S\) 的边的数量 \(e_2\)

    注意到 \(e_2\) 中的每条可以随便选择,而 \(e_1\) 中有一条和 \(\rm MST\) 上钦定的颜色不一样即可,方案数就是 \(2(2^{e_1}-1)2^{e_2}\)

    如果 \(x=S\) 还可以将 \(\rm MST\) 上的边染成不一样的颜色,其它边的颜色不再重要,方案数是 \((2^{n-1}-2)2^{m-n+1}\)

    求增量使用倍增,复杂度 \(\Theta((n+m)\log n)\)

    Code Display
    const int N=2010;
    struct edge{int u,v,w;}e[N];
    struct DSU{
    	int fa[N];
    	inline void init(int n){rep(i,1,n) fa[i]=i; return ;}
    	inline int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
    	inline void merge(int x,int y){fa[find(x)]=find(y); return ;}
    }Dsu;
    int n,m,X;
    bool mark[N];
    signed main(){
    	freopen("b.in","r",stdin); freopen("b.out","w",stdout);
    	n=read(); m=read(); X=read(); 
    	for(int i=1;i<=m;++i) e[i].u=read(),e[i].v=read(),e[i].w=read();
    	sort(e+1,e+m+1,[&](const edge a,const edge b){return a.w<b.w;});
    	Dsu.init(n);
    	vector<int> dep(n+1),val(n+1),fa(n+1);
    	vector<vector<pair<int,int> > >G(n+1);
    	int sum=0;
    	for(int i=1;i<=m;++i){
    		int x=Dsu.find(e[i].u),y=Dsu.find(e[i].v);
    		if(x==y) continue;
    		mark[i]=1;
    		Dsu.merge(x,y);
    		G[e[i].u].emplace_back(e[i].v,e[i].w);
    		G[e[i].v].emplace_back(e[i].u,e[i].w);
    		sum+=e[i].w;
    	}
    	if(X<sum) puts("0"),exit(0);
    	function<void(int,int)>dfs=[&](int x,int fat){
    		dep[x]=dep[fa[x]=fat]+1;
    		for(auto e:G[x]){
    			int t=e.fir; if(t==fat) continue;
    			val[t]=e.sec;
    			dfs(t,x);
    		}
    	};
    	dfs(1,0);
    	auto calc_max=[&](int u,int v){
    		int res=0;
    		if(dep[u]>dep[v]) swap(u,v);
    		while(dep[u]<dep[v]) res=max(res,val[v]),v=fa[v];
    		if(u==v) return res;
    		while(u!=v){
    			res=max(res,val[u]); u=fa[u];
    			res=max(res,val[v]); v=fa[v];
    		}
    		return res;
    	};
    	int cnt_sam=0,cnt_lar=0;
    	for(int i=1;i<=m;++i) if(!mark[i]){
    		int w=calc_max(e[i].u,e[i].v);
    		if(e[i].w-w>X-sum) cnt_lar++;
    		else if(e[i].w-w==X-sum) cnt_sam++;
    	}
    	int ans1=del(ksm(2,cnt_lar+cnt_sam+1),ksm(2,cnt_lar+1)),ans2=del(ksm(2,m),ksm(2,m-n+2));
    
    	if(sum==X) printf("%lld\n",add(ans1,ans2));
    	else printf("%lld\n",ans1);
    	return 0;
    }
    
    

    Network

    建立点分树,求出来子联通块中每个点到联通块的路径上边权最大最小值 \((l,r)\) 并提前排序,同时求出来 \(f_i\) 表示现在的服务器中到 \(i\) 的最大的 \(r-l\)

    二分答案,每次根据点分树上维护的信息判断哪些点不能成为被改变的点

    对于每个分治重心,如果利用同一棵子树中的点判定另一个点不合法是不劣的,因为极值的差不会变小

    先倒序扫描当前某个子联通块根处排好序的信息,并维护一个单调队列,其中包含使用 \(l\) 获得极值和使用 \(r\) 获得极值的限制,并且都是升序排序

    如果当前元素能满足队尾的使用 \(r-mid\) 获得极值的限制可以弹出,不能弹出者中如果存在 \(l+mid\) 大于当前 \(r\) 的情况则当前元素不合法,注意联通块的根也可能造成当前元素不合法

    如果当前元素需要其它元素的改变来满足 \(mid\) 的限制那么将其加入单调队列,由于是倒序扫描数组,那么可以对队首进行更新

    之后正序扫描,这次以判定 \(r-mid\) 是否 \(<l\) 作为标准即可,过程和上面的类似

    时间复杂度 \(\Theta(n\log n\log V)\)

    Code Display
    const int N=1e5+10;
    int n,typ[N];
    vector<pair<int,int> >G[N];
    int rt,cursum,siz[N],ff[N];
    bool vis[N];
    inline void findrt(int x,int fat){
    	ff[x]=0; siz[x]=1;
    	for(auto e:G[x]){
    		int t=e.fir; if(vis[t]||t==fat) continue;
    		findrt(t,x);
    		siz[x]+=siz[t];
    		ckmax(ff[x],siz[t]);
    	}
    	ckmax(ff[x],cursum-siz[x]);
    	if(ff[x]<ff[rt]) rt=x;
    	return ;
    }
    int f[N];
    vector<tuple<int,int,int> >inter[N];
    inline void build(int x){
    	vis[x]=1;
    	vector<vector<tuple<int,int,int> > >tmp;
    	vector<tuple<int,int,int> > sub;
    	function<void(int,int,int,int)>dfs=[&](int x,int fat,int Mn,int Mx){
    		sub.emplace_back(Mn,Mx,x);
    		for(auto e:G[x]){
    			int t=e.fir; if(vis[t]||t==fat) continue;
    			dfs(t,x,min(Mn,e.sec),max(Mx,e.sec));
    		}
    		return ;
    	};
    	for(auto e:G[x]){
    		int t=e.fir; if(vis[t]) continue;
    		sub.clear();
    		dfs(t,x,e.sec,e.sec);
    		tmp.emplace_back(sub);
    	}
    	int minl=1e9,maxr=0,mxlen=0;
    	for(auto &sub:tmp){
    		for(auto a:sub){
    			int id,L,R; tie(L,R,id)=a;
    			if(typ[id]) continue;
    			ckmax(f[id],max({R-minl,maxr-L,mxlen}));
    			if(maxr||typ[x]) ckmax(f[id],R-L);
    		}
    		for(auto a:sub){
    			int id,L,R; tie(L,R,id)=a;
    			if(!typ[id]) continue;
    			ckmax(maxr,R);
    			ckmin(minl,L);
    			ckmax(mxlen,R-L);
    		}
    	}
    	reverse(tmp.begin(),tmp.end());
    	minl=1e9; maxr=mxlen=0;
    	for(auto &sub:tmp){
    		for(auto a:sub){
    			int id,L,R; tie(L,R,id)=a;
    			if(typ[id]) continue;
    			ckmax(f[id],max({R-minl,maxr-L,mxlen}));
    			if(maxr||typ[x]) ckmax(f[id],R-L);
    		}
    		for(auto a:sub){
    			int id,L,R; tie(L,R,id)=a;
    			if(!typ[id]) continue;
    			ckmax(maxr,R);
    			ckmin(minl,L);
    			ckmax(mxlen,R-L);
    		}
    	}
    	if(!typ[x]) ckmax(f[x],mxlen);
    	for(auto &sub:tmp){
    		for(auto a:sub){
    			int id=get<2>(a);
    			if(!typ[id]) inter[x].emplace_back(a);
    		}
    	}
    	sort(inter[x].begin(),inter[x].end());
    	for(auto e:G[x]){
    		int t=e.fir; if(vis[t]) continue;
    		cursum=siz[t]; rt=0;
    		findrt(t,0);
    		build(rt);
    	}
    }
    bool legal[N];
    inline void solve(int x,int mid){
    	deque<pair<int,int> >q;
    	int minr=1e9;
    	for(auto iter=inter[x].rbegin();iter!=inter[x].rend();++iter){
    		int l,r,id; tie(l,r,id)=*iter;
    		while(q.size()&&q.back().first>=l) q.pop_back();
    		if(r-l<mid && q.size() && r<q.back().second) legal[id]=0;
    		if(!typ[x] && f[x]<mid && r-l<mid) legal[id]=0; 
    		if(f[id]<mid&&r<=minr){
    			minr=r;
    			while(q.size() && q.front().sec<l+mid) q.pop_front();
    			q.push_front({r-mid,l+mid});
    		}
    	}
    	q.clear();
    	for(auto iter:inter[x]){
    		int l,r,id; tie(l,r,id)=iter;
    		while(q.size()>=2&& q[1].first<l) q.pop_front();
    		if(r-l<mid && !q.empty() && q.front().sec>r && q.front().fir<l){
    			legal[id]=0;
    		}
    		if(f[id]<mid){
    			while(q.size() && q.back().fir>=r-mid) q.pop_back();
    			q.push_back({r-mid,l+mid});
    		}
    	}
    	if(!typ[x]){
    		for(auto iter:inter[x]){
    			int l,r,id; tie(l,r,id)=iter;
    			if(r-l<mid && f[id]<mid) legal[x]=0;
    		}
    	}
    	return ;
    }
    inline int check(int mid){
    	rep(i,1,n) legal[i]=1;
    	for(int i=1;i<=n;++i) solve(i,mid);
    	for(int i=1;i<=n;++i) if(!typ[i]&&legal[i]) return i;
    	return -1;
    }
    int main(){
    	freopen("c.in","r",stdin); freopen("c.out","w",stdout);
    	n=read(); 
    	for(int i=1;i<=n;++i) typ[i]=read();
    	for(int i=1;i<n;++i){
    		int u=read(),v=read(),w=read();
    		G[u].emplace_back(v,w);
    		G[v].emplace_back(u,w);
    	}
    	ff[0]=1e9; cursum=n;
    	findrt(1,0);
    	build(rt);
    	int l=0,r=1e9,val=0,node=0;
    	while(l<=r){
    		int mid=(l+r)>>1;
    		int pos=check(mid);
    		if(pos==-1) r=mid-1;
    		else{
    			l=mid+1;
    			val=mid;
    			node=pos;
    		}
    	}
    	if(val==1e9) val=0;
    	printf("%d %d\n",node,val);
    	return 0;
    }
    
    

  • 相关阅读:
    聊聊Spark的分区、并行度 —— 前奏篇
    深入探讨HBASE
    分布式流平台Kafka
    GeoServer中使用SLD样式
    OpenLayer修改WFS中的要素
    leaflet加载GeoServer的WFS服务
    OL实现属性查询的功能
    OL3实现空间查询的代码示例
    WFS—GetFeature方法
    OpenLayer+Geoserver+postgis实现路径分析
  • 原文地址:https://www.cnblogs.com/yspm/p/16367630.html
Copyright © 2020-2023  润新知