2022年7月练习

7月1日~7月3日

专心贺网络流


7月4日~5日(两天做一道题我好菜呀)

P4768 [NOI2018] 归程

挺经典的 kruskal 重构树题。
考虑将边按高度从大到小排序,从前往后连边,弄一颗 kruskal 生成树,点与点的关系用并查集维护。
由这棵树是小根堆的性质,可得每一个点与其父亲相隔边海拔一定不小于与其儿子相隔边海拔,你若能到达一个点,就一定能到达其所在子树的所有点。
提前用 dijkstra 处理 1 到各点的花费,随后对于每个询问,用倍增向上翻点,加上海拔 \(>p\) 的限制即可。
code


7月6日~7日

P4899 [IOI2018] werewolf 狼人

问你在一幅图中能否找到一条起点 \(s\) ,终点 \(t\) 的路线及数值 \(l,r\)\(s,t,l,r\) 固定),使得路线中存在点 \(u\) ,使得 \([s,u] \ge l , [u,t] \le r\)
对于两个条件,按每边两点中最大最小值大小为条件对边排序,分别建出 kruskal 重构树,将 \(s,t\) 倍增到在自己范围内的边界位置。
判断在两棵树内可走到的点集是否有交,用主席树二位数点(虽然我也不是很理解)。

code(未在atc上ac)
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define rgi register int
#define FOR(i,a,b) for(int i=a;i<=b;i++)
#define ROF(i,a,b) for(int i=a;i>=b;i--)
const int M=1e6+7,inf=1e9+7,modd=998244353;
inline int read(){
	int w=0,r=1;char c=getchar();
	while(!(isdigit(c)||c=='-'))c=getchar();
	if(c=='-')r=-1,c=getchar();
	while(isdigit(c))w=w*10+c-'0',c=getchar();
	return w*r;
}
int n,m,q; 
int fa[M];
int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
struct ee{int fr,tr;}a[M*2],b[M*2];
bool cmp1(ee aa,ee bb){return -min(aa.fr,aa.tr)<-min(bb.fr,bb.tr);}
bool cmp2(ee aa,ee bb){return max(aa.fr,aa.tr)<max(bb.fr,bb.tr);}
int st,ed,lx,rx,nfd[M*2];
struct syt{
	int cnt,ct,dfn[M*2],ctl[M*2],ctr[M*2],lim[M*2];
	vector<int>edge[M*2];
	int up[21][M];
	void dfs(int u,int fa){
//		printf("****%d %d %d\n",u,fa,edge[u].size());
		up[0][u]=fa;
		if(u<=n)ctl[u]=++ct,dfn[ct]=u,lim[u]=u,nfd[u]=ct;
		else ctl[u]=ct+1;
		FOR(i,1,20)up[i][u]=up[i-1][up[i-1][u]];
		if(edge[u].size())ROF(i,edge[u].size()-1,0){
			int kid=edge[u][i];
			if(kid==fa)continue;
			dfs(kid,u);
		}
		ctr[u]=ct;
	}
	int fd1(int u){
		for(int i=20;i>=0;i--)if(up[i][u]&&lim[up[i][u]]>=lx){
//			printf("fd1 u=%d lim is %d,grand is %d,lim is %d ,while the lx is %d\n",u,lim[u],up[i][u],lim[up[i][u]],lx);
			u=up[i][u];
		}
		return u;
	}
	int fd2(int u){
		for(int i=20;i>=0;i--)if(up[i][u]&&lim[up[i][u]]<=rx){
//			printf("fd2 u=%d lim is %d,grand is %d,lim is %d ,while the rx is %d\n",u,lim[u],up[i][u],lim[up[i][u]],rx);
			u=up[i][u];
		}
		return u;
	}
}s1,s2;
int root[M*2];
struct sgt{
	int siz[M*10],ls[M*10],rs[M*10],tot;
	void insert(int &u,int uu,int l,int r,int x){
		if(!u)u=++tot;
		siz[u]=siz[uu]+1;
		if(l==r)return;
		int mid=(l+r)>>1;
		if(x<=mid)rs[u]=rs[uu],insert(ls[u],ls[uu],l,mid,x);
		else ls[u]=ls[uu],insert(rs[u],rs[uu],mid+1,r,x);
	}
	int query(int u,int uu,int l,int r,int x,int y){
		if(x<=l&&r<=y)return siz[uu]-siz[u];
		int mid=(l+r)>>1,ans=0;
		if(x<=mid)ans+=query(ls[u],ls[uu],l,mid,x,y);
		if(y>mid)ans+=query(rs[u],rs[uu],mid+1,r,x,y);
		return ans;
	}
}T;
void kruskal(){
	FOR(i,1,m)a[i]=b[i];
	sort(a+1,a+m+1,cmp1);
	s1.cnt=n;
	FOR(i,1,n<<1)fa[i]=i; 
	FOR(i,1,m){
		int faa=find(a[i].fr),fbb=find(a[i].tr);
		
		if(faa!=fbb){
//			printf("*%d           %d\n",faa,fbb);
			fa[faa]=fa[fbb]=++s1.cnt;
			s1.lim[s1.cnt]=min(a[i].fr,a[i].tr);
			s1.edge[s1.cnt].push_back(faa);
			s1.edge[s1.cnt].push_back(fbb);
		}
		if(s1.cnt==(n<<1)-1)break;
	}
	s1.dfs(s1.cnt,0);
	FOR(i,1,m)a[i]=b[i];
	sort(a+1,a+m+1,cmp2);
	FOR(i,1,n<<1)fa[i]=i; 
	s2.cnt=n;
	FOR(i,1,m){
		int faa=find(a[i].fr),fbb=find(a[i].tr);
		if(faa!=fbb){
//			printf("**%d           %d\n",faa,fbb);
			fa[faa]=fa[fbb]=++s2.cnt;
			s2.lim[s2.cnt]=max(a[i].fr,a[i].tr);
			s2.edge[s2.cnt].push_back(faa);
			s2.edge[s2.cnt].push_back(fbb);
		}
		if(s2.cnt==(n<<1)-1)break;
	}
	s2.dfs(s2.cnt,0);
}
void init(){
	n=read(),m=read(),q=read();
	FOR(i,1,m){
		int fr=read()+1,tr=read()+1;
		b[i].fr=fr,b[i].tr=tr;
	}
}
void work(){
	kruskal();
	FOR(i,1,n)nfd[s1.dfn[i]]=i;
	FOR(i,1,n)T.insert(root[i],root[i-1],1,n,nfd[s2.dfn[i]]);//,printf("%d\n",s1.nfd[s2.dfn[i]]);
	while(q--){
		st=read()+1,ed=read()+1,lx=read()+1,rx=read()+1;
		st=s1.fd1(st),ed=s2.fd2(ed);
//		printf("%d %d %d %d\n",root[s2.ctl[ed]-1],root[s2.ctr[ed]],s1.ctl[st],s1.ctr[st]);
		printf("%d\n",T.query(root[s2.ctl[ed]-1],root[s2.ctr[ed]],1,n,s1.ctl[st],s1.ctr[st])>0);
	}
} 
int main(){
//	freopen("text.in","r",stdin);
//	freopen("text.out","w",stdout);
	init();
	work();
	return 0;
}
/*
6 6 3
5 1
1 2
1 3
3 4
3 0
5 2
4 2 1 2
4 2 2 2
5 4 3 4
*/

7月10日

矩阵数定理的学习(机房里其他神犇都是五天前就学了的)
设一个 \(n\) 个点的图有一个对应的 \(n\)\(n\) 列的拉普拉斯矩阵 \(L\),该矩阵满足:
\(L_{i,i}=deg(i),L_{i,j}=0,i\ne j\)
再将其减去邻接矩阵,得出最终的拉普拉斯矩阵 \(L'\),随便减去一行一列,将其写成行列式形式,对角线数的积就是该图的生成树数量。
证明过程我不知道。
写成行列式形式过程类似于高斯消元,但过程中会用到分数,所以要用辗转相除法。
一些特殊情况:

  1. 求生成树的边权总和时,点的度数变为点相邻边的权值和,邻接矩阵变为边权的相反数;
  2. 图有向时,点的度数变为点相邻边的权值和,可求外向树(由根向外),点的度数变为点相邻边的权值和,可求内向树(由外向根)
  3. 有向则有指定根,所减去的行列均为指定根所在的行列。

一些板子题:
7.10:[HEOI2015]小 Z 的房间P6178 【模板】Matrix-Tree 定理P3317 [SDOI2014]重建
7.11:P4455 [CQOI2018]社交网络darkbzoj#4894. 天赋
7.12:P4208 [JSOI2008]最小生成树计数
注:该题需要枚举边权,将该类边权的边从最小生成树中移除,再对于所有连接各连通块的该类边做矩阵树定理。
7.13:P4336 [SHOI2016]黑暗前的幻想乡(矩阵树+容斥)
7.14:P3349 [ZJOI2016]小星星(容斥)P5029 T'ill It's Over(线段树优化建图网络流)

posted @ 2022-07-01 15:06  wcy2006  阅读(40)  评论(0)    收藏  举报