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

前言

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

题目

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

思路

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

代码

#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;
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值