D
题面
思路
首先要知道奇排列和偶排列的概念;
对一个数列,如果总的逆序数为奇数,则此排列为奇排列,否则为偶排列;
对于一次交换swap(i,j)
来说,会改变排列的奇偶性,如下图;
而题目要我们执行swap(i,j)
与swap(j,k)
;
也就是两次交换,因此排列的奇偶性不变;
因此,对于排列来说,我们只需要计算逆序对是奇还是偶,偶数则为YES,奇数则是NO;
因为这道题可能有重复的数字出现;
比如 [ 3 , 2 , 3 , 1 ] [3,2,3,1] [3,2,3,1],下面两种换法是等价的;
我们可以设第一个3为 p p p,第二个3为 q q q,数字1为 r r r;
r
→
p
r → p
r→p
p
→
q
p → q
p→q
q
→
r
q → r
q→r
因为 p p p与 q q q数字是相等的,因此我们可以写成如下形式;
r
→
p
r → p
r→p
q
→
q
q → q
q→q
p
→
r
p → r
p→r
也就是说,如果出现了重复数字,则允许交换任意两个元素,那么必然是YES;
Code
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <algorithm>
#include <map>
using namespace std;
typedef long long ll;
const int N = 5e5 + 10;
int a[N],tr[N];
int lowbit(int x){
return x & -x;
}
void add(int u,int v){
for(int i=u;i<N;i+=lowbit(i)){
tr[i] += v;
}
}
int query(int x){
int ret = 0;
for(int i=x;i;i-=lowbit(i)){
ret += tr[i];
}
return ret;
}
vector<int> ve;
int get(int x){
return 1 + lower_bound(ve.begin(),ve.end(),x) - ve.begin();
}
map<int,int> mp;
void solve(){
int n;
cin >> n;
for(int i=1;i<=n;++i) tr[i] = 0;
mp.clear();
ve.clear();
for(int i=1;i<=n;++i){
cin >> a[i];
ve.push_back(a[i]);
++mp[a[i]];
}
sort(ve.begin(),ve.end());
ve.erase(unique(ve.begin(),ve.end()),ve.end());
for(auto x : mp){
if(x.second > 1){
cout << "YES\n";
return;
}
}
int res = 0;
for(int i=1;i<=n;++i){
res += query(n) - query(get(a[i]));
add(get(a[i]),1);
}
if(res & 1) cout << "NO\n";
else cout << "YES\n";
}
signed main(){
std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int t;
cin >> t;
while(t--)
solve();
return 0;
}
E
题面
思路
将询问存储下来离线处理;
cnt(i) = x 表示数字i出现了x次
nums(i) 表示出现i次的集合中有谁
在dfs
的过程中,我们新加入当前遇到的这个点u
会导致一加一减;
比如原本出现次数 2 2 2次的数有两个,那么当前这个数加进来以后,出现次数 2 2 2次的数就少了一个,而出现次数 3 3 3次的数就多了一个;
同时我们需要在nums
中insert
和erase
;
对于一次查询来说,我们要去掉出现次数不足 l l l次的,然后在剩余的数中找第 k k k大的;
假设出现不足 l l l次的数字个数为 x x x个,那么我们相当于要找排第 x + k x + k x+k名的;
这个操作其实就是树状数组中的查询排名为k的数字;
然后回溯的过程就和刚刚遇到这个点反过来即可;
Code
#include <iostream>
#include <cstring>
#include <cstdio>
#include <set>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 1e6 + 10;
struct Query{
int l,k,id;
};
vector<Query> query_ve[N];
vector<int> sons[N];
int a[N],n,q;
set<int> nums[N];//nums(i) 表示数字出现i次集合中有谁
int cnt[N],tr[N],ans[N];//cnt(i) = x 表示数字i出现了x次
int lowbit(int x){
return x & -x;
}
void add(int u,int v){
for(int i=u;i<=n;i+=lowbit(i)){
tr[i] += v;
}
}
int query(int u){
int ret = 0;
for(int i=u;i;i-=lowbit(i)){
ret += tr[i];
}
return ret;
}
int query2(int rank){
int idx = 0;
for(int i=20;i>=0;--i){
idx += (1 << i);
if(idx > n || tr[idx] >= rank){
idx -= (1 << i);
}
else{
rank -= tr[idx];
}
}
return idx + 1;
}
void dfs(int u){
int times = cnt[a[u]];
if(times){
add(times,-1);
if(nums[times].count(a[u])){
nums[times].erase(a[u]);
}
}
++times,++cnt[a[u]];
add(times,1);
nums[times].insert(a[u]);
for(auto x : query_ve[u]){
//查询l - 1有多少个数
int number = query(x.l - 1);
//查询第k大的数的索引
int k = number + x.k;
int idx = query2(k);
if(idx > n) ans[x.id] = -1;
else ans[x.id] = *nums[idx].begin();
}
for(auto v : sons[u]){
dfs(v);
}
//回溯
add(times,-1);
nums[times].erase(a[u]);
--times,--cnt[a[u]];
if(times){
add(times,1);
nums[times].insert(a[u]);
}
}
void solve(){
cin >> n >> q;
for(int i=1;i<=n;++i) cin >> a[i];
for(int i=1;i<=n;++i){
tr[i] = 0;
cnt[i] = 0;
nums[i].clear();
sons[i].clear();
query_ve[i].clear();
}
for(int i=2,p;i<=n;++i){
cin >> p;
sons[p].push_back(i);
}
for(int i=1,v,l,k;i<=q;++i){
cin >> v >> l >> k;
query_ve[v].push_back({l,k,i});
}
dfs(1);
for(int i=1;i<=q;++i)
cout << ans[i] << ' ';
cout << '\n';
}
signed main(){
std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int t;
cin >> t;
while(t--)
solve();
return 0;
}