根号带 log 算法跑的居然比两只 log 跑得快?& 题解:P3157 [CQOI2011] 动态逆序对
由于我是一个励志成为第二个 lxl 的毒瘤。
我成功把分块从这个卡到了这个,甚至比某位金钩大佬的cdq 分治要快,来看看我是怎么做的吧。
做法
首先考虑拆性质,将序列中删去一个数,会被删去多少个逆序对的?稍微手玩可以发现,减去的逆序对数等于左边比它大的元素个数加上右边比它小的元素个数,可以先求出原本有几个逆序对,然后容易发现可以分块维护,经典 trick 是对于每个块内排序,不会的去看这个,然后考虑如何删,发现要改变一下策略,对于每个块维护一个 vector,对于每个删除操作直接直接用内置函数删,不会超时因为虽然 vector 删除 \(O(n)\) 但是每个 vector 长度是 \(O(\sqrt{n})\),所以非常快,这个 vector 的套路也是一个后续非常常见的套路可以记一下,然后再维护一个数组 \(act_i\) 表示 \(i\) 是否被删除。然后还有一个细节是要求输出操作之前的逆序对数,对于这一点我们可以把我们所求出来的答案存起来,最后再输出,存的这一步可以用 vector 去做,提高了命中率会快很多。
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
int a[N],p[N],bl[N],st[N],ed[N],tot,mx;
bool act[N];
vector<int>v[N];
long long ans;
int d[N];
int qg(int l,int r,int x){
if(l>r)return 0;
int il=bl[l],ir=bl[r],res=0;
if(il==ir){
for(int i=l;i<=r;i++)if(act[i]&&a[i]>x)res++;
return res;
}
for(int i=l;i<=ed[il];i++)if(act[i]&&a[i]>x)res++;
for(int i=il+1;i<ir;i++)res+=v[i].end()-upper_bound(v[i].begin(),v[i].end(),x);
for(int i=st[ir];i<=r;i++)if(act[i]&&a[i]>x)res++;
return res;
}
int ql(int l,int r,int x){
if(l>r)return 0;
int il=bl[l],ir=bl[r],res=0;
if(il==ir){
for(int i=l;i<=r;i++)if(act[i]&&a[i]<x)res++;
return res;
}
for(int i=l;i<=ed[il];i++)if(act[i]&&a[i]<x)res++;
for(int i=il+1;i<ir;i++)res+=lower_bound(v[i].begin(),v[i].end(),x)-v[i].begin();
for(int i=st[ir];i<=r;i++)if(act[i]&&a[i]<x)res++;
return res;
}
int main(){
int n,m;cin>>n>>m;
mx=sqrt(n);tot=(n-1)/mx+1;
for(int i=1;i<=n;i++){
cin>>a[i];
p[a[i]]=i;
}
for(int i=1;i<=m;i++)cin>>d[i];
for(int i=1;i<=tot;i++){
st[i]=(i-1)*mx+1;
ed[i]=min(i*mx,n);
for(int j=st[i];j<=ed[i];j++)bl[j]=i;
}
fill(act+1,act+n+1,1);
for(int i=1;i<=n;i++)v[bl[i]].push_back(a[i]);
for(int i=1;i<=tot;i++)sort(v[i].begin(),v[i].end());
ans=0;
for(int i=1;i<=n;i++)ans+=qg(1,i-1,a[i]);
vector<long long>res(m+1);
res[1]=ans;
for(int i=1;i<=m;i++){
int val=d[i],pos=p[val];
int lf=qg(1,pos-1,a[pos]);
int rt=ql(pos+1,n,a[pos]);
ans-=lf+rt;
int id=bl[pos];
act[pos]=0;
v[id].erase(lower_bound(v[id].begin(),v[id].end(),a[pos]));
if(i<m)res[i+1]=ans;
}
for(int i=1;i<=m;i++)cout<<res[i]<<'\n';
return 0;
}
现在的这个代码速度是。
太慢了,实在是太慢了,考虑怎么卡常。
卡常
首先一定要加上咱们的飞速读入:
namespace Fread
{
const int SIZE = 1 << 16;
char buf[SIZE], *S, *T;
inline char getchar() { if (S == T) { T = (S = buf) + fread(buf, 1, SIZE, stdin); if (S == T) return '\n'; } return *S++; }
}
using namespace Fread;
namespace Fwrite
{
const int SIZE = 1 << 16;
char buf[SIZE], *S = buf, *T = buf + SIZE;
inline void flush() { fwrite(buf, 1, S - buf, stdout); S = buf; }
inline void putchar(char c) { *S++ = c; if (S == T) flush(); }
struct NTR { ~NTR() { flush(); } } ztr;
}
using namespace Fwrite;
#define getchar Fread::getchar
#define putchar Fwrite::putchar
namespace Fastio
{
struct Reader
{
template <typename T> Reader& operator >> (T &x)
{
x = 0;
short f = 1;
char c = getchar();
while (c < '0' || c > '9') { if (c == '-') f *= -1; c = getchar(); }
while (c >= '0' && c <= '9') x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
x *= f;
return *this;
}
Reader& operator >> (double &x)
{
x = 0;
double t = 0;
short f = 1, s = 0;
char c = getchar();
while ((c < '0' || c > '9') && c != '.') { if (c == '-') f *= -1; c = getchar(); }
while (c >= '0' && c <= '9' && c != '.') x = x * 10 + (c ^ 48), c = getchar();
if (c == '.') c = getchar();
else { x *= f; return *this; }
while (c >= '0' && c <= '9') t = t * 10 + (c ^ 48), s++, c = getchar();
while (s--) t /= 10.0;
x = (x + t) * f;
return *this;
}
Reader& operator >> (long double &x)
{
x = 0;
long double t = 0;
short f = 1, s = 0;
char c = getchar();
while ((c < '0' || c > '9') && c != '.') { if (c == '-') f *= -1; c = getchar(); }
while (c >= '0' && c <= '9' && c != '.') x = x * 10 + (c ^ 48), c = getchar();
if (c == '.') c = getchar();
else { x *= f; return *this; }
while (c >= '0' && c <= '9') t = t * 10 + (c ^ 48), s++, c = getchar();
while (s--) t /= 10.0;
x = (x + t) * f;
return *this;
}
Reader& operator >> (__float128 &x)
{
x = 0;
__float128 t = 0;
short f = 1, s = 0;
char c = getchar();
while ((c < '0' || c > '9') && c != '.') { if (c == '-') f *= -1; c = getchar(); }
while (c >= '0' && c <= '9' && c != '.') x = x * 10 + (c ^ 48), c = getchar();
if (c == '.') c = getchar();
else { x *= f; return *this; }
while (c >= '0' && c <= '9') t = t * 10 + (c ^ 48), s++, c = getchar();
while (s--) t /= 10.0;
x = (x + t) * f;
return *this;
}
Reader& operator >> (char &c)
{
c = getchar();
while (c == ' ' || c == '\n' || c == '\r') c = getchar();
return *this;
}
Reader& operator >> (char *str)
{
int len = 0;
char c = getchar();
while (c == ' ' || c == '\n' || c == '\r') c = getchar();
while (c != ' ' && c != '\n' && c != '\r') str[len++] = c, c = getchar();
str[len] = '\0';
return *this;
}
Reader& operator >> (string &str)
{
str.clear();
char c = getchar();
while (c == ' ' || c == '\n' || c == '\r') c = getchar();
while (c != ' ' && c != '\n' && c != '\r') str.push_back(c), c = getchar();
return *this;
}
Reader() {}
} cin;
const char endl = '\n';
struct Writer
{
const int Setprecision = 6;
typedef int mxdouble;
template <typename T> Writer& operator << (T x)
{
if (x == 0) { putchar('0'); return *this; }
if (x < 0) putchar('-'), x = -x;
static short sta[40];
short top = 0;
while (x > 0) sta[++top] = x % 10, x /= 10;
while (top > 0) putchar(sta[top] + '0'), top--;
return *this;
}
Writer& operator << (double x)
{
if (x < 0) putchar('-'), x = -x;
mxdouble _ = x;
x -= (double)_;
static short sta[40];
short top = 0;
while (_ > 0) sta[++top] = _ % 10, _ /= 10;
if (top == 0) putchar('0');
while (top > 0) putchar(sta[top] + '0'), top--;
putchar('.');
for (int i = 0; i < Setprecision; i++) x *= 10;
_ = x;
while (_ > 0) sta[++top] = _ % 10, _ /= 10;
for (int i = 0; i < Setprecision - top; i++) putchar('0');
while (top > 0) putchar(sta[top] + '0'), top--;
return *this;
}
Writer& operator << (long double x)
{
if (x < 0) putchar('-'), x = -x;
mxdouble _ = x;
x -= (long double)_;
static short sta[40];
short top = 0;
while (_ > 0) sta[++top] = _ % 10, _ /= 10;
if (top == 0) putchar('0');
while (top > 0) putchar(sta[top] + '0'), top--;
putchar('.');
for (int i = 0; i < Setprecision; i++) x *= 10;
_ = x;
while (_ > 0) sta[++top] = _ % 10, _ /= 10;
for (int i = 0; i < Setprecision - top; i++) putchar('0');
while (top > 0) putchar(sta[top] + '0'), top--;
return *this;
}
Writer& operator << (__float128 x)
{
if (x < 0) putchar('-'), x = -x;
mxdouble _ = x;
x -= (__float128)_;
static short sta[40];
short top = 0;
while (_ > 0) sta[++top] = _ % 10, _ /= 10;
if (top == 0) putchar('0');
while (top > 0) putchar(sta[top] + '0'), top--;
putchar('.');
for (int i = 0; i < Setprecision; i++) x *= 10;
_ = x;
while (_ > 0) sta[++top] = _ % 10, _ /= 10;
for (int i = 0; i < Setprecision - top; i++) putchar('0');
while (top > 0) putchar(sta[top] + '0'), top--;
return *this;
}
Writer& operator << (char c) { putchar(c); return *this; }
Writer& operator << (char *str)
{
int cur = 0;
while (str[cur]) putchar(str[cur++]);
return *this;
}
Writer& operator << (const char *str)
{
int cur = 0;
while (str[cur]) putchar(str[cur++]);
return *this;
}
Writer& operator << (string str)
{
int st = 0, ed = str.size();
while (st < ed) putchar(str[st++]);
return *this;
}
Writer() {}
} cout;
}
using namespace Fastio;
#define cin Fastio::cin
#define cout Fastio::cout
#define endl Fastio::endl
然后用就直接调 cin/cout/endl 就行。
接着分块题是一定要考虑块长的,实测是 \(900\) 左右表现非常良好,能帮助z最高点你卡到 \(750ms\) 左右。
还是不够快啊,考虑如何继续卡常,首先借助 static 神力,可以把最高点卡到 700ms,最后的最后,把编译选项换成 C++98,把你的小 O2 开开,就可以得到!
不建议观看的卡常后的代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
static int a[N],p[N],bl[N],st[N],ed[N],tot;
const int mx=900;
static bool act[N];
static vector<int>v[mx];
static long long ans;
static int d[N];
namespace Fread
{
const int SIZE = 1 << 16;
char buf[SIZE], *S, *T;
inline char getchar() { if (S == T) { T = (S = buf) + fread(buf, 1, SIZE, stdin); if (S == T) return '\n'; } return *S++; }
}
using namespace Fread;
namespace Fwrite
{
const int SIZE = 1 << 16;
char buf[SIZE], *S = buf, *T = buf + SIZE;
inline void flush() { fwrite(buf, 1, S - buf, stdout); S = buf; }
inline void putchar(char c) { *S++ = c; if (S == T) flush(); }
struct NTR { ~NTR() { flush(); } } ztr;
}
using namespace Fwrite;
#define getchar Fread::getchar
#define putchar Fwrite::putchar
namespace Fastio
{
struct Reader
{
template <typename T> Reader& operator >> (T &x)
{
x = 0;
short f = 1;
char c = getchar();
while (c < '0' || c > '9') { if (c == '-') f *= -1; c = getchar(); }
while (c >= '0' && c <= '9') x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
x *= f;
return *this;
}
Reader& operator >> (double &x)
{
x = 0;
double t = 0;
short f = 1, s = 0;
char c = getchar();
while ((c < '0' || c > '9') && c != '.') { if (c == '-') f *= -1; c = getchar(); }
while (c >= '0' && c <= '9' && c != '.') x = x * 10 + (c ^ 48), c = getchar();
if (c == '.') c = getchar();
else { x *= f; return *this; }
while (c >= '0' && c <= '9') t = t * 10 + (c ^ 48), s++, c = getchar();
while (s--) t /= 10.0;
x = (x + t) * f;
return *this;
}
Reader& operator >> (long double &x)
{
x = 0;
long double t = 0;
short f = 1, s = 0;
char c = getchar();
while ((c < '0' || c > '9') && c != '.') { if (c == '-') f *= -1; c = getchar(); }
while (c >= '0' && c <= '9' && c != '.') x = x * 10 + (c ^ 48), c = getchar();
if (c == '.') c = getchar();
else { x *= f; return *this; }
while (c >= '0' && c <= '9') t = t * 10 + (c ^ 48), s++, c = getchar();
while (s--) t /= 10.0;
x = (x + t) * f;
return *this;
}
Reader& operator >> (__float128 &x)
{
x = 0;
__float128 t = 0;
short f = 1, s = 0;
char c = getchar();
while ((c < '0' || c > '9') && c != '.') { if (c == '-') f *= -1; c = getchar(); }
while (c >= '0' && c <= '9' && c != '.') x = x * 10 + (c ^ 48), c = getchar();
if (c == '.') c = getchar();
else { x *= f; return *this; }
while (c >= '0' && c <= '9') t = t * 10 + (c ^ 48), s++, c = getchar();
while (s--) t /= 10.0;
x = (x + t) * f;
return *this;
}
Reader& operator >> (char &c)
{
c = getchar();
while (c == ' ' || c == '\n' || c == '\r') c = getchar();
return *this;
}
Reader& operator >> (char *str)
{
int len = 0;
char c = getchar();
while (c == ' ' || c == '\n' || c == '\r') c = getchar();
while (c != ' ' && c != '\n' && c != '\r') str[len++] = c, c = getchar();
str[len] = '\0';
return *this;
}
Reader& operator >> (string &str)
{
str.clear();
char c = getchar();
while (c == ' ' || c == '\n' || c == '\r') c = getchar();
while (c != ' ' && c != '\n' && c != '\r') str.push_back(c), c = getchar();
return *this;
}
Reader() {}
} cin;
const char endl = '\n';
struct Writer
{
const int Setprecision = 6;
typedef int mxdouble;
template <typename T> Writer& operator << (T x)
{
if (x == 0) { putchar('0'); return *this; }
if (x < 0) putchar('-'), x = -x;
static short sta[40];
short top = 0;
while (x > 0) sta[++top] = x % 10, x /= 10;
while (top > 0) putchar(sta[top] + '0'), top--;
return *this;
}
Writer& operator << (double x)
{
if (x < 0) putchar('-'), x = -x;
mxdouble _ = x;
x -= (double)_;
static short sta[40];
short top = 0;
while (_ > 0) sta[++top] = _ % 10, _ /= 10;
if (top == 0) putchar('0');
while (top > 0) putchar(sta[top] + '0'), top--;
putchar('.');
for (int i = 0; i < Setprecision; i++) x *= 10;
_ = x;
while (_ > 0) sta[++top] = _ % 10, _ /= 10;
for (int i = 0; i < Setprecision - top; i++) putchar('0');
while (top > 0) putchar(sta[top] + '0'), top--;
return *this;
}
Writer& operator << (long double x)
{
if (x < 0) putchar('-'), x = -x;
mxdouble _ = x;
x -= (long double)_;
static short sta[40];
short top = 0;
while (_ > 0) sta[++top] = _ % 10, _ /= 10;
if (top == 0) putchar('0');
while (top > 0) putchar(sta[top] + '0'), top--;
putchar('.');
for (int i = 0; i < Setprecision; i++) x *= 10;
_ = x;
while (_ > 0) sta[++top] = _ % 10, _ /= 10;
for (int i = 0; i < Setprecision - top; i++) putchar('0');
while (top > 0) putchar(sta[top] + '0'), top--;
return *this;
}
Writer& operator << (__float128 x)
{
if (x < 0) putchar('-'), x = -x;
mxdouble _ = x;
x -= (__float128)_;
static short sta[40];
short top = 0;
while (_ > 0) sta[++top] = _ % 10, _ /= 10;
if (top == 0) putchar('0');
while (top > 0) putchar(sta[top] + '0'), top--;
putchar('.');
for (int i = 0; i < Setprecision; i++) x *= 10;
_ = x;
while (_ > 0) sta[++top] = _ % 10, _ /= 10;
for (int i = 0; i < Setprecision - top; i++) putchar('0');
while (top > 0) putchar(sta[top] + '0'), top--;
return *this;
}
Writer& operator << (char c) { putchar(c); return *this; }
Writer& operator << (char *str)
{
int cur = 0;
while (str[cur]) putchar(str[cur++]);
return *this;
}
Writer& operator << (const char *str)
{
int cur = 0;
while (str[cur]) putchar(str[cur++]);
return *this;
}
Writer& operator << (string str)
{
int st = 0, ed = str.size();
while (st < ed) putchar(str[st++]);
return *this;
}
Writer() {}
} cout;
}
using namespace Fastio;
#define cin Fastio::cin
#define cout Fastio::cout
#define endl Fastio::endl
static int qg(int l,int r,int x){
if(l>r)return 0;
int il=bl[l],ir=bl[r],res=0;
if(il==ir){
for(int i=l;i<=r;i++)if(act[i]&&a[i]>x)res++;
return res;
}
for(int i=l;i<=ed[il];i++)if(act[i]&&a[i]>x)res++;
for(int i=il+1;i<ir;i++)res+=v[i].end()-upper_bound(v[i].begin(),v[i].end(),x);
for(int i=st[ir];i<=r;i++)if(act[i]&&a[i]>x)res++;
return res;
}
static int ql(int l,int r,int x){
if(l>r)return 0;
int il=bl[l],ir=bl[r],res=0;
if(il==ir){
for(int i=l;i<=r;i++)if(act[i]&&a[i]<x)res++;
return res;
}
for(int i=l;i<=ed[il];i++)if(act[i]&&a[i]<x)res++;
for(int i=il+1;i<ir;i++)res+=lower_bound(v[i].begin(),v[i].end(),x)-v[i].begin();
for(int i=st[ir];i<=r;i++)if(act[i]&&a[i]<x)res++;
return res;
}
int main(){
static int n,m;
cin>>n>>m;
tot=(n-1)/mx+1;
for(int i=1;i<=n;i++){
cin>>a[i];
p[a[i]]=i;
}
for(int i=1;i<=m;i++)cin>>d[i];
for(int i=1;i<=tot;i++){
st[i]=(i-1)*mx+1;
ed[i]=min(i*mx,n);
for(int j=st[i];j<=ed[i];j++)bl[j]=i;
}
fill(act+1,act+n+1,1);
for(int i=1;i<=n;i++)v[bl[i]].push_back(a[i]);
for(int i=1;i<=tot;i++)sort(v[i].begin(),v[i].end());
ans=0;
for(int i=1;i<=n;i++)ans+=qg(1,i-1,a[i]);
static vector<long long>res(m+1);
res[1]=ans;
for(int i=1;i<=m;i++){
int val=d[i],pos=p[val],lf=qg(1,pos-1,a[pos]),rt=ql(pos+1,n,a[pos]);
ans-=lf+rt;
int id=bl[pos];
act[pos]=0;
v[id].erase(lower_bound(v[id].begin(),v[id].end(),a[pos]));
if(i<m)res[i+1]=ans;
}
for(int i=1;i<=m;i++)cout<<res[i]<<endl;
return 0;
}
后记
膜拜写树套树写的那么好的巨佬,另外本题十分luo的 cqd 分治我没有想到是否降智?(另外我的 \(O(n \sqrt{n}\log n)\) 跑的比 \(O(n \log ^2 n)\) 快多了),应该写面相对象编程还能更快(估计可以冲进最优解前几页但我不会写)。
愿天堂没有分块!

浙公网安备 33010602011771号