Do Not Duplicate(AGC-B)(模式循环行走)

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/qq_37555704/article/details/102618895

前言

还是对模式循环行走问题不熟悉
回忆起跳房子,都是按照他所给的方法模拟会超时,就算找到循环节也会超时,于是由于地图固定,可以处理出一些点的下一个特征点,具体问题具体分析,也可以为分块

题目

Atcoder
题目大意:
在这里插入图片描述

思路

首先是暴力,模拟栈找循环节即可,会 T T
然后我们怎么想呢,这种问题一般是一段一段地跨,我们多用暴力程序分析一下就会发现如果我们到一个位置 p p ,我们可以处理出和它值一样的下一个位置 n x t p nxt_p ,于是下一个位置就是 n x t p + 1 nxt_p+1 (到了 n n 处理一下) ,我们假设对 p , n x t p + 1 p,nxt_p+1 连边,就是一个 n n 个点 n n 条边的有向图,每个点的出度为1,但是我们要证明每个点入度为1,假设存在这样的图:
在这里插入图片描述
我们可以得到
在这里插入图片描述
两种情况,此时会发现B会连向A或者A会连向B,但是此时在环上保证了 n x t B + 1 = C nxt_B+1=C ,矛盾,于是每个点入度只能为1,于是 n x t nxt 组成许多个环。
于是如果我们通过 n x t nxt 进行遍历的话最多 n n 次会回到 1,形成了循环,但如果次数并不够我们跳一圈(并不是进行一次操作,是循环次数) 我们就用 n x t nxt 一步一步走,考虑如果只剩一次,假设现在在 p p 就判断哪些点 n x t p p nxt_p\le p 输出即可
实现用的破环成链

代码

#include<set>
#include<map>
#include<stack>
#include<cmath>
#include<queue>
#include<cstdio>
#include<vector>
#include<climits>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define LL long long
using namespace std;
LL read(){
	bool f=0;LL x=0;char c=getchar();
	while(c<'0'||'9'<c){if(c=='-')f=1;c=getchar();}
	while('0'<=c&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
	return !f?x:-x;
}
#define MAXN 200000
#define INF 0x3f3f3f3f
bool vis[MAXN+5];
int n,a[MAXN+5],val[MAXN+5],nxt[MAXN+5];
void Print(int p){
	while(p<=n){
		if(nxt[p]>n) printf("%d ",a[p]),p++;
		else p=nxt[p]+1;
	}
	puts("");
	exit(0);
}
int main(){
	n=read();
	LL k=read();
	for(int i=1;i<=n;i++)
		a[i]=read();
	for(int i=2*n;i>=n+1;i--)
		val[a[i-n]]=i;
	for(int i=n;i>=1;i--)
		nxt[i]=val[a[i]],val[a[i]]=i;
	int p=1;
	LL sum=0;
	while(!vis[p]){
		sum+=nxt[p]-p,vis[p]=1;
		if(nxt[p]>n)
			p=(nxt[p]-n)%n+1;
		else p=nxt[p]%n+1;
	}
	k%=(sum/n+1);
	while(k){
		if(!k) break;
		if(k==1) Print(p);
		if(nxt[p]>=n){
			k--;
			if(nxt[p]==2*n) k--;
			p=(nxt[p]-n)%n+1;
		}
		else p=nxt[p]+1;
	}
	puts("");
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_37555704/article/details/102618895