关于逆序对的一种运用(树状数组)
2018-10-06 08:38
95 查看
主要题目:bzoj4430 2789
4430
题目大意:给定三个串,长度为n,求三个串中序列前后性质的字符对数。
发现直接求不好求,但是我们学过逆序对。已知总的对数是n(n-1),只要求出3个中前后不一样的对数就好。
我们可以两两求出逆序对数,再除以2,用总对数减去就是答案了。
问题的关键在于对于两两序列求其中逆序的对数。
我们整理一个序列ai,令ai为在第一个串中位置为i的数再第二个串中的位置,那么在两串中若两字符顺序相同,那么其在序列中一定是正序的,反之,就可以求出逆序的对数了。
#include<bits/stdc++.h> using namespace std; typedef long long LL; const int maxn=2e5+10; inline int read() { int ret=0;char c=getchar(); while(c<'0'||c>'9') c=getchar(); while(c>='0'&&c<='9') ret=(ret<<1)+(ret<<3)+c-'0',c=getchar(); return ret; } inline int lowbit(int x){return x&(-x);} int cam[maxn][3],n,pos[maxn]; LL tree[maxn]; void add(int x) { while(x<=n){ tree[x]++;x+=lowbit(x); } } LL query(int x) { LL res=0; while(x>0){ res+=tree[x];x-=lowbit(x); } return res; } LL work(int x,int y) { LL res=0; memset(tree,0,sizeof(tree)); for(int i=1;i<=n;i++) pos[cam[i][x]]=i; for(int i=n;i>=1;i--) res+=query(pos[cam[i][y]]),add(pos[cam[i][y]]); return res; } int main() { freopen("4430.in","r",stdin); freopen("4430.out","w",stdout); LL ans; n=read(); for(int j=1;j<=3;j++) for(int i=1;i<=n;i++) cam[i][j]=read(); ans=(1LL*n*(n-1))/2; ans=ans-(work(1,2)+work(2,3)+work(1,3))/2; printf("%lld",ans); return 0; }
2789
题目大意:给定两个串,求最少的交换次数使1串等于2串,交换只能相邻的交换。
可以用反证法证明把1串中字符2串挪到最近的相同位置是最优的。(我不确定也不会证)
那么我们考虑交换次数如何计算:
我们同样求出如4030ai的一个序列,那么逆序对数就是最少的交换次数。在序列ai中,根据题目要求,我们要把它变成ai=i,这样1、2串就相同了,考虑交换对于序列ai的改变,通过手算 我们可以发现交换就是序列ai相邻位置的交换,那么ai也只能相邻交换,那么每有一个逆序对就意味着需要交换一次(感性理解一下),最后就把交换序列转化成了逆序对数。
#include<bits/stdc++.h> using namespace std; typedef long long LL; const int maxn=1e6+10; char s1[maxn],s2[maxn]; int n,len[26],num[maxn],tree[maxn]; vector<int>siz[26]; inline int lowbit(int x){return x&(-x);} void add(int x){ while(x<=n){ tree[x]++;x+=lowbit(x); } } LL query(int x){ LL ans=0; while(x>0){ ans+=tree[x];x-=lowbit(x); } return ans; } int main() { freopen("2789.in","r",stdin); freopen("2789.out","w",stdout); LL ans=0; cin>>n; cin>>s1+1>>s2+1; for(int i=1;i<=n;i++) siz[s1[i]-'A'].push_back(i); for(int i=1;i<=n;i++){ num[i]=siz[s2[i]-'A'][len[s2[i]-'A']]; len[s2[i]-'A']++; } for(int i=1;i<=n;i++){ add(num[i]); ans=ans+i-query(num[i]); } printf("%lld",ans); return 0; }
总结:我们发现对于字符串之间的比较,我们可以构造类似序列a之类的东西来解决字符串不同顺序的问题,同时问题的转化同样重要。
阅读更多相关文章推荐
- bzoj3289(莫队+树状数组求逆序对)
- nyoj 117 求逆序数 【树状数组】+【离散化】
- HDOJ 3743 Frosh Week(树状数组求逆序对)
- 树状数组求逆序对
- 树状数组求逆序对
- 2017 ACM-ICPC 亚洲区(南宁赛区)网络赛Train Seats Reservation(树状数组的运用)
- dp 树状数组 逆序元组
- hdu_2838_Cow Sorting(树状数组求逆序对)
- 树状数组 求逆序数 poj 2299 离散化
- 树状数组的基本+运用(HDU1166-敌兵布阵)
- 求逆序对 ----归并排 & 树状数组
- Codeforces Round 459 D. Pashmak and Parmida's problem 树状数组求逆序数 变形
- 树状数组求逆序数
- POJ 2299 利用树状数组求逆序对数
- 关于树状数组区间修改和查询详解
- 树状数组求逆序对 + 离散化(poj2299)
- SGU 180-Inversions(树状数组离散化求逆序对数)
- 蓝桥杯 小朋友排队(树状数组求逆序对)
- 【算法】逆序对问题的四种解法(归并排序,BST,树状数组,线段树)及变形
- 树状数组求逆序数