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,i−1] 中大于
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;
}