CF 711 B(思维),C(性质);ARC 136 B(性质,逆序对)

B. Odd Swap Sort

题意:

给出一个数列,判断是否可以经过若干次下列操作使其变为非降数列?
操作:如果相邻的两个位置相加为奇数,可以互换。

思路:

那么对于一个数想要移动就必须穿过和其奇偶性不同的数
移动时两个数交换,所以只需考虑较小的数往前移动:
从前往后遍历每个位置,该想要往前移动就必须穿过所有比其大的数。
但是如果前面出现过和其奇偶性相同的比其大的数,那么这个数是穿不过去的,就无解。
所以分别维护前面位置中,奇数和偶数最大值即可。

Code:

const int N = 200010, mod = 1e9+7;
int T, n, m, k;
int a[N];

signed main(){
	Ios;
	cin>>T;
	while(T--)
	{
		cin>>n;
		for(int i=1;i<=n;i++) cin>>a[i];
		
		int flag=0, emax=0, omax=0;
		
		for(int i=1;i<=n;i++)
		{
			if(a[i]%2)
			{
				if(omax > a[i]) flag=1;
				omax = max(omax, a[i]);
			}
			else{
				if(emax > a[i]) flag=1;
				emax = max(emax, a[i]);
			}
		}
		
		if(flag) cout<<"NO\n";
		else cout<<"YES\n";
	}
	
	return 0;
}

C. Inversion Graph

题意:

给出一个全排列,对于位置 i i i 上的数 a [ i ] a[i] a[i],可以和所有 [ 1 , i − 1 ] [1,i-1] [1,i1] 中大于 a [ i ] a[i] a[i] 的数连边。
求构成的图中,连通块的个数?

思路:

法1:寻找性质
如果两个位置 l,r 满足 a[l]>a[r] ,那么 [l, r] 这个区间中的所有元素都会在
同一集合中。
(对于中间位置的数,如果小于 a[l] 的话会和 a[l] 合并,如果大于 a[l] 会和 a[r] 合并)。
所以遍历所有的相邻的位置 x,x+1,看是否 [1, x] 中存在比 [x+1, n] 中大的元素:
如果是,那么这两个相邻的位置就是在同一区间中;
否则就不在同一区间,区间个数++。

法2:单调栈。
后面较小的数会和前面较大的数合并,所以维护单调递增栈,每个集合挑选一个最大值放到栈中。
如果当前较小的值比栈中一些元素大,把栈中的比当前值大的元素弹出,因为这些弹出的元素可以和当前值合并。将最大值重新入栈。
最终栈中剩余元素的个数便是连通块个数

Code:

const int N = 200010, mod = 1e9+7;
int T, n, m, k;
int a[N], maxa[N], mina[N];

int main(){
	Ios;
	
	//法1: 
	cin>>T;
	while(T--)
	{
		cin>>n;
		for(int i=1;i<=n;i++) cin>>a[i];
		mina[n+1] = 1e9;
		for(int i=1;i<=n;i++) maxa[i] = max(maxa[i-1], a[i]);
		for(int i=n;i>=1;i--) mina[i] = min(mina[i+1], a[i]);
		
		int cnt=1;
		for(int i=1;i<n;i++)
		{
			if(maxa[i] < mina[i+1]) cnt++;
		}
		cout<<cnt<<endl;
	}
	
	//法2: 
	cin>>T;
	while(T--)
	{
		cin>>n;
		for(int i=1;i<=n;i++) cin>>a[i];
		
		stack<int> stk;
		int maxa=0;
		for(int i=1;i<=n;i++)
		{
			while(stk.size() && stk.top()>a[i]) stk.pop();
			
			maxa = max(maxa, a[i]);
			
			if(stk.size() && stk.top()==maxa);
			else stk.push(maxa);
		}
		cout<<stk.size()<<endl;
	}
	
	return 0;
}

B - Triple Shift

题意:

给出两个数列A和B,判断是否A可以经过若干次下列操作变为B?
初始令 x,y,z 分别为 ai, ai+1, ai+2 的值,然后重新赋值:ai=y, ai+1=z, ai+2=x.

思路:

一类问题:判断一个字符串经过若干次操作是否可以变为另一个串。
寻找变化前后不变的属性,判断两个串是否都有该属性。

  • 如果A是全排列时:
    发现变化之后,整个数列的逆序对个数的奇偶性是不会变化的,所以判断A和B逆序对数的奇偶性是否相同;
  • 当A不是全排列时:一定可以转化。

Code:

const int N = 200010, mod = 1e9+7;
int T, n, m, k;
int a[N], c[N], b[N];
multiset<int> st1, st2;

int lbit(int x){
	return x & -x;
}

void add(int x, int y){
	for(int i=x;i<=5000;i+=lbit(i)) c[i]+=y;
}

int query(int x)
{
	int ans=0;
	for(int i=x;i;i-=lbit(i)) ans+=c[i];
	return ans;
}

int check(int a[])
{
	for(int i=1;i<=5000;i++) c[i]=0;
	
	int cnt=0;
	for(int i=n;i>=1;i--)
	{
		cnt += query(a[i]-1);
		add(a[i], 1);
	}
	return cnt;
}

int main(){
	Ios;
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i], st1.insert(a[i]);
	for(int i=1;i<=n;i++) cin>>b[i], st2.insert(b[i]);
	
	if(st1!=st2){
		cout<<"No";return 0;
	}
	
	int flag=0;
	for(int i=1;i<=n;i++){
		if(mp[a[i]]) flag=1;
		mp[a[i]]++;
	}
	
	if((check(a)%2) == check(b)%2) flag=1;
	
	if(flag) cout<<"Yes";
	else cout<<"No";
	
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值