人,只有自己站起来,这个世界才能属于他。|

园龄:粉丝:关注:

CF Round 1002 题解合集

here

困难场。

C

重要观察:只有每行的后缀 1 有用。

考虑归纳证明,设 n 为一行后缀 1 的个数。

对于 n=1 的情况,想要让 mex 值为 2,必须选择这个后缀,否则其他后缀的和 一定 >1

对于 n>1 的情况,在之前,每次选择后缀 1 都是在再不选就不行的情况下,比如在最后一个时刻选 n=1,在倒数第二个时刻选 n=2 之类,所以这个时刻也没有选择,只能选后缀 1

因此,我们保留每一行的极长后缀 1 个数,贪心减小若干值最大化 mex 即可。

具体贪心的过程为如果当前数不存在,那么选择最接近它的大于它的数来减小。

总体复杂度 O(nm)

#include<bits/stdc++.h>
using namespace std;
int t,n,a[301][301],sum[301],flag,cnt,ans;
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin>>t;
	while(t--){
		cin>>n;
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				cin>>a[i][j];
			}
		}
		for(int i=1;i<=n;i++){
			cnt=0;
			for(int j=n;j>=1;j--){
				if(a[i][j]!=1) break;
				cnt++;
			}
			sum[cnt]++;
		}
		for(int i=0;i<=n;i++){
			if(sum[i]) continue;
			flag=false;
			for(int j=i+1;j<=n;j++){
				if(sum[j]){
					sum[j]--;
					flag=true;
					break;
				}
			}
			if(!flag){
				ans=i;
				break;
			}
		}
		cout<<ans<<'\n';
		for(int i=0;i<=n;i++) sum[i]=0;
		ans=0;
	}
	return 0;
}

D

考虑想要使得代价为定值,那么一定是走到了图上相同的两点 (u,u),满足 u 有相同的出点 v

因为点数很小,考虑新建一张图,每个点 (u,v),表示在原图第一张图上走到点 u,原图第二张图上走到点 v,然后连边就是枚举 u,v 的出点直接连。

这样总点数不会超过 n2,总边数不会超过 m2,合法的目标点就是上文所说的合法的 (u,u)

总体复杂度 (m2logm2)

#include<bits/stdc++.h>
using namespace std;
const int inf=1e9;
int t,n,s1,s2,x,y,dcnt,m1,m2,ans,id[1001][1001];
bool vis[1001][1001],chk[1001];
vector<int> v[1001],g[1001];
int head[1000001],nxt[4000001],targetx[4000001],targetw[4000001],tot;
void add(int x,int y,int w){
	tot++;
	nxt[tot]=head[x];
	head[x]=tot;
	targetx[tot]=y;
	targetw[tot]=w;
}
struct node{
	int x,dis;
	bool operator <(const node &a)const{
		return a.dis<dis;
	}
};
priority_queue<node> q;
int dis[1000001],ok[1000001];
void dij(int s){
	for(int i=1;i<=dcnt;i++){
		dis[i]=inf;
		ok[i]=0;
	}
	dis[s]=0;
	q.push((node){s,dis[s]});
	while(q.size()){
		int x=q.top().x;
		q.pop();
		if(ok[x]) continue;
		ok[x]=1;
		for(int i=head[x];i;i=nxt[i]){
			int y=targetx[i],w=targetw[i];
			if(dis[y]>dis[x]+w){
				dis[y]=dis[x]+w;
				q.push((node){y,dis[y]});
			}
		}
	}
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin>>t;
	while(t--){
		cin>>n>>s1>>s2;
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				id[i][j]=++dcnt;
			}
		}
		cin>>m1;
		for(int i=1;i<=m1;i++){
			cin>>x>>y;
			vis[x][y]=1;
			vis[y][x]=1;
			v[x].push_back(y);
			v[y].push_back(x);
		}
		cin>>m2;
		for(int i=1;i<=m2;i++){
			cin>>x>>y;
			if(vis[x][y]) chk[x]=chk[y]=1;
			g[x].push_back(y);
			g[y].push_back(x); 
		}
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				for(int l1=0;l1<v[i].size();l1++){
					for(int l2=0;l2<g[j].size();l2++){
						x=v[i][l1],y=g[j][l2];
						add(id[i][j],id[x][y],abs(x-y));
					}
				}
			}
		}
		dij(id[s1][s2]);
		ans=inf;
		for(int i=1;i<=n;i++){
			if(chk[i]) ans=min(ans,dis[id[i][i]]);
		}
		if(ans<inf) cout<<ans<<'\n';
		else cout<<-1<<'\n';
		for(int i=1;i<=n;i++){
			v[i].clear();
			g[i].clear();
			chk[i]=0;
		}
		for(int i=1;i<=dcnt;i++){
			head[i]=0;
		}
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				vis[i][j]=0;
			}
		}
		tot=dcnt=0;
	}
	return 0;
}

E1

困难题。

考虑将矩形摊成序列上,那么相当于我们可以在 m 个位置加数,并且在 m 个位置删数。

考虑最小化操作次数,一定是最大化不动的点的个数,并且这些不动的点,构成了 a 的前缀与 b 的子序列。

从小到大枚举 i,设 ciaib 中的匹配位置,考虑前缀 [1,i] 是否合法。

  • 如果 b 中不存在 i,死;

  • 如果 cii<ci1(i1) 死;

  • 如果 cii>ci1(i1) 并且 i1 不是块的右端点,死。

如果 i 死了,那么有可能整个块都死了。

所以考虑如果块的左端点到匹配位置的距离比 i 到右端点的距离更小,那么整个块都死了。

否则只有 i 这个点会死。

做完了,总体复杂度 O(n)

#include<bits/stdc++.h>
using namespace std;
int t,n,m,a[300005],b[300005],pos[600005],bel[300005],flag,ans;
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin>>t;
	while(t--){
		cin>>n>>m;
		n*=m;
		for(int i=1;i<=n;i++){
			cin>>a[i];
		}
		for(int i=1;i<=n;i++){
			cin>>b[i];
			pos[b[i]]=i;
		}
		for(int i=1;i<=n;i++){
			bel[i]=(i-1)/m+1;
		}
		for(int i=1;i<=n;i++){
			if(!pos[a[i]]) flag=true;
			if(i>=2 && pos[a[i]]-i<pos[a[i-1]]-(i-1)) flag=true;
			if(i>=2 && pos[a[i]]-i>pos[a[i-1]]-(i-1) && pos[a[i-1]]<m*bel[i-1]) flag=true;
			if(flag){
				ans=true;
				int p=(bel[i]-1)*m+1;
				if(p+m-i-1>=pos[a[p]]-p) cout<<n-p+1<<'\n';
				else cout<<n-i+1<<'\n'; 
				break;
			}
		}
		if(!ans) cout<<0<<'\n';
		ans=flag=0;
		for(int i=1;i<=n;i++){
			pos[b[i]]=0;
		}
	}
	return 0;
}

本文作者:Kenma

本文链接:https://www.cnblogs.com/Kenma/p/18714301

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   _Kenma  阅读(9)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起