您的位置:首页 > 其它

关于逆序对的一种运用(树状数组)

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之类的东西来解决字符串不同顺序的问题,同时问题的转化同样重要。

阅读更多
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: