QOJ 3804 Distributing the Treasure 题解

QOJ 3804 Distributing the Treasure 题解


知识点

构造,二分图,DAG,最长路。

分析

首先有个很明显的特判:\(n\ge m\) 时,输出 \(1\sim m\) 即可。那么排除了上述情况后,发现 \(n\le m\le \sqrt{2\times 10^5}<448\)

构造一张二分图,左边 \(n\) 个是人,右边是 \(n\) 个选取物品的集合。

为什么要这么做呢?考虑 \(n=2\) 的情况,发现当现在是一个合法的局面,而加入一个无论放在哪边都不能够合法的物品时,我们只要把两个人之前选的所有物品都交换,这个物品就变成了无论放在哪边都能够合法。这个可以扩展到 \(n>2\) 的情况。

所以我们在二分图上维护完美匹配,每个人对应一个集合,在过程中我们会交换集合,以及在某个集合中插入物品。所有物品都插入后,每个集合对应的人就是答案。

\(A\prec B\) 表示 \(v(A,S_A)<v(A,S_B)\)

假设存在 \(x\prec y\),那么我们加入物品时,如果将它给了 \(y\),那么 \(x\) 就会 angry。所以我们只能把物品给一个不存在 \(x\) 满足 \(x\prec y\)\(y\)

但是当不存在这样的 \(y\),我们就无法加入,要尝试交换集合来使这样的 \(y\) 出现。那么肯定存在轮换 \((a_1,a_2,\ldots,a_k)\),使轮换后匹配总权值会变大,这给我们提供了一个思路:如果匹配是二分图最大权完美匹配,那么就一定存在 \(y\) 给我们加入值。所以我们可以维护二分图最大权完美匹配,用 KM 算法可以得到 \(O(mn^3)\) 的算法,相当于 \((2\times 10^5)^2\),不能过(官解说如果你转换成费用流用预流推进就可以水过啦!!!虽然复杂度不变)。

考虑优化,发现由于每次加入后我们都会更新至二分图最大权完美匹配,那么如果存在上述的轮换使其总权值变大,肯定包括新加入的这个物品所在的集合 \(S\)。所以我们如果把 \(S\) 这个集合从图中删掉,再把 \(x\prec y\) 的边连起来,那么就变成了一张 DAG,跑一遍最长路,再把 \(S\) 加入,就可以找到那个最大轮换,轮换之后又变成了二分图最大权完美匹配,单次时间是 \(O(n^2)\) 的。

所以最终时间复杂度是 \(O(mn^2)\),上界 \((2\times 10^5)^{1.5}\)

当然这题还有一个做法:每次贪心暴力找 \(\prec\) 轮换,也可以水过,而且非常快,不过理论上界是 \(O(mn^3)\)

代码

constexpr int N(447+10),M(2e5+10);
constexpr ll LINF(1e18);

int n,m;
int ans[M],bel[N],lst[N],deg[N];
ll dis[N];
Vi a[N],g[N],res[N];
Vl st[N];

signed main() {
#ifdef Plus_Cat
	freopen(Plus_Cat ".in","r",stdin),freopen(Plus_Cat ".out","w",stdout);
#endif
	I(n,m);
	if(n>=m) {
		FOR(i,1,m)O(i,' ');
		return 0;
	}
	FOR(i,1,n)a[i].resize(m+1,0),st[i].resize(n+1,0),bel[i]=i;
	FOR(i,1,n)FOR(j,1,m)I(a[i][j]);
	FOR(t,1,m) {
		int pos(0);
		FOR(i,1,n) {
			bool flag(false);
			FOR(j,1,n)if(st[j][bel[j]]<st[j][bel[i]]&&(flag=true))break;
			if(!flag&&(pos=i,true))break;
		}
		res[bel[pos]].push_back(t);
		FOR(i,1,n)st[i][bel[pos]]+=a[i][t],g[i].clear(),deg[i]=0;
		FOR(i,1,n)FOR(j,1,n)if(j!=pos&&st[i][bel[i]]<st[i][bel[j]])g[i].push_back(j),++deg[j];
		queue<int> q;
		RCL(lst+1,0,int,n),RCL(dis+1,-0x3f,ll,n),dis[pos]=0;
		FOR(i,1,n)if(!deg[i])q.push(i);
		while(!q.empty()) {
			int u(q.front());
			q.pop();
			for(int v:g[u]) {
				if(dis[v]<dis[u]-st[u][bel[u]]+st[u][bel[v]])
					dis[v]=dis[u]-st[u][bel[u]]+st[u][bel[v]],lst[v]=u;
				if(!--deg[v])q.push(v);
			}
		}
		int u(0);
		ll mx(-LINF);
		FOR(i,1,n)if(st[i][bel[i]]<st[i][bel[pos]]&&mx<dis[i]-st[i][bel[i]]+st[i][bel[pos]])
			mx=dis[i]-st[i][bel[i]]+st[i][bel[pos]],u=i;
		for(int cur(bel[pos]); u; u=lst[u])swap(cur,bel[u]);
	}
	FOR(i,1,n)for(int x:res[bel[i]])ans[x]=i;
	FOR(i,1,m)O(ans[i],' ');
	return 0;
}

posted @ 2025-09-03 22:09  Add_Catalyst  阅读(8)  评论(0)    收藏  举报