CodeForces 274D - Lovely Matrix

难度只有\(^*2100\)?这难度虚低吧??只是个蓝题?这是恶评吧??

我想了足足两个晚上都没想出来,可能是因为我是弱弱吧/kk

洛谷题目页面传送门 & CodeForces题目页面传送门

给定一个\(n\times m\)的矩阵\(a\),其中\(a_{i,j}=-1\)表示位置\((i,j)\)可以替换成任何数。要求重新排列这\(m\)列,使得每行都非严格单调递增。输出可能的排列。若有多解,输出任一。若无解,输出\(-1\)。

\(nm\in\left[1,10^5\right]\)。

考虑建一张有向图\(G=(V,E)\),路径\(x\to y\)存在当且仅当第\(x\)列必须在第\(y\)列前面。考虑对每行排序,然后会分成若干个相等段。\(\forall i\),若第\(i\)个相等段不都为\(-1\),那么第\(i\)个相等段中每一列和第\(i+1\)个相等段中每一列都连一条有向边,这样连边正确性显然。最终跑一遍拓扑排序即可。

但是这样很容易被卡成\(\mathrm O\!\left(nm^2\right)\)的。比如说,每一行都有\(\dfrac m2\)个数等于\(x\),另\(\dfrac m2\)个数等于\(y\neq x\)。于是考虑如何优化连边方式使得相等段到相等段之间的连边的复杂度是线性的。其实很简单,只需要套路地虚拟节点优化建图:对于每\(2\)个要连边的相等段都建一个虚拟节点,然后前面那个相等段的列全部连向虚拟节点,虚拟节点再连向所有后面相等段内的列。这样显然\(|E|=\mathrm O(nm)\)。需要连边的相等段对数为\(\mathrm O(nm)\),虚拟节点数自然也是\(\mathrm O(nm)\);再加上表示列们的\(m\)个节点,\(|V|=\mathrm O(nm)\)。

下面是AC代码:

#include<bits/stdc++.h>
using namespace std;
#define mp make_pair
#define X first
#define Y second
#define pb push_back
const int N_TIMES_M=100000;
int n,m;//矩阵大小 
int now;//|V| 
vector<int> nei[2*N_TIMES_M+1];//邻接矩阵 
int ideg[2*N_TIMES_M+1];//入度 
vector<int> topo;//拓扑序列 
void toposort(){//拓扑排序 
	queue<int> q;
	for(int i=1;i<=now;i++)if(!ideg[i])q.push(i);
	while(q.size()){
		int x=q.front();
		q.pop();
		topo.pb(x);
		for(int i=0;i<nei[x].size();i++){
			int y=nei[x][i];
			if(!--ideg[y])q.push(y);
		}
	}
}
int main(){
	cin>>n>>m;
	vector<vector<pair<int,int> > > a(n+1,vector<pair<int,int> >(m+1));//矩阵 
	for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)cin>>a[i][j].X,a[i][j].Y=j;
	now=m;//初始有表示m列的m个节点 
	for(int i=1;i<=n;i++){
		sort(a[i].begin()+1,a[i].end());//排序 
//		for(int j=1;j<=m;j++)printf("(%d,%d) ",a[i][j].X,a[i][j].Y);puts("");
		vector<pair<int,int> > rg;//相等段们 
		for(int j=1,je;j<=m;j=je+1){//算出有哪些相等段 
			je=j;while(je+1<=m&&a[i][je+1].X==a[i][j].X)je++;
			rg.pb(mp(j,je));
		}
//		for(int j=0;j<rg.size();j++)printf("[%d,%d] ",rg[j].X,rg[j].Y);puts("");
		for(int j=0;j+1<rg.size();j++){//相等段之间连边 
			int l1=rg[j].X,r1=rg[j].Y,l2=rg[j+1].X,r2=rg[j+1].Y;
			if(a[i][l1].X==-1)continue;
			now++;//新建虚拟节点 
			for(int k=l1;k<=r1;k++)nei[a[i][k].Y].pb(now),ideg[now]++;//连边 
			for(int k=l2;k<=r2;k++)nei[now].pb(a[i][k].Y),ideg[a[i][k].Y]++;//连边 
		}
	}
	toposort();//拓扑排序 
	if(topo.size()<now)puts("-1");//不是DAG,无解 
	else for(int i=0;i<topo.size();i++)if(topo[i]<=m)cout<<topo[i]<<" ";//输出解 
	return 0;
}
上一篇:欧拉函数的两个性质


下一篇:[CSP-S模拟测试]:简单的玄学(数学)