CF Div2 977 C2

原题链接:Problem - C2 - Codeforces

题意:给出长度为n的数组p,这是一个1~n的排列,含义是第i个位置站着第p[i]号人。给出长度为m的数组q,含义是第i个幻灯片被第p[i]号人讲解,那么就是完美的,每个幻灯片都需要有人来讲解,每次讲解的人必须是数组p的第一个人,当某个人讲解完成之后,他可以去数组p的任何位置,有k次询问每次会改变q数组,每次给出q和w,让q[x]=w,每次改变都是永久的。输出k+1行,判断初始情况是否可以完成讲解和每次询问是否可以完成讲解,完成讲解的定义是每个幻灯片都被完美讲解。

思路:通过模拟可以知道,如果某个人讲解完成了之后,那么这个人就是自由的,他一定可以去需要他的位置,所以只要幻灯片需要的人正好是第一个或者这个人是自由的,那么就是完美的。如果不修改q数组,那么只要让q数组中不同数出现的相对位置和p数组中相同就可以了。可以观察到,如果完成讲解p数组中的数在q数组里面第一次出现的下标一定是升序的,用f数组来代表p数组里面每个数第一次在q数组里面出现的位置,完成讲解的情况下一定满足f[p[i]]<f[p[i+1]]。每次询问可能改变的是q[x]和w第一次出现的位置,如何判断改变后的状态和改变前的状态是否不同,那么可以用cnt来表示f[p[i]]>f[p[i+1]]的个数,如果是0那么就是完成讲解。因为要找到每个数第一次出现的位置,所以需要一个有序的容器来装每个数的位置,使用set。

//冷静,冷静,冷静
//调不出来就重构
//#pragma GCC optimize(2)
//#pragma GCC optimize("O3")
#include<bits/stdc++.h>
#define count2(x) __builtin_popcountll(x)
#define is2(x) __builtin_ffsll(x)
#define endl '\n'
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
typedef pair<ll,ll> pii;
const int N=1e6+10,mod=1000000007;
ll p[N],id[N],q[N];
set<ll> st[N];
ll f[N];
ll cnt,n,m,sb;
void updata(ll x,ll op)
{
	//如果这个数,在p数组中左右还有数的情况下,那么就先考虑删除这个数变化之前的影响。
	//id[q[x]]是q[x]在p中的位置p[id[q[x]]是q[x],p[id[q[x]]-1]是q[x]在p中位置的左边的数,f[p[id[q[x]]-1]]是左边数第一次出现的位置  
	if(id[q[x]]-1>0)cnt=cnt-(f[p[id[q[x]]-1]]>f[p[id[q[x]]]]);
	if(id[q[x]]+1<=n)cnt=cnt-(f[p[id[q[x]]]]>f[p[id[q[x]]+1]]);
	if(op)st[q[x]].insert(x);
	else st[q[x]].erase(x);
	f[q[x]]=*st[q[x]].begin();
	//更新f数组之后,重新加上x这个位置的影响。 
	if(id[q[x]]-1>0)cnt=cnt+(f[p[id[q[x]]-1]]>f[p[id[q[x]]]]);
	if(id[q[x]]+1<=n)cnt=cnt+(f[p[id[q[x]]]]>f[p[id[q[x]]+1]]);
}
void Jiuyuan()
{
	cnt=0;cin>>n>>m>>sb;
	for(int i=1;i<=n;i++)//读入p[i],映射p[i]这个数的位置,清空之前的set,插入一个很大的数 
	{
		cin>>p[i];
		id[p[i]]=i;
		st[i].clear(); 
		st[i].insert(1e9);
	}
	for(int i=1;i<=m;i++)
	{
		cin>>q[i];
		st[q[i]].insert(i);
	}
	for(int i=1;i<=n;i++)//f数组含义是这个数第一次出现的位置 
	{
		f[p[i]]=*st[p[i]].begin();
	}
	for(int i=1;i<n;i++) 
	{
		cnt=cnt+(f[p[i]]>f[p[i+1]]);
	}
	if(cnt==0)
	{
		cout<<"YA"<<endl;
	}
	else cout<<"TIDAK"<<endl;
	while(sb--)
	{
		ll x,w;cin>>x>>w;
		updata(x,0);
		q[x]=w;
		updata(x,1);
		if(cnt==0)
		{
			cout<<"YA"<<endl;
		}
		else cout<<"TIDAK"<<endl;
	}
}
int main()
{
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	ll T=1;
	cin>>T;
	while(T--)
	{
		Jiuyuan();
	}
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值