• [WC2022] 杂题选讲钱易


    新年的聚会

    题目描述

    点此看题

    解法

    其实用分治的思想很容易解决聚会个数的限制,我们可以枚举一个点对其他点做分治,那么询问次数是 \(O(m\log n)\),但是这样做总人数不满足条件。

    关键结论:对于一个边数为 \(m\) 的图可以划分出 \(\sqrt m\) 个独立集。对于度数 \(\geq\sqrt m\) 的点可以单独划分成独立集,对于度数 \(<\sqrt m\) 的点,考虑邻接点颜色的 \(\tt mex\) 必然小于 \(\sqrt m\),所以可以根据 \(\tt mex\) 确定它的颜色(颜色就是指独立集标号)

    划分独立集可以对于每一个点枚举它划分在哪个独立集中,然后用 \(\tt meeting\) 函数检验。然后我们对于枚举两个独立集,用分治的方法来确定他们的边,也就是把较大的那个集合拆成两半,然后递归下去即可,出口就是两个集合大小都为 \(1\)

    复杂度显然是说不清楚的,但是上面是一个平衡复杂度的做法,如果不够放心可以在划分独立集的时候把所有点 \(\tt random\_shuffle\) 一遍。

    总结

    图论问题常用度数 \(\sqrt m\) 分类的方法来平衡复杂度

    #include <cstdio>
    #include <vector>
    #include <cstdlib>
    #include <algorithm>
    #include <ctime>
    #include "meeting.h"
    using namespace std;
    #define pii pair<int,int>
    #define pb push_back
    #define mp make_pair
    const int M = 1005;
    int cnt=0,p[M];
    vector<pii> ans;vector<int> s[M];
    void cdq(int x,int y,int xl,int xr,int yl,int yr)
    {
    	if(xr-xl<yr-yl) swap(x,y),swap(xl,yl),swap(xr,yr);
    	if(xl==xr && yl==yr)
    	{
    		ans.pb(mp(s[x][xl],s[y][yl]));
    		return ;
    	}
    	int mid=(xl+xr)>>1;vector<int> A,B;
    	for(int i=yl;i<=yr;i++)
    		A.pb(s[y][i]),B.pb(s[y][i]);
    	for(int i=xl;i<=xr;i++)
    		i<=mid?A.pb(s[x][i]):B.pb(s[x][i]);
    	if(!meeting(A)) cdq(x,y,xl,mid,yl,yr);
    	if(!meeting(B)) cdq(x,y,mid+1,xr,yl,yr);
    }
    vector<pii> solve(int n)
    {
    	srand(time(0));
    	for(int i=0;i<n;i++) p[i]=i;
    	random_shuffle(p,p+n);
    	for(int w=0;w<n;w++)
    	{
    		int fl=0,i=p[w];
    		for(int j=1;j<=cnt;j++)
    		{
    			s[j].pb(i);
    			if(meeting(s[j])) {fl=1;break;}
    			s[j].pop_back();
    		}
    		if(!fl) s[++cnt].pb(i);
    	}
    	for(int i=1;i<=cnt;i++)
    		for(int j=i+1;j<=cnt;j++)
    		{
    			int l1=s[i].size(),l2=s[j].size();
    			cdq(i,j,0,l1-1,0,l2-1);
    		}
    	return ans;
    }
    

    赶路

    题目描述

    点此看题

    解法

    猜测结论:原问题一定有解,我们考虑用类似归纳法的方法找出做法。

    考虑现在有一个固定起点和终点的集合 \(S\),我们找到一个分界向量 \(\vec v\),设起点和终点的向量是 \(\vec x\),那么对于剩下的点,如果它和起点连成的向量和 \(\vec x\)\(\vec v\) 的异侧,那么划分到 \(S_1\);否则我们把这个点划分到 \(S_2\)

    由于任意三点不共线,我们可以把原问题分成这样两个独立的子问题:起点和终点固定的 \(S_1\);起点和终点都固定的 \(S_2\),它们之间是绝对不会有边相交的,因为他们的凸包就不交。

    那么分界向量怎么选取呢?我们可以在随机取集合中的一个点,这样每次都可以对半分,所以时间复杂度 \(O(n\log n)\)

    总结

    可以用分治实现所谓归纳法,关键是划分成之间没有限制的两个集合。

    #include <cstdio>
    const int M = 505;
    #define int long long
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int T,n,p[M],L[M],R[M];
    struct node
    {
    	int x,y;
    	node(int X=0,int Y=0) : x(X) , y(Y) {}
    	node operator - (node b) {return node(x-b.x,y-b.y);}
    	int operator * (node b) {return x*b.y-y*b.x;}
    }a[M];
    int cross(node a,node b,node z) {return (z-a)*(z-b)>0;}
    void cdq(int l,int r)
    {
    	if(l>=r-1) return ;
    	int s=p[l],v=p[l+1],j=0,k=0;
    	int f=cross(a[s],a[v],a[p[r]]);
    	for(int i=l+2;i<r;i++)
    		if(cross(a[s],a[v],a[p[i]])^f) L[++j]=p[i];
    		else R[++k]=p[i];
    	int i=l,mid=0;
    	while(j) p[++i]=L[j--];
    	p[mid=++i]=v;
    	while(k) p[++i]=R[k--];
    	cdq(l,mid);cdq(mid,r);
    }
    void work()
    {
    	n=read();
    	for(int i=1;i<=n;i++)
    		a[i].x=read(),a[i].y=read(),p[i]=i;
    	cdq(1,n);
    	for(int i=1;i<=n;i++)
    		printf("%d ",p[i]);
    	puts("");
    }
    signed main()
    {
    	T=read();
    	while(T--) work();
    }
    

    Logistical Questions

    题目描述

    点此看题

    解法

    本题虽然是魔改的树上重心,但是仍然有一些比较好的性质。如果从一个点其子树方向调整,那么至多只有一个子树满足调整之后可能更优

    找出这个子树我们可以求导,只要套上一个点分治其他都可以暴力计算,时间复杂度 \(O(n\log n)\)

    #include <cstdio>
    #include <iostream>
    #include <cmath>
    using namespace std;
    const int M = 200005;
    #define db double
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int n,tot,rt,sz,id,f[M],siz[M],mx[M],vis[M];
    db sd,sum,ans,d[M],w[M];
    struct edge{int v,c,next;}e[M<<1];
    void findrt(int u,int fa)
    {
    	siz[u]=1;mx[u]=0;
    	for(int i=f[u];i;i=e[i].next)
    	{
    		int v=e[i].v;
    		if(v==fa || vis[v]) continue;
    		findrt(v,u);
    		siz[u]+=siz[v];
    		mx[u]=max(mx[u],siz[v]);
    	}
    	mx[u]=max(mx[u],sz-siz[u]);
    	if(mx[u]<mx[rt]) rt=u;
    }
    void calc(int u,int fa,int o,db c)
    {
    	sum+=pow(c,1.5)*w[u];
    	d[o]+=pow(c,0.5)*1.5*w[u];
    	for(int i=f[u];i;i=e[i].next)
    	{
    		int v=e[i].v;
    		if(v==fa) continue;
    		calc(v,u,o,e[i].c+c);
    	}
    }
    void solve(int u)
    {
    	if(vis[u]) return;vis[u]=1;
    	sum=sd=0;
    	for(int i=f[u];i;i=e[i].next)
    	{
    		int v=e[i].v;d[v]=0;
    		calc(v,u,v,e[i].c);sd+=d[v];
    	}
    	if(ans<0 || sum<ans) id=u,ans=sum;
    	for(int i=f[u];i;i=e[i].next)
    	{
    		int v=e[i].v;
    		if(sd-2*d[v]>=0) continue;
    		rt=0;sz=siz[v];
    		findrt(v,u);solve(rt);
    	}
    }
    signed main()
    {
    	n=read();
    	for(int i=1;i<=n;i++)
    		w[i]=read();
    	for(int i=1;i<n;i++)
    	{
    		int u=read(),v=read(),c=read();
    		e[++tot]=edge{v,c,f[u]},f[u]=tot;
    		e[++tot]=edge{u,c,f[v]},f[v]=tot;
    	}
    	ans=-1;mx[0]=sz=n;
    	findrt(1,0);solve(1);
    	printf("%d %.10f\n",id,ans);
    }
    
  • 相关阅读:
    SpringBoot快速入门(三)
    分布式解决方案
    计算机网络-自顶向下方法第六章
    计算机网络-自顶向下方法第四章
    springcloud记录
    计算机网络-自顶向下方法第三章
    springboot整合kafka
    计算机网络-自顶向下方法第二章
    计算机网络-自顶向下方法第一章
    Spring常用注解【经典总结】
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/15856601.html
Copyright © 2020-2023  润新知