題目鏈接:點擊查看
題目大意:給出一個長度為 n 的數列 a,現在要求選出一個 x,將 a 中的每個元素都異或之后得到一個新的數列 b,要求數列 b 的逆序對最小,問最小的逆序對是多少,x 該如何選擇
題目分析:才知道字典樹上分治也可以求逆序對,時間復雜度是 nlogn 的,做法如下:
將每個數都插入到字典樹中,記錄一下其下標對于某個節點來說,其左子樹中的點一定小于其右子樹中的點所以遍歷一遍左子樹中的點,雙指針找一下右子樹中有多少個點的下標大于當前枚舉的點,累加起來就是逆序對的個數了
因為每一層最多有 n 個數,一共有 logn 層,所以遍歷的次數約等于 nlogn 次
現在考慮該如何計算 x,假設 x 中的某一位為 0,那么在字典樹上將不產生影響,如果其中的某一位為 1 ,將對其對應層數的所有節點的左右子樹互換,注意這個過程中,只會影響到當前層數的逆序對,而不會影響到其他的層數的答案,換句話說,在這個題目中,x 中的每一位相互獨立
再考慮如果 x 的某一位是 1 的話會造成什么影響,設原本的逆序對為 ans1,順序對為 ans2,如果將該層的左右子樹互換的話,那么 ans1 和 ans2 的值也會相應的被交換,所以我們可以對于每一層統計一下 dp[ deep ][ 0 ] 和 dp[ deep ][ 1 ] 分別表示當前層數中逆序對和順序對的個數,如果某一層的逆序對更少,顯然當前位填 0 更優,否則填 1 更優
代碼:
?
#pragma GCC optimize(2)
#pragma GCC optimize("Ofast","inline","-ffast-math")
#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
#include<bitset>
#include<unordered_map>
using namespace std;typedef long long LL;typedef unsigned long long ull;const int inf=0x3f3f3f3f;const int N=3e5+100;vector<int>node[N*40];int trie[N*40][2],cnt;LL dp[40][2];int newnode()
{cnt++;trie[cnt][0]=trie[cnt][1]=0;node[cnt].clear();return cnt;
}void insert(int x,int id)
{int pos=0;for(int i=30;i>=0;i--){int to=(x>>i)&1;if(!trie[pos][to])trie[pos][to]=newnode();pos=trie[pos][to];node[pos].push_back(id);}
}void dfs(int pos,int deep)
{if(deep==-1)return;int lson=trie[pos][0],rson=trie[pos][1];LL res=0,r=0;int lsz=node[lson].size(),rsz=node[rson].size();for(int i=0;i<lsz;i++){while(r<rsz&&node[lson][i]>node[rson][r])r++;res+=r;}dp[deep][0]+=res;dp[deep][1]+=1LL*lsz*rsz-res;if(lson)dfs(lson,deep-1);if(rson)dfs(rson,deep-1);
}void init()
{cnt=-1;newnode();
}int main()
{
#ifndef ONLINE_JUDGE
// freopen("data.in.txt","r",stdin);
// freopen("data.out.txt","w",stdout);
#endif
// ios::sync_with_stdio(false);init();int n;scanf("%d",&n);for(int i=1;i<=n;i++){int num;scanf("%d",&num);insert(num,i);}dfs(0,30);LL ans=0,x=0;for(int i=0;i<=30;i++){if(dp[i][0]<=dp[i][1])ans+=dp[i][0];else{ans+=dp[i][1];x|=(1<<i);}}printf("%lld %lld\n",ans,x);return 0;
}
?
總結
以上是生活随笔為你收集整理的CodeForces - 1417E XOR Inverse(字典树求逆序对+分治)的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。