【题解】洛谷P3531、P1966:LIT-Letters 、火柴排队
P3531 [POI2012] LIT-Letters
写了个假做法有点伤心,本以为是两个都求逆序对然后答案相减,但是这样在部分数据上确实合法,但是实际上毫无章法没有逻辑。
正解考虑贪心,我们一个数字肯定要找最前面,第二次出现就去最前面第二次出现的位置,因为如果第一个A放在了后面,那么就有可能产生更对逆序对(因为你把大的数放在了前面)。
我们对每个字母重新编号,\(A\) 为 ABC
编号为 123
,\(B\) 为 CBA
编号为 321
。
接下来就可以正常求逆序对了。
其实思想就是多个重复的我们进行去重,让他们有对应的编号,像原来为 \(A\) 为 010
,\(B\) 为 100
,完全没法去逆序对,不知道的还以为是区间差逆序对呢,但是我们重编号就为 123,213
这就很明确了,我们都尽量向前靠,每个数该去哪就去哪。
#include<bits/stdc++.h>
#define int long long
#define ls p<<1
#define rs p<<1|1
#define re register
const int N=1e6+10;
const int mod=998244353;
using namespace std;
int n;
int a[N],b[N];
int t[N];
int lb(int x){
return x&-x;
}
void add(int x,int k){
while(x<=n){
t[x]+=k;
x+=lb(x);
}
}
int query(int x){
int sum=0;
while(x){
sum+=t[x];
x-=lb(x);
}
return sum;
}
int vis[30][N];
int tt[100];
signed main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin>>n;
for(int i=1;i<=n;i++){
char c;
cin>>c;
a[i]=c-'A'+2;
}
for(int i=1;i<=n;i++){
char c;
cin>>c;
b[i]=c-'A'+2;
}
for(int i=1;i<=n;i++){
vis[a[i]][++vis[a[i]][0]]=i;
}
for(int i=1;i<=n;i++){
b[i]=vis[b[i]][++tt[b[i]]];
}
int ans=0;
for(int i=n;i>=1;i--){
ans+=query(b[i]-1);
add(b[i],1);
}
cout<<ans;
return 0;
}
P1966 [NOIP2013 提高组] 火柴排队
这是上面那题的略微复杂版,可以想到上面最大与下面最大这样造成的代价最小,欸!是大小关系不是具体数值,我们进行离散化去重可以分别得到 1 3 4 2 与 1 4 2 3
,可以发现到这就是对应数字到对应位置,直接套上一题的模板即可,注意:题目已经说了一盒火柴互不相同所以不用去重,但就这样吧。
#include<bits/stdc++.h>
//#define int long long
#define ll long long
#define ls p<<1
#define rs p<<1|1
#define re register
const int N=1e5+10;
const int mod=1e8-3;
using namespace std;
int n;
int a[N],b[N];
ll t[N];
inline int lb(int x){
return x&-x;
}
int mx=0;
inline void add(int x,int k){
while(x<=mx){
t[x]+=k;
t[x]%=mod;
x+=lb(x);
}
}
inline int query(int x){
ll sum=0;
while(x){
sum+=t[x];
sum%=mod;
x-=lb(x);
}
return sum;
}
int vis[N][100];
int tt[N];
int f1[N],o1=0;
int f2[N],o2=0;
signed main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin>>n;
for(re int i=1;i<=n;i++){
cin>>a[i];
f1[++o1]=a[i];
}
for(re int i=1;i<=n;i++){
cin>>b[i];
f2[++o2]=b[i];
}
sort(f1+1,f1+o1+1);
sort(f2+1,f2+o2+1);
int tot1=unique(f1+1,f1+o1+1)-f1-1;
int tot2=unique(f2+1,f2+o2+1)-f2-1;
for(re int i=1;i<=n;i++){
a[i]=lower_bound(f1+1,f1+1+tot1,a[i])-f1;
b[i]=lower_bound(f2+1,f2+1+tot2,b[i])-f2;
}
if(tot1<tot2){
swap(a,b);
}
mx=max(tot1,tot2);
// cout<<tot1<<" "<<tot2;
for(re int i=1;i<=n;i++){
vis[a[i]][++vis[a[i]][0]]=i;
}
for(re int i=1;i<=n;i++){
b[i]=vis[b[i]][++tt[b[i]]];
}
ll ans=0;
for(re int i=n;i>=1;i--){
ans+=query(b[i]-1);
ans%=mod;
add(b[i],1);
}
cout<<ans;
return 0;
}