题目:https://icpcarchive.ecs.baylor.edu/external/43/4329.pdf
考虑的方法是,依次考虑编号为i的人当裁判,然后计算出编号比他小且技能比他低的人ci个,和编号比他大且技能比他低的人di个
,那么编号比他小且技能比他高的人有 i-1-ci , 编号比他大且技能比他高的人有 n-i-di 个。
所以第i个人当裁判的情形= (i-1-ci)*di+ci*(n-i-di) ;
之前考虑时,总是固定 编号最高或者是技能最高的人,然后考虑从比他低的人中选两个,这样行不通。
本题还有一个关键就是,技能值各不相同,编号各不相同。
其实就算技能值出现相同的好像也可以解 :我先用技能值排个序,然后从小到大扫 。(利用编号一定不同)
#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<climits>
#include<queue>
#include<vector>
#include<map>
#include<sstream>
#include<set>
#include<stack>
#include<cctype>
#include<utility>
#pragma comment(linker, "/STACK:102400000,102400000")
#define PI 3.1415926535897932384626
#define eps 1e-10
#define sqr(x) ((x)*(x))
#define FOR0(i,n) for(int i=0 ;i<(n) ;i++)
#define FOR1(i,n) for(int i=1 ;i<=(n) ;i++)
#define FORD(i,n) for(int i=(n) ;i>=0 ;i--)
#define lson num<<1,le,mid
#define rson num<<1|1,mid+1,ri
#define MID int mid=(le+ri)>>1
#define zero(x)((x>0? x:-x)<1e-15)
#define mk make_pair
#define _f first
#define _s second
using namespace std;
//const int INF= ;
typedef long long ll;
//const ll inf =1000000000000000;//1e15;
//ifstream fin("input.txt");
//ofstream fout("output.txt");
//fin.close();
//fout.close();
//freopen("a.in","r",stdin);
//freopen("a.out","w",stdout);
const int INF =0x3f3f3f3f;
const int maxn= 20000+20 ;
//const int maxm= ;
//by yskysker123
const int N=100000;
int skill[maxn];
ll ans;
int n,C[100000+20],D[100000+20];
inline int lowbit(int x) { return x&(-x);}
void add(int C[maxn],int x,int d)
{
while(x<=N)
{
C[x]+=d;
x+=lowbit(x);
}
}
int sum(int C[maxn],int x)
{
int ans=0;
while(x)
{
ans+=C[x];
x-=lowbit(x);
}
return ans;
}
void work()
{
for(int i=1;i<=n;i++)
{
add(D, skill[i],1);
}
for(int i=1;i<=n;i++)
{
int cs=sum(C,skill[i]);
int cl=i-1-cs;
add(D,skill[i],-1);
int ds=sum(D,skill[i]);
int dl=n-i-ds;
ans+= cs*dl+cl*ds;
add(C,skill[i],+1);
}
}
void init()
{
memset(C ,0,sizeof C);
memset(D ,0,sizeof D);
}
int main()
{
int T;scanf("%d",&T);
while(T--)
{
init();
scanf("%d",&n);
FOR1(i,n)
{
scanf("%d",&skill[i]);
}
ans=0;
work();
printf("%lld\n",ans);
}
return 0;
}
我这样写其实有些麻烦,事先要把skill[]扫过一遍,更新D[]值。
因为,只需要知道ci、di 且ci,di是固定不变的,与扫描(/操作)过程无关.
故可以从右往左扫一次,把di存下来。
下面是佳神的代码。
// LA4329 Ping pong
// Rujia Liu
#include<cstdio>
#include<vector>
using namespace std;
//inline int lowbit(int x) { return x&(x^(x-1)); }
inline int lowbit(int x) { return x&-x; }
struct FenwickTree {
int n;
vector<int> C;
void resize(int n) { this->n = n; C.resize(n); }
void clear() { fill(C.begin(), C.end(), 0); }
// ¼ÆËãA[1]+A[2]+...+A[x] (x<=n)
int sum(int x) {
int ret = 0;
while(x > 0) {
ret += C[x]; x -= lowbit(x);
}
return ret;
}
// A[x] += d (1<=x<=n)
void add(int x, int d) {
while(x <= n) {
C[x] += d; x += lowbit(x);
}
}
};
const int maxn = 20000 + 5;
int n, a[maxn], c[maxn], d[maxn];
FenwickTree f;
int main() {
int T;
scanf("%d", &T);
while(T--) {
scanf("%d", &n);
int maxa = 0;
for(int i = 1; i <= n; i++) { scanf("%d", &a[i]); maxa = max(maxa, a[i]); }
f.resize(maxa);
f.clear();
for(int i = 1; i <= n; i++) {
f.add(a[i], 1);
c[i] = f.sum(a[i]-1);
}
f.clear();
for(int i = n; i >= 1; i--) {
f.add(a[i], 1);
d[i] = f.sum(a[i]-1);
}
long long ans = 0;
for(int i = 1; i <= n; i++)
ans += (long long)c[i]*(n-i-d[i]) + (long long)(i-c[i]-1)*d[i];
printf("%lld\n", ans);
}
return 0;
}