2.图论

图论/省选图论专题

开题顺序: K,C,Q,F,B,J,AC,R,E,A,AL,AH,O,N,L,S,AK,G,D,AJ

A CF19E Fairy

  • 观察到 n,m104 ,直接线段树分治即可,时间复杂度为 O(mlog2m)

    点击查看代码
    int n,ans[10010];
    pair<int,int>e[10010];
    struct quality
    {
    	int id,fa,siz;
    };
    struct DSU
    {
    	int fa[20010],siz[20010];
    	int find(int x)
    	{
    		return (fa[x]==x)?x:find(fa[x]);
    	}
    	void init(int n)
    	{
    		for(int i=1;i<=n;i++)
    		{
    			fa[i]=i;
    			siz[i]=1;
    		}
    	}
    	void merge(int x,int y,stack<quality>&s)
    	{
    		x=find(x);
    		y=find(y);
    		if(x!=y)
    		{
    			s.push((quality){x,fa[x],siz[x]});
    			s.push((quality){y,fa[y],siz[y]});
    			if(siz[x]<siz[y])
    			{
    				swap(x,y);
    			}
    			fa[y]=x;
    			siz[x]+=siz[y];
    		}
    	}
    	void split(stack<quality>&s)
    	{
    		while(s.empty()==0)
    		{
    			fa[s.top().id]=s.top().fa;
    			siz[s.top().id]=s.top().siz;
    			s.pop();
    		}
    	}
    }D;
    struct SMT
    {
    	struct SegmentTree
    	{
    		vector<int>info;
    	}tree[40010];
    	#define lson(rt) (rt<<1)
    	#define rson(rt) (rt<<1|1)
    	void update(int rt,int l,int r,int x,int y,int id)
    	{
    		if(x<=l&&r<=y)
    		{
    			tree[rt].info.push_back(id);
    			return;
    		}
    		int mid=(l+r)/2;
    		if(x<=mid)
    		{
    			update(lson(rt),l,mid,x,y,id);
    		}
    		if(y>mid)
    		{
    			update(rson(rt),mid+1,r,x,y,id);
    		}
    	}
    	void solve(int rt,int l,int r)
    	{
    		if(l>r)
    		{
    			return;
    		}
    		stack<quality>s;
    		int mid=(l+r)/2,x,y,flag=1;
    		for(int i=0;i<tree[rt].info.size();i++)
    		{
    			x=D.find(e[tree[rt].info[i]].first);
    			y=D.find(e[tree[rt].info[i]].second);
    			if(x==y)
    			{
    				flag=0;
    				break;
    			}
    			else
    			{
    				D.merge(e[tree[rt].info[i]].first,e[tree[rt].info[i]].second+n,s);
    				D.merge(e[tree[rt].info[i]].second,e[tree[rt].info[i]].first+n,s);
    			}
    		}
    		if(flag==0)
    		{
    			for(int i=l;i<=r;i++)
    			{
    				ans[i]=0;
    			}
    		}
    		else
    		{
    			if(l==r)
    			{
    				ans[l]=1;
    			}
    			else
    			{
    				solve(lson(rt),l,mid);
    				solve(rson(rt),mid+1,r);
    			}
    		}
    		D.split(s);
    	}
    
    }T;
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	int m,cnt=0,i;
    	cin>>n>>m;
    	for(i=1;i<=m;i++)
    	{
    		cin>>e[i].first>>e[i].second;
    		if(i-1>=1)
    		{
    			T.update(1,1,m,1,i-1,i);
    		}
    		if(i+1<=m)
    		{
    			T.update(1,1,m,i+1,m,i);
    		}
    	}
    	D.init(2*n);
    	T.solve(1,1,m);
    	for(i=1;i<=m;i++)
    	{
    		cnt+=ans[i];
    	}
    	cout<<cnt<<endl;
    	for(i=1;i<=m;i++)
    	{
    		if(ans[i]==1)
    		{
    			cout<<i<<" ";
    		}
    	}
    	return 0;
    }
    
  • 但实际上复杂度可以做到线性。二分图存在当且仅当不存在奇环。当图中不存在奇环时,所有边都可以删掉;否则,只能删掉所有奇环交集上不被偶环覆盖的边。树上差分维护奇环、偶环数量即可。

    点击查看代码
    struct node
    {
    	int nxt,to,id;
    }e[20010];
    int head[20010],d[2][20010],dep[20010],fa[20010],vis[20010],cnt=1,tot=0;
    vector<int>ans;
    void add(int u,int v,int id)
    {
    	cnt++;
    	e[cnt].nxt=head[u];
    	e[cnt].to=v;
    	e[cnt].id=id;
    	head[u]=cnt;
    }
    void dfs1(int x,int father,int last)
    {
    	fa[x]=e[last].id;
    	dep[x]=dep[father]+1;
    	for(int i=head[x];i!=0;i=e[i].nxt)
    	{
    		if(e[i].to!=father)
    		{
    			if(dep[e[i].to]==0)
    			{
    				vis[e[i].id]=1;
    				dfs1(e[i].to,x,i);
    			}
    			else
    			{
    				if(dep[x]>dep[e[i].to])
    				{
    					tot+=(dep[x]-dep[e[i].to]+1)%2;
    					d[(dep[x]-dep[e[i].to]+1)%2][e[i].id]++;
    					d[(dep[x]-dep[e[i].to]+1)%2][e[last].id]++;
    					d[(dep[x]-dep[e[i].to]+1)%2][fa[e[i].to]]--;
    				}
    			}
    		}
    	}
    }
    void dfs2(int x,int father,int last)
    {
    	for(int i=head[x];i!=0;i=e[i].nxt)
    	{
    		if(e[i].to!=father&&vis[e[i].id]==1)
    		{
    			dfs2(e[i].to,x,i);
    			d[0][e[last].id]+=d[0][e[i].id];
    			d[1][e[last].id]+=d[1][e[i].id];
    		}
    	}
    }
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif	
    	int n,m,u,v,i;
    	cin>>n>>m;
    	for(i=1;i<=m;i++)
    	{
    		cin>>u>>v;
    		add(u,v,i);
    		add(v,u,i);
    		ans.push_back(i);
    	}
    	for(i=1;i<=n;i++)
    	{
    		if(dep[i]==0)
    		{
    			dfs1(i,0,0);
    			dfs2(i,0,0);
    		}
    	}
    	if(tot!=0)
    	{
    		ans.clear();
    		for(i=1;i<=m;i++)
    		{
    			if(d[1][i]==tot&&d[0][i]==0)
    			{
    				ans.push_back(i);
    			}
    		}
    	}
    	cout<<ans.size()<<endl;
    	for(i=0;i<ans.size();i++)
    	{
    		cout<<ans[i]<<" ";
    	}
    	return 0;
    }
    

B CF412D Giving Awards

  • 建出反图后拓扑排序无法处理欠钱关系中存在环的情况,但可以借鉴其思路。

  • 仍考虑自下而上叫人,在叫完所有子节点后再加入答案即可。

    点击查看代码
    struct node
    {
    	int nxt,to;
    }e[200010];
    int head[200010],vis[200010],cnt=0;
    vector<int>ans;
    void add(int u,int v)
    {
    	cnt++;
    	e[cnt].nxt=head[u];
    	e[cnt].to=v;
    	head[u]=cnt;
    }
    void dfs(int x)
    {
    	vis[x]=1;
    	for(int i=head[x];i!=0;i=e[i].nxt)
    	{
    		if(vis[e[i].to]==0)
    		{
    			dfs(e[i].to);
    		}
    	}
    	ans.push_back(x);
    }
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	int n,m,u,v,i;
    	cin>>n>>m;
    	for(i=1;i<=m;i++)
    	{
    		cin>>u>>v;
    		add(u,v);
    	}
    	for(i=1;i<=n;i++)
    	{
    		if(vis[i]==0)
    		{
    			dfs(i);
    		}
    	}
    	for(i=0;i<ans.size();i++)
    	{
    		cout<<ans[i]<<" ";
    	}
    	return 0;
    }
    
    

C luogu P4151 [WC2011] 最大XOR和路径

D luogu P1477 [NOI2008] 假面舞会

  • 若没有环,最大答案显然为所有极大连通块内最长链的长度,最小答案为 3

  • 否则,最大答案为所有环长的 gcd 。同 luogu P4151 [WC2011] 最大XOR和路径 ,考虑通过 DFS 树上的路径拼接成环。此时最小答案即为最大答案的一个 3 的因子。

  • 难点在于环有相交的部分导致出现环套环了怎么处理。由更相减损法,不妨从 uv 连接一条权值为 1 的边,从 vu 连接一条权值为 1 的边。正确性显然。

    点击查看代码
    struct node
    {
    	int nxt,to,w;
    }e[2000010];
    int head[100010],dis[100010],vis[100010],maxx,minn,sum,cnt=0;
    void add(int u,int v,int w)
    {
    	cnt++;
    	e[cnt].nxt=head[u];
    	e[cnt].to=v;
    	e[cnt].w=w;
    	head[u]=cnt;
    }
    void dfs(int x,int d)
    {
    	if(dis[x]!=0)
    	{
    		sum=__gcd(sum,abs(d-dis[x]));
    		return;
    	}
    	dis[x]=d;  vis[x]=1;
    	maxx=max(maxx,d);  minn=min(minn,d);
    	for(int i=head[x];i!=0;i=e[i].nxt)  dfs(e[i].to,d+e[i].w);
    }
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	int n,m,u,v,ans=0,i;
    	cin>>n>>m;
    	for(i=1;i<=m;i++)
    	{
    		cin>>u>>v;
    		add(u,v,1);
    		add(v,u,-1);
    	}
    	for(i=1;i<=n;i++)
    	{
    		if(vis[i]==0)
    		{
    			maxx=0;  minn=0x7f7f7f7f;
    			dfs(i,0);
    			ans+=maxx-minn+1;
    		}
    	}
    	if(sum==0)
    	{
    		if(ans>=3)  cout<<ans<<" "<<3<<endl;
    		else  cout<<-1<<" "<<-1<<endl;
    	}
    	else
    	{
    		if(sum>=3)
    		{
    			for(i=3;i<=sum;i++)
    			{
    				if(sum%i==0)
    				{
    					cout<<sum<<" "<<i<<endl;
    					break;
    				}
    			}
    		}
    		else  cout<<-1<<" "<<-1<<endl;
    	}
    	return 0;
    }
    

E luogu P7025 [NWRRC2017] Grand Test

F luogu P4819 [中山市选] 杀人游戏

  • 缩完点后对所有入度为 0 的点询问一次即可。

  • 一开始询问若不是杀手,则可以顺次知道所在 DAG 内能到达的所有人是否是杀手;否则就被直接干掉了。

  • 形式化地,设最终有 c 个入度为 0 的点,每个点是杀手的概率是 1n1cn 即为所求。

  • 特别地,需要在缩点后存在入度为 0 大小(缩点后的大小)为 1 且能到达的点的入度都 2 的点时进行特判,此时可以不对这个点进行询问也可以知道这个点的身份。

  • 需要对边进行去重。

    点击查看代码
    struct node
    {
    	int nxt,to;
    }e[600010];
    int head[600010],dfn[600010],low[600010],ins[600010],col[600010],u[600010],v[600010],din[600010],siz[600010],tot=0,cnt=0,scc_cnt=0;
    stack<int>s;
    vector<int>E[600010];
    map<pair<int,int>,int> vis;
    void add(int u,int v)
    {
    	cnt++;
    	e[cnt].nxt=head[u];
    	e[cnt].to=v;
    	head[u]=cnt;
    }
    void tarjan(int x)
    {
    	tot++;
    	dfn[x]=low[x]=tot;
    	ins[x]=1;
    	s.push(x);
    	for(int i=head[x];i!=0;i=e[i].nxt)
    	{
    		if(dfn[e[i].to]==0)
    		{
    			tarjan(e[i].to);
    			low[x]=min(low[x],low[e[i].to]);
    		}
    		else
    		{
    			if(ins[e[i].to]==1)
    			{
    				low[x]=min(low[x],dfn[e[i].to]);
    			}
    		}
    	}
    	if(dfn[x]==low[x])
    	{
    		scc_cnt++;
    		int tmp=0;
    		while(x!=tmp)
    		{
    			tmp=s.top();
    			s.pop();
    			ins[tmp]=0;
    			col[tmp]=scc_cnt;
    			siz[scc_cnt]++;
    		}
    	}
    }
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	int n,m,ans=0,flag,i,j;
    	cin>>n>>m;
    	for(i=1;i<=m;i++)
    	{
    		cin>>u[i]>>v[i];
    		add(u[i],v[i]);
    	}
    	for(i=1;i<=n;i++)
    	{
    		if(dfn[i]==0)
    		{
    			tarjan(i);
    		}
    	}
    	for(i=1;i<=m;i++)
    	{
    		if(col[u[i]]!=col[v[i]]&&vis[make_pair(col[u[i]],col[v[i]])]==0)
    		{
    			vis[make_pair(col[u[i]],col[v[i]])]=1;
    			E[col[u[i]]].push_back(col[v[i]]);
    			din[col[v[i]]]++;
    		}
    	}
    	for(i=1;i<=scc_cnt;i++)
    	{
    		ans+=(din[i]==0);
    	}
    	for(i=1;i<=scc_cnt;i++)
    	{
    		if(din[i]==0&&siz[i]==1)
    		{
    			flag=1;
    			for(j=0;j<E[i].size();j++)
    			{
    				flag&=(din[E[i][j]]>=2);
    			}
    			if(flag==1)
    			{
    				ans--;
    				break;
    			}
    		}
    	}
    	printf("%.6lf\n",1.0-1.0*ans/n);
    	return 0;
    }
    
    

G luogu P4630 [APIO2018] 铁人两项

  • 固定 s,f 后可行的 c 的数量为 sf 的所有路径的并的点数 2 ,考虑通过圆方树计算。

  • 将圆方树上圆点的点权设为 1 ,方点的点权设为其所在点双大小,此时 c 的数量为圆方树上 sf 的点权和。

  • 现在只需要统计圆方树上所有路径的点权和了,简单树形 DP 处理一下即可。

    点击查看代码
    struct node
    {
    	ll nxt,to;
    }e[400010];
    ll head[100010],dfn[100010],low[100010],c[200010],siz[200010],v_dcc=0,tot=0,cnt=0,ans=0,n,nn;
    stack<ll>s;
    vector<ll>g[200010];
    void add(ll u,ll v)
    {
    	cnt++;
    	e[cnt].nxt=head[u];
    	e[cnt].to=v;
    	head[u]=cnt;
    }
    void tarjan(ll x)
    {
    	nn++;
    	tot++;
    	dfn[x]=low[x]=tot;
    	s.push(x);
    	c[x]=-1;
    	for(ll i=head[x];i!=0;i=e[i].nxt)
    	{
    		if(dfn[e[i].to]==0)
    		{
    			tarjan(e[i].to);
    			low[x]=min(low[x],low[e[i].to]);
    			if(dfn[x]==low[e[i].to])
    			{
    				v_dcc++;
    				g[v_dcc].push_back(x);
    				g[x].push_back(v_dcc);
    				c[v_dcc]++;
    				ll tmp=0;
    				while(e[i].to!=tmp)
    				{
    					tmp=s.top();
    					s.pop();
    					c[v_dcc]++;
    					g[v_dcc].push_back(tmp);
    					g[tmp].push_back(v_dcc);
    				}
    			}
    		}
    		else
    		{
    			low[x]=min(low[x],dfn[e[i].to]);
    		}
    	}
    }
    void dfs(ll x,ll fa)
    {
    	siz[x]=(x<=n);
    	for(ll i=0;i<g[x].size();i++)
    	{
    		if(g[x][i]!=fa)
    		{
    			dfs(g[x][i],x);
    			ans+=2*siz[x]*siz[g[x][i]]*c[x];
    			siz[x]+=siz[g[x][i]];
    		}
    	}
    	ans+=2*siz[x]*(nn-siz[x])*c[x];
    }
    int main()
    {
    //#define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	ll m,u,v,i;
    	cin>>n>>m;
    	for(i=1;i<=m;i++)
    	{
    		cin>>u>>v;
    		add(u,v);
    		add(v,u);
    	}
    	v_dcc=n;
    	for(i=1;i<=n;i++)
    	{
    		if(dfn[i]==0)
    		{
    			nn=0;
    			tarjan(i);
    			dfs(i,0);
    		}
    	}
    	cout<<ans<<endl;
    	return 0;
    }
    
    

H LibreOJ 546. 「LibreOJ β Round #7」网格图

I luogu P3783 [SDOI2017] 天才黑客

J luogu P3403 跳楼机

  • 同余最短路板子。

    • 同余最短路常利用同余性质构造状态来进行优化空间,并使用最短路进行辅助转移,形如 f((i+y)modx)=f(i)+y
    • 模数 x 的选择会影响建边的数量,以至于影响时空复杂度,实际应用时应尽可能选择较小的 x
  • 操作 4 没什么用,可以直接不管。

  • disi 表示仅通过操作 1,2 能到达的楼层中满足 modz=i 时的最小楼层,使用同余最短路进行转移。最终有 i=1n[disih]×(hdisiz+1) 即为所求。

  • 因本题中 h 较大,不妨先让 h 减一并钦定起始楼层为 0 楼即可。

    点击查看代码
    struct node
    {
    	ll nxt,to,w;
    }e[400010];
    ll head[400010],dis[400010],vis[400010],cnt=0;
    void add(ll u,ll v,ll w)
    {
    	cnt++;
    	e[cnt].nxt=head[u];
    	e[cnt].to=v;
    	e[cnt].w=w;
    	head[u]=cnt;
    }
    void dijkstra(ll s,ll h)
    {
    	priority_queue<pair<ll,ll> >q;
    	dis[s]=0;
    	q.push(make_pair(-dis[s],s));
    	while(q.empty()==0)
    	{
    		ll x=q.top().second;
    		q.pop();
    		if(vis[x]==0)
    		{
    			vis[x]=1;
    			for(ll i=head[x];i!=0;i=e[i].nxt)
    			{
    				if(dis[e[i].to]>dis[x]+e[i].w)
    				{
    					dis[e[i].to]=dis[x]+e[i].w;
    					q.push(make_pair(-dis[e[i].to],e[i].to));
    				}
    			}
    		}
    	}
    }
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	ll h,x,y,z,ans=0,i;
    	cin>>h>>x>>y>>z;
    	for(i=0;i<=z-1;i++)
    	{
    		add(i,(i+x)%z,x);
    		add(i,(i+y)%z,y);
    		dis[i]=h;
    	}
    	h--;
    	dijkstra(0,h);
    	for(i=0;i<=z-1;i++)
    	{
    		ans+=(dis[i]<=h)*((h-dis[i])/z+1);
    	}
    	cout<<ans<<endl;
    	return 0;
    }
    

K luogu P3275 [SCOI2011] 糖果

L luogu P7515 [省选联考 2021 A 卷] 矩阵游戏

  • 不妨先钦定第 n 行和第 m 列的元素均为 0 ,然后就得到了一组可行解,然后对其进行调整。

  • 观察到对某一行或某一列的元素第 i 位的变化量为 (1)ix 时,整个矩阵仍满足 {b} 的限制。

  • 设第 i 行、列的变化量分别为 ri,ci ,则需要满足 i[1,n],j[1,m],0ai,j+(1)iri+(1)jcj106 ,移项得到 ai,j(1)jri+(1)icj106ai,j

  • 差分约束处理不了 ri+cj/ricj 的情况,仍需进行调整。

  • 对于偶数列 jcj 取相反数,奇数行 iri 取相反数,此时的约束条件就只剩下了两个数相减的形式,差分约束即可。

  • 需要 SLF 优化 SPFA

    点击查看代码
    const ll inf=1000000;
    struct node
    {
    	ll nxt,to,w;
    }e[200010];
    ll head[610],a[310][310],b[310][310],dis[610],vis[610],num[610],cnt=0;
    void add(ll u,ll v,ll w)
    {
    	cnt++;
    	e[cnt].nxt=head[u];
    	e[cnt].to=v;
    	e[cnt].w=w;
    	head[u]=cnt;
    }
    bool spfa(ll s,ll n)
    {
    	memset(dis,0x3f,sizeof(dis));
    	memset(vis,0,sizeof(vis));
    	memset(num,0,sizeof(num));
    	deque<ll>q;
    	dis[s]=0;
    	vis[s]=1;
    	q.push_back(s);
    	while(q.empty()==0)
    	{
    		ll x=q.front();
    		vis[x]=0;
    		q.pop_front();
    		for(ll i=head[x];i!=0;i=e[i].nxt)
    		{
    			if(dis[e[i].to]>dis[x]+e[i].w)
    			{
    				dis[e[i].to]=dis[x]+e[i].w;
    				num[e[i].to]=num[x]+1;
    				if(num[e[i].to]>=n+1)
    				{
    					return false;
    				}
    				if(vis[e[i].to]==0)
    				{
    					vis[e[i].to]=1;
    					if(q.empty()==0&&dis[e[i].to]>=dis[q.front()])
    					{
    						q.push_back(e[i].to);
    					}
    					else
    					{
    						q.push_front(e[i].to);
    					}
    				}
    			}
    		}
    	}
    	return true;
    }
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	ll t,n,m,i,j,k;
    	scanf("%lld",&t);
    	for(k=1;k<=t;k++)
    	{
    		cnt=0;
    		memset(e,0,sizeof(e));
    		memset(head,0,sizeof(head));
    		memset(a,0,sizeof(a));
    		scanf("%lld%lld",&n,&m);
    		for(i=1;i<=n-1;i++)
    		{
    			for(j=1;j<=m-1;j++)
    			{
    				scanf("%lld",&b[i][j]);
    			}
    		}
    		for(i=n-1;i>=1;i--)
    		{
    			for(j=m-1;j>=1;j--)
    			{
    				a[i][j]=b[i][j]-a[i][j+1]-a[i+1][j]-a[i+1][j+1];
    			}
    		}
    		for(i=1;i<=n;i++)
    		{
    			for(j=1;j<=m;j++)
    			{
    				if((i+j)%2==0)
    				{
    					add(j+n,i,inf-a[i][j]);
    					add(i,j+n,a[i][j]);
    				}
    				else
    				{
    					add(i,j+n,inf-a[i][j]);
    					add(j+n,i,a[i][j]);
    				}
    			}
    		}
    		if(spfa(1,n+m)==true)
    		{
    			printf("YES\n");
    			for(i=1;i<=n;i++)
    			{
    				for(j=1;j<=m;j++)
    				{
    					printf("%lld ",a[i][j]+(((i+j)%2==0)?dis[i]-dis[j+n]:dis[j+n]-dis[i]));
    				}
    				printf("\n");
    			}
    		}
    		else
    		{
    			printf("NO\n");
    		}
    	}
    	return 0;
    }
    

M luogu P6965 [NEERC2016] Binary Code

N luogu P5332 [JSOI2019] 精准预测

O CF888G Xor-MST

  • 难点在于 Boruvka 的过程中如何求不同连通块间的最小边权。

  • 对全局建立一棵 01Trie ,再对每个连通块建一棵 01Trie 。枚举 ai 的过程中二者相减求出 aj 即可。

  • 为方便代码书写,不妨先对 {a} 进行去重。

    点击查看代码
    int a[200010];
    pair<int,int>g[200010];
    struct Trie
    {
    	int root[200010],rt_sum=0;
    	struct node
    	{
    		int ch[2],cnt,pos;
    	}tree[200010<<6];
    	int build_rt()
    	{
    		rt_sum++;
    		return rt_sum;
    	}
    	void insert(int &rt,int s,int id)
    	{
    		rt=(rt==0)?build_rt():rt;
    		int x=rt;
    		tree[x].cnt++;
    		for(int i=30;i>=0;i--)
    		{
    			if(tree[x].ch[(s>>i)&1]==0)
    			{
    				tree[x].ch[(s>>i)&1]=build_rt();
    			}
    			x=tree[x].ch[(s>>i)&1];
    			tree[x].cnt++;
    		}
    		tree[x].pos=id;
    	}
    	int merge(int rt1,int rt2)
    	{
    		if(rt1==0||rt2==0)
    		{
    			return rt1+rt2;
    		}
    		tree[rt1].cnt+=tree[rt2].cnt;
    		tree[rt1].ch[0]=merge(tree[rt1].ch[0],tree[rt2].ch[0]);
    		tree[rt1].ch[1]=merge(tree[rt1].ch[1],tree[rt2].ch[1]);
    		return rt1;
    	}
    	pair<int,int>query(int rt1,int rt2,int x)
    	{
    		int ans=0;
    		for(int i=30;i>=0;i--)
    		{
    			if(tree[tree[rt2].ch[(x>>i)&1]].cnt-tree[tree[rt1].ch[(x>>i)&1]].cnt>=1)
    			{
    				rt1=tree[rt1].ch[(x>>i)&1];
    				rt2=tree[rt2].ch[(x>>i)&1];
    			}
    			else
    			{
    				ans|=(1<<i);
    				rt1=tree[rt1].ch[((x>>i)&1)^1];
    				rt2=tree[rt2].ch[((x>>i)&1)^1];
    			}
    		}
    		return make_pair(ans,tree[rt2].pos);
    	}
    }T;
    struct DSU
    {
    	int fa[200010];
    	void init(int n)
    	{
    		for(int i=1;i<=n;i++)
    		{
    			fa[i]=i;
    		}
    	}
    	int find(int x)
    	{
    		return fa[x]==x?x:fa[x]=find(fa[x]);
    	}
    	void merge(int x,int y)
    	{
    		x=find(x);
    		y=find(y);
    		if(x!=y)
    		{
    			fa[x]=y;
    		}
    	}
    }D;
    ll boruvka(int n)
    {
    	ll ans=0;
    	int flag=1,x,y;
    	pair<int,int>tmp;
    	D.init(n);
    	while(flag==1)
    	{
    		flag=0;
    		fill(g+1,g+1+n,make_pair(0,0x7f7f7f7f));
    		for(int i=1;i<=n;i++)
    		{
    			x=D.find(i);
    			tmp=T.query(T.root[x],T.root[0],a[i]);
    			y=D.find(tmp.second);
    			if(x!=y&&tmp.first<g[x].second)
    			{
    				g[x]=make_pair(y,tmp.first);
    			}
    		}
    		for(int i=1;i<=n;i++)
    		{
    			x=D.find(i);
    			if(g[x].first!=0&&D.find(x)!=D.find(g[x].first))
    			{
    				y=D.find(g[x].first);
    				D.merge(x,g[x].first);
    				T.root[y]=T.merge(T.root[x],T.root[y]);
    				ans+=g[x].second;
    				flag=1;
    			}
    		}
    	}
    	return ans;
    }
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	int n,i;
    	cin>>n;
    	for(i=1;i<=n;i++)
    	{
    		cin>>a[i];
    	}
    	sort(a+1,a+1+n);
    	n=unique(a+1,a+1+n)-(a+1);
    	for(i=1;i<=n;i++)
    	{
    		T.insert(T.root[0],a[i],i);
    		T.insert(T.root[i],a[i],i);
    	}
    	cout<<boruvka(n)<<endl;
    	return 0;
    }
    

P CF141E Clearing Up

Q luogu P4768 [NOI2018] 归程

R SP41 WORDS1 - Play on Words

S luogu P6628 [省选联考 2020 B 卷] 丁香之路

  • 转化为对于每一个 i[1,n] ,寻找一个可重边集 E 使其包含 m 条关键边且有一条从 si 的欧拉路径,而这个边集中边权总和的最小值即为所求。

  • 欧拉路径还有起点和终点的限制,不妨先把 (s,i) 作为一条虚边加入 E ,即不参与最终答案统计,此时等价于满足存在一条从 si 的欧拉回路。

  • 为使尽可能让图连通,考虑用添加的虚边去拼接成最终情况的实边(关键边和实际为了使图连通加入的边)。

  • 对度数为奇数的点,按编号排序后相邻两个数先连一条实边(假如我们要连接 u,v 两点,加入的虚边为 {(i,i+1)|i[u,v1]} )使得这两个点的度数变成偶数。

  • 此时形成了若干个连通块且每个连通块内部已经形成了欧拉路径,为使其连通再跑一遍 Kruskal 即可,注意此时一去一回边权需要计算两遍。

    点击查看代码
    struct node
    {
    	ll from,to,w;
    };
    ll du[2510];
    vector<node>e;
    bool cmp(node a,node b)
    {
    	return a.w<b.w;
    }
    struct DSU
    {
    	ll fa[2510],tmp[2510];
    	void init(ll n)
    	{
    		for(ll i=1;i<=n;i++)
    		{
    			fa[i]=tmp[i];
    		}
    	}
    	void clear(ll n)
    	{
    		for(ll i=1;i<=n;i++)
    		{
    			fa[i]=i;
    		}
    	}
    	ll find(ll x)
    	{
    		return fa[x]==x?x:fa[x]=find(fa[x]);
    	}
    	void merge(ll x,ll y)
    	{
    		x=find(x);
    		y=find(y);
    		if(x!=y)
    		{
    			fa[x]=y;
    		}
    	}
    }D;
    ll kruskal()
    {
    	sort(e.begin(),e.end(),cmp);
    	ll ans=0,x,y;
    	for(ll i=0;i<e.size();i++)
    	{
    		x=D.find(e[i].from);
    		y=D.find(e[i].to);
    		if(x!=y)
    		{
    			D.fa[x]=y;
    			ans+=e[i].w;
    		}
    	}
    	return ans;
    }	
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	ll n,m,s,u,v,ans,sum=0,last,i,j,k;
    	cin>>n>>m>>s;
    	D.clear(n);
    	for(i=1;i<=m;i++)
    	{
    		cin>>u>>v;
    		du[u]++;
    		du[v]++;
    		sum+=abs(u-v);
    		D.merge(u,v);
    	}
    	for(i=1;i<=n;i++)
    	{
    		D.tmp[i]=D.fa[i];
    	}
    	for(i=1;i<=n;i++)
    	{
    		last=0;
    		ans=sum;
    		du[s]++;
    		du[i]++;
    		e.clear();
    		D.init(n);
    		for(j=1;j<=n;j++)
    		{
    			if(du[j]%2==1)
    			{
    				if(last==0)
    				{
    					last=j;
    				}
    				else
    				{
    					ans+=j-last;
    					for(k=last;k<=j-1;k++)
    					{
    						D.merge(k,k+1);
    					}
    					last=0;
    				}
    			}
    		}
    		for(j=1;j<=n;j++)
    		{
    			if(du[j]!=0)
    			{
    				if(last!=0&&D.find(last)!=D.find(j))
    				{
    					e.push_back((node){j,last,j-last});
    				}
    				last=j;
    			}
    		}
    		du[s]--;
    		du[i]--;
    		cout<<ans+2*kruskal()<<" ";
    	}
    	return 0;
    }
    

T luogu P6624 [省选联考 2020 A 卷] 作业题

U [AGC051D] C4

V luogu P6657 【模板】LGV 引理

W luogu P7736 [NOI2021] 路径交点

X luogu P7428 [THUPC2017] 母亲节的礼物

Y luogu P4386 [SHOI2015] 零件组装机

Z luogu P1173 [NOI2016] 网格

AA luogu P3687 [ZJOI2017] 仙人掌

AB luogu P3180 [HAOI2016] 地图

AC luogu P2371 [国家集训队] 墨墨的等式

  • 观察到 {b} 可差分,然后同余最短路维护即可。

    点击查看代码
    struct node
    {
    	ll nxt,to,w;
    }e[6000010];
    ll head[6000010],dis[6000010],vis[6000010],a[6000010],cnt=0;
    void add(ll u,ll v,ll w)
    {
    	cnt++;
    	e[cnt].nxt=head[u];
    	e[cnt].to=v;
    	e[cnt].w=w;
    	head[u]=cnt;
    }
    void dijkstra(ll s)
    {
    	memset(dis,0x3f,sizeof(dis));
    	memset(vis,0,sizeof(vis));
    	priority_queue<pair<ll,ll> >q;
    	dis[s]=0;
    	q.push(make_pair(-dis[s],s));
    	while(q.empty()==0)
    	{
    		ll x=q.top().second;
    		q.pop();
    		if(vis[x]==0)
    		{
    			vis[x]=1;
    			for(ll i=head[x];i!=0;i=e[i].nxt)
    			{
    				if(dis[e[i].to]>dis[x]+e[i].w)
    				{
    					dis[e[i].to]=dis[x]+e[i].w;
    					q.push(make_pair(-dis[e[i].to],e[i].to));
    				}
    			}
    		}
    	}
    }
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	ll n,l,r,ans=0,i,j;
    	cin>>n>>l>>r;
    	l--;
    	for(i=1;i<=n;i++)
    	{
    		cin>>a[i];
    		if(i>=2)
    		{
    			for(j=0;j<=a[1]-1;j++)
    			{
    				add(j,(j+a[i])%a[1],a[i]);
    			}
    		}
    	}
    	dijkstra(0);
    	for(i=0;i<=a[1]-1;i++)
    	{
    		ans+=(dis[i]<=r)*((r-dis[i])/a[1]+1);
    		ans-=(dis[i]<=l)*((l-dis[i])/a[1]+1);
    	}
    	cout<<ans<<endl;
    	return 0;
    }
    

AD CF1450E Capitalism

AE CF1416D Graph and Queries

AF luogu P3350 [ZJOI2016] 旅行者

AG LibreOJ 571. 「LibreOJ Round #11」Misaka Network 与 Accelerator

AH luogu P4180 [BJWC2010] 严格次小生成树

  • 随便找到一棵最小生成树后考虑非树边的替换。倍增或树剖套 ST 表维护最大值及严格次大值即可。

    点击查看代码
    struct node
    {
    	ll from,to,w;
    };
    ll fa[600010][25],dep[600010],vis[600010];
    pair<ll,ll>f[600010][25];
    vector<node>g;
    vector<pair<ll,ll> >e[600010];
    void add(ll u,ll v,ll w)
    {
    	e[u].push_back(make_pair(v,w));
    }
    bool cmp(node a,node b)
    {
    	return a.w<b.w;
    }
    struct DSU
    {
    	ll fa[100010];
    	void init(ll n)
    	{
    		for(ll i=1;i<=n;i++)
    		{
    			fa[i]=i;
    		}
    	}
    	ll find(ll x)
    	{
    		return fa[x]==x?x:fa[x]=find(fa[x]);
    	}
    }D;
    ll krsukal(ll n)
    {
    	sort(g.begin(),g.end(),cmp);
    	D.init(n);
    	ll ans=0,u,v;
    	for(ll i=0;i<g.size();i++)
    	{	
    		u=D.find(g[i].from);
    		v=D.find(g[i].to);
    		if(u!=v)
    		{
    			D.fa[u]=v;
    			ans+=g[i].w;
    			vis[i]=1;
    			add(g[i].from,g[i].to,g[i].w);
    			add(g[i].to,g[i].from,g[i].w);
    		}
    	}
    	return ans;
    }
    pair<ll,ll> add(pair<ll,ll>a,pair<ll,ll>b)
    {
    	pair<ll,ll> tmp;
    	tmp.first=max(a.first,b.first);
    	if(a.first>b.first)
    	{
    		tmp.second=max(a.second,b.first);
    	}
    	if(a.first==b.first)
    	{
    		tmp.second=max(a.second,b.second);
    	}
    	if(a.first<b.first)
    	{
    		tmp.second=max(a.first,b.second);
    	}
    	return tmp;
    }
    void dfs(ll x,ll father,ll w)
    {	
    	dep[x]=dep[father]+1;
    	fa[x][0]=father;
    	f[x][0]=make_pair(w,-0x3f3f3f3f);
    	for(ll i=1;i<=20;i++)
    	{
    		fa[x][i]=fa[fa[x][i-1]][i-1];
    		f[x][i]=add(f[x][i-1],f[fa[x][i-1]][i-1]);
    	}
    	for(ll i=0;i<e[x].size();i++)
    	{
    		if(e[x][i].first!=father)
    		{
    			dfs(e[x][i].first,x,e[x][i].second);
    		}
    	}
    }
    pair<ll,ll>lca(ll x,ll y)
    {
    	pair<ll,ll>maxx=make_pair(0,-0x3f3f3f3f);
    	if(dep[x]>dep[y])
    	{
    		swap(x,y);
    	}
    	for(int i=20;i>=0;i--)
    	{
    		if(dep[x]+(1<<i)<=dep[y])
    		{
    			maxx=add(maxx,f[y][i]);
    			y=fa[y][i];
    		}
    	}
    	if(x==y)
    	{
    		return maxx;
    	}
    	else
    	{
    		for(int i=20;i>=0;i--)	
    		{
    			if(fa[x][i]!=fa[y][i])
    			{
    				maxx=add(maxx,add(f[x][i],f[y][i]));
    				x=fa[x][i];
    				y=fa[y][i];
    			}
    		}
    		maxx=add(maxx,add(f[x][0],f[y][0]));
    		return maxx;
    	}
    }
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	ll n,m,u,v,w,sum,ans=0x7f7f7f7f7f7f7f7f,i;
    	pair<ll,ll>tmp;
    	cin>>n>>m;
    	for(i=1;i<=m;i++)
    	{
    		cin>>u>>v>>w;
    		g.push_back((node){u,v,w});
    	}
    	sum=krsukal(n);
    	dfs(1,0,0);
    	for(i=0;i<g.size();i++)
    	{
    		if(vis[i]==0)
    		{
    			tmp=lca(g[i].from,g[i].to);
    			if(tmp.first==g[i].w&&tmp.second!=-0x3f3f3f3f)
    			{
    				ans=min(ans,sum-tmp.second+g[i].w);
    			}
    			if(tmp.first<g[i].w&&tmp.first!=0)
    			{
    				ans=min(ans,sum-tmp.first+g[i].w);
    			}
    		}
    	}
    	cout<<ans<<endl;
    	return 0;
    }
    

AI luogu P2144 [FJOI2007] 轮状病毒

AJ luogu P5163 WD与地图

  • 考虑将删边转化为加边,线段树合并维护前 k 大的数之和。

  • 现在问题来到了如何快速地维护加边后强连通分量的变化。

  • 观察到每条边第一次被加入强连通分量的时间具有单调性,考虑整体二分。

  • 将出现时间 mid 的边加入一张新图中跑 Tarjan 进行缩点,为保证复杂度递归右区间时继承当前区间已经缩完点的图,递归左边的时候再撤销回去,可撤销并查集维护强连通分量即可。

  • 清空时只对连边时涉及到的两个端点进行清空。

    点击查看代码
    const ll inf=1000000000;
    struct edge
    {
    	ll u,v,t;
    	bool operator < (const edge &another) const
    	{
    		return t<another.t;
    	}
    }q[200010],tmp[2][200010];
    struct quality
    {
    	ll x,fa,siz;
    };
    ll c[200010],op[200010],a[200010],b[200010],dfn[200010],low[200010],ins[200010],tot=0;
    vector<ll>e[200010];
    vector<pair<ll,ll> >pos[200010];
    stack<ll>s,ans;
    map<pair<ll,ll>,ll>tim;
    void add(ll u,ll v)
    {
    	e[u].push_back(v);
    }
    struct SMT
    {
    	ll root[200010],rt_sum;
    	struct SegmentTree
    	{
    		ll ls,rs,siz,sum;
    	}tree[200010<<6];
    	#define lson(rt) (tree[rt].ls)
    	#define rson(rt) (tree[rt].rs)
    	void pushup(ll rt)
    	{
    		tree[rt].siz=tree[lson(rt)].siz+tree[rson(rt)].siz;
    		tree[rt].sum=tree[lson(rt)].sum+tree[rson(rt)].sum;
    	}
    	ll build_rt()
    	{
    		rt_sum++;
    		lson(rt_sum)=rson(rt_sum)=tree[rt_sum].siz=tree[rt_sum].sum=0;
    		return rt_sum;
    	}
    	void update(ll &rt,ll l,ll r,ll pos,ll val)
    	{
    		if(rt==0)  rt=build_rt();
    		if(l==r)
    		{
    			tree[rt].siz+=val;
    			tree[rt].sum+=val*l;
    			return;
    		}
    		ll mid=(l+r)/2;
    		if(pos<=mid)  update(lson(rt),l,mid,pos,val);
    		else  update(rson(rt),mid+1,r,pos,val);
    		pushup(rt);
    	}
    	ll merge(ll rt1,ll rt2,ll l,ll r)
    	{
    		if(rt1==0||rt2==0)  return rt1+rt2;
    		if(l==r)
    		{
    			tree[rt1].siz+=tree[rt2].siz;
    			tree[rt1].sum+=tree[rt2].sum;
    			return rt1;
    		}
    		ll mid=(l+r)/2;
    		lson(rt1)=merge(lson(rt1),lson(rt2),l,mid);
    		rson(rt1)=merge(rson(rt1),rson(rt2),mid+1,r);
    		pushup(rt1);
    		return rt1;
    	}
    	ll query(ll rt,ll l,ll r,ll k)
    	{
    		if(rt==0)  return 0;
    		if(tree[rt].siz<=k)  return tree[rt].sum;
    		if(l==r)  return min(k,tree[rt].siz)*l;
    		ll mid=(l+r)/2;
    		if(tree[rson(rt)].siz>=k)  return query(rson(rt),mid+1,r,k);
    		else  return tree[rson(rt)].sum+query(lson(rt),l,mid,k-tree[rson(rt)].siz);
    	}
    }T;
    struct DSU
    {
    	ll fa[200010],siz[200010];
    	void init(ll n)
    	{
    		for(ll i=1;i<=n;i++)  
    		{
    			fa[i]=i;  siz[i]=1;
    		}
    	}
    	ll find(ll x)
    	{
    		return fa[x]==x?x:find(fa[x]);
    	}
    	void merge(ll x,ll y,stack<quality>&s)
    	{
    		x=find(x);  y=find(y);
    		if(x!=y)
    		{
    			if(siz[x]<siz[y])  swap(x,y);
    			s.push((quality){x,fa[x],siz[x]});
    			s.push((quality){y,fa[y],siz[y]});
    			fa[y]=x;
    			siz[x]+=siz[y];
    		}
    	}
    	void _merge(ll x,ll y)
    	{
    		x=find(x);  y=find(y);
    		if(x!=y)
    		{
    			if(siz[x]<siz[y])  swap(x,y);
    			T.root[x]=T.merge(T.root[x],T.root[y],0,inf);
    			fa[y]=x;
    			siz[x]+=siz[y];
    		}
    	}
    	void split(stack<quality>&s)
    	{
    		while(s.empty()==0)
    		{
    			fa[s.top().x]=s.top().fa;
    			siz[s.top().x]=s.top().siz;
    			s.pop();
    		}
    	}
    }D;
    void clear(vector<ll>&v)
    {
    	tot=0;
    	while(s.empty()==0)  s.pop();
    	for(ll i=0;i<v.size();i++)  
    	{
    		dfn[v[i]]=low[v[i]]=ins[v[i]]=0;
    		e[v[i]].clear();
    	}
    }
    void tarjan(ll x,stack<quality>&_s)
    {
    	tot++;  dfn[x]=low[x]=tot;
    	s.push(x);  ins[x]=1;
    	for(ll i=0;i<e[x].size();i++)
    	{
    		if(dfn[e[x][i]]==0)
    		{
    			tarjan(e[x][i],_s);
    			low[x]=min(low[x],low[e[x][i]]);
    		}
    		else  if(ins[e[x][i]]==1)  low[x]=min(low[x],dfn[e[x][i]]);
    	}
    	if(dfn[x]==low[x])
    	{
    		int tmp=0;
    		while(tmp!=x)
    		{
    			tmp=s.top();
    			s.pop();  ins[tmp]=0;
    			D.merge(tmp,x,_s);
    		}
    	}
    }
    void solve(ll l,ll r,ll ql,ll qr)
    {
    	if(ql>qr)  return;
    	if(l==r)
    	{
    		for(ll i=ql;i<=qr;i++)  pos[l].push_back(make_pair(q[i].u,q[i].v));
    		return;
    	}
    	ll mid=(l+r)/2,x=0,y=0,_x,_y;
    	vector<ll>v; 
    	stack<quality>s;
    	for(ll i=ql;i<=qr;i++)
    	{
    		if(q[i].t<=mid)
    		{
    			_x=D.find(q[i].u);  _y=D.find(q[i].v);
    			if(_x!=_y) 
    			{
    				add(_x,_y);
    				v.push_back(_x);  v.push_back(_y); 
    			}
    		}
    	}
    	for(ll i=0;i<v.size();i++)  if(dfn[v[i]]==0)  tarjan(v[i],s);
    	for(ll i=ql;i<=qr;i++)
    	{
    		if(q[i].t<=mid&&D.find(q[i].u)==D.find(q[i].v))
    		{
    			x++;  tmp[0][x]=q[i];
    		}
    		else
    		{
    
    			y++;  tmp[1][y]=q[i];
    		}
    	}
    	for(ll i=1;i<=x;i++)  q[ql+i-1]=tmp[0][i];
    	for(ll i=1;i<=y;i++)  q[ql+x+i-1]=tmp[1][i];
    	clear(v);
    	solve(mid+1,r,ql+x,qr);
    	D.split(s);
    	solve(l,mid,ql,ql+x-1);
    }
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	ll n,m,k,x,i,j;
    	cin>>n>>m>>k;
    	for(i=1;i<=n;i++)  cin>>c[i];
    	for(i=1;i<=m;i++)  
    	{
    		cin>>q[i].u>>q[i].v;
    	}
    	for(i=1;i<=k;i++)
    	{
    		cin>>op[i]>>a[i]>>b[i];
    		if(op[i]==1)  tim[make_pair(a[i],b[i])]=k-i+1;
    		if(op[i]==2)  c[a[i]]+=b[i];
    	}
    	for(i=1;i<=m;i++)  q[i].t=tim[make_pair(q[i].u,q[i].v)];
    	sort(q+1,q+1+m);
    	D.init(n);  solve(0,k+1,1,m);
    	for(i=1;i<=n;i++)  T.update(T.root[i],0,inf,c[i],1);
    	D.init(n);  reverse(op+1,op+1+k);  reverse(a+1,a+1+k);  reverse(b+1,b+1+k);
    	for(i=0;i<=k;i++)
    	{
    		for(j=0;j<pos[i].size();j++)  D._merge(pos[i][j].first,pos[i][j].second);
    		if(op[i]==2)
    		{
    			x=D.find(a[i]);
    			T.update(T.root[x],0,inf,c[a[i]],-1);  c[a[i]]-=b[i];
    			T.update(T.root[x],0,inf,c[a[i]],1);
    		}
    		if(op[i]==3)  ans.push(T.query(T.root[D.find(a[i])],0,inf,b[i]));
    	}
    	for(;ans.empty()==0;ans.pop())  cout<<ans.top()<<endl;
    	return 0;
    }
    

AK BZOJ3331 压力

  • 在圆方树上做树上差分。

    点击查看代码
    struct node
    {
    	int nxt,to;
    }e[400010];
    int head[100010],dfn[100010],low[100010],siz[200010],son[200010],fa[200010],dep[200010],top[200010],d[200010],tot=0,cnt=0,v_dcc=0;
    vector<int>g[200010];
    stack<int>s;
    void add(int u,int v)
    {
    	cnt++;
    	e[cnt].nxt=head[u];
    	e[cnt].to=v;
    	head[u]=cnt;
    }
    void tarjan(int x)
    {
    	tot++;
    	dfn[x]=low[x]=tot;
    	s.push(x);
    	for(int i=head[x];i!=0;i=e[i].nxt)
    	{
    		if(dfn[e[i].to]==0)
    		{
    			tarjan(e[i].to);
    			low[x]=min(low[x],low[e[i].to]);
    			if(low[e[i].to]==dfn[x])
    			{
    				v_dcc++;
    				g[v_dcc].push_back(x);
    				g[x].push_back(v_dcc);
    				int tmp=0;
    				while(e[i].to!=tmp)
    				{
    					tmp=s.top();
    					s.pop();
    					g[v_dcc].push_back(tmp);
    					g[tmp].push_back(v_dcc);
    				}
    			}
    		}
    		else
    		{
    			low[x]=min(low[x],dfn[e[i].to]);
    		}
    	}
    }
    void dfs1(int x,int father)
    {
    	siz[x]=1;
    	fa[x]=father;
    	dep[x]=dep[father]+1;
    	for(int i=0;i<g[x].size();i++)
    	{
    		if(g[x][i]!=father)
    		{
    			dfs1(g[x][i],x);
    			siz[x]+=siz[g[x][i]];
    			son[x]=(siz[g[x][i]]>siz[son[x]])?g[x][i]:son[x];
    		}
    	}
    }
    void dfs2(int x,int id)
    {
    	top[x]=id;
    	if(son[x]!=0)
    	{
    		dfs2(son[x],id);
    		for(int i=0;i<g[x].size();i++)
    		{
    			if(g[x][i]!=fa[x]&&g[x][i]!=son[x])
    			{
    				dfs2(g[x][i],g[x][i]);
    			}
    		}
    	}
    }
    int lca(int u,int v)
    {
    	while(top[u]!=top[v])
    	{
    		if(dep[top[u]]>dep[top[v]])
    		{
    			u=fa[top[u]];
    		}
    		else
    		{
    			v=fa[top[v]];
    		}
    	}
    	return dep[u]<dep[v]?u:v;
    }
    void dfs(int x)
    {
    	for(int i=0;i<g[x].size();i++)
    	{
    		if(g[x][i]!=fa[x])
    		{
    			dfs(g[x][i]);
    			d[x]+=d[g[x][i]];
    		}
    	}
    }
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	int n,m,u,v,q,i;
    	cin>>n>>m>>q;
    	for(i=1;i<=m;i++)
    	{
    		cin>>u>>v;
    		add(u,v);
    		add(v,u);
    	}
    	v_dcc=n;
    	for(i=1;i<=n;i++)
    	{
    		if(dfn[i]==0)
    		{
    			tarjan(i);
    		}
    	}
    	dfs1(1,0);
    	dfs2(1,1);
    	for(i=1;i<=q;i++)
    	{
    		cin>>u>>v;
    		d[u]++;
    		d[v]++;
    		d[lca(u,v)]--;
    		d[fa[lca(u,v)]]--;
    	}
    	dfs(1);
    	for(i=1;i<=n;i++)
    	{
    		cout<<d[i]<<endl;
    	}
    	return 0;
    }
    

AL luogu P5631 最小mex生成树

AM luogu P5633 最小度限制生成树

posted @   hzoi_Shadow  阅读(34)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下
扩大
缩小
点击右上角即可分享
微信分享提示