拟阵交 学习笔记

总觉得在大考前学习新知识点不太好。
模拟赛考了一道拟阵交。。。。。
拟阵是一个集合的集合(二元组\((S,U)\)其中\(S\)是集合内集合元素的取值,\(U\)是集合内的集合),满足2条公理:
遗传性:若拟阵中有一集合\(S\),则\(S-{x}\)依然在拟阵中
交换性:若有二集合\(S1,S2\)\(card(S1)<card(S2)\)
则存在一元素\(x\)\(S2-S1\)中且\(S1+x\)仍然属于拟阵。
定义基:拟阵中加入任何元素都不在拟阵的集合
定义环:不在拟阵中,且任意删除一个元素后在拟阵中的集合。
引理1:所有基的大小相同
引理2:如果存在两个不同的基\(A,B\),令\(x\)\(A-B\)的任意元素,\(y\)\(B-A\)的任意元素
\(A-{x}+{y}\)在拟阵中。
引理3:如果存在2个环\(X\),\(Y\)\(X\)属于\(Y\),则\(X=Y\)
引理4:如果有两个环\(X\),\(Y\)\(e\)属于\(X\)\(Y\)的交集,则存在一环\(c\)属于\(X+Y-e\)
引理5:令\(I\)是拟阵的一个基,如果\(x\)元素不属于\(I\),则\(I+x\)只有一个环子集
拟阵上的最优化:
把所有元素从大到小排序后,能加就加,不形成环就加入。
可以证明正确性
秩函数:定义一个拟阵的秩为拟阵的一个极大独立集\(U\)的大小
引理6:对于所有在拟阵中的\(U\)\(0\leq r(U)\leq card(U)\)
引理7:对于任意集合\(A\)属于\(B\)属于\(S\)\(r(A)\leq r(B)\)
引理8:对于任意集合\(A,B\supset S\)\(r(A\cup B)+r(A\cap B)\leq r(A)+r(B)\)
拟阵交:
给定两个拟阵,求他们的\(U\)值的交的最大(权)独立集
考虑增量法。
每次从一个答案为\(X\)的集合到答案为\(X+1\)的集合。
考虑一个二分图:把元素看作一个点。
左边表示已经被拓展的集合\(A\),右边表示未被拓展的集合\(B\)
左边\(a\)向右边\(b\)连有向边,只有\(A-a+b\)是独立集
右边\(b\)向左边\(a\)连有向边,只有\(B-b+a\)是独立集
找到集合\(C,D\)\(C\)是满足以下条件的点集合:
\(A+x\)满足第一个拟阵的条件,\(x\)\(C\)
\(A+y\)满足第二个拟阵的条件,\(y\)\(D\)中。
\(C->D\)的最短路,把最短路上元素的选择状态反转就是答案。

#include<bits/stdc++.h>
using namespace std;
#define N 1010
int d[N],pr[N],v1[N],v2[N],n,m,x[N],y[N],z[N],f[N],k,c[N],v3[N],v4[N],tc[N];
vector<int>v[N];
int fd(int x){
	return f[x]==x?x:f[x]=fd(f[x]);
}
int bfs(){
	queue<int>q;
	for(int i=0;i<=m;i++){
		d[i]=1e9;
		pr[i]=0;
	}
	for(int i=1;i<=m;i++)
		if(v3[i]){
			q.push(i);
			d[i]=0;
		}
	while(!q.empty()){
		int x=q.front();
		q.pop();
		for(int y:v[x])
			if(d[y]>d[x]+1){
				d[y]=d[x]+1;
				q.push(y);
				pr[y]=x;
			}
	}
	int ans=0;
	for(int i=1;i<=m;i++)
		if(v4[i]&&d[ans]>d[i])
			ans=i;
	if(d[ans]>1e8)
		return 0;
	return ans;
}
int main(){
	freopen("forget.in","r",stdin);
	freopen("forget.out","w",stdout);
	scanf("%d%d%d",&n,&m,&k);
	for(int i=1;i<=k;i++)
		scanf("%d",&c[i]);
	for(int i=1;i<=m;i++)
		scanf("%d%d%d",&x[i],&y[i],&z[i]);
	for(int i=1;i<=m;i++)
		v2[i]=1;
	int ans=0;
	for(int i=1;i<n;i++){
		for(int j=1;j<=m;j++)
			v[j].clear();
		for(int j=1;j<=m;j++)
			if(v1[j]){
				for(int l=1;l<=n;l++)
					f[l]=l;
				for(int l=1;l<=m;l++)
					if(v1[l]&&l!=j){
						int xx=fd(x[l]),yy=fd(y[l]);
						if(xx!=yy)
							f[xx]=yy;
					}
				for(int l=1;l<=m;l++)
					if(v2[l]){
						int xx=fd(x[l]),yy=fd(y[l]);
						if(xx!=yy)
							v[j].push_back(l);
					}
			}
		for(int j=1;j<=m;j++)
			if(v2[j]){
				for(int l=1;l<=m;l++)
					if(v1[l]){
						tc[z[l]]--;
						if(tc[z[j]]<c[z[j]])
							v[j].push_back(l);
						tc[z[l]]++;
					}
			}
		for(int j=1;j<=n;j++)
			f[j]=j;
		for(int j=1;j<=m;j++)
			if(v1[j]){
				int xx=fd(x[j]),yy=fd(y[j]);
				if(xx!=yy)
					f[xx]=yy;
			}
		for(int j=1;j<=m;j++){
			if(v2[j]&&fd(x[j])!=fd(y[j]))
				v3[j]=1;
			else
				v3[j]=0;
			if(v2[j]&&tc[z[j]]<c[z[j]])
				v4[j]=1;
			else
				v4[j]=0;
		}
		int po=bfs(),ok=0;
		if(!po)
			break;
		ans=i;
		while(po){
			if(!ok){
				v2[po]=0;
				v1[po]=1;
				tc[z[po]]++;
			}
			else{
				v2[po]=1;
				v1[po]=0;
				tc[z[po]]--;
			}
			ok^=1;
			po=pr[po];
		}
	}
	printf("%d\n",m-ans);
	for(int i=1;i<=m;i++)
		if(v2[i])
			printf("%d ",i);
}
posted @ 2021-01-24 00:03  celerity1  阅读(431)  评论(0编辑  收藏  举报