CQOI2011 动态逆序对

题目描述

题解:

将前n个数看作插入,后m个数仍看作删除。

然后就是cdq分治。

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 150050
#define ll long long
inline int rd()
{
    int f=1,c=0;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){c=10*c+ch-'0';ch=getchar();}
    return f*c;
}
int n,m;
struct node
{
    int a,b,c,d;
}p[N],tmp[N];
int pos[N];
ll ans[N];
int f[N];
void up(int x,int d)
{
    if(!x)return ;
    while(x<=100000)f[x]+=d,x+=(x&(-x));
}
int down(int x)
{
    if(!x)return 0;
    int ret = 0;
    while(x)ret+=f[x],x-=(x&(-x));
    return ret;
}
void Sort(int l,int r)
{
    int mid = (l+r)>>1;
    int i=l,j=mid+1,k=l;
    while(i<=mid&&j<=r)
    {
        while(p[i].a<=p[j].a&&i<=mid)
        {
            tmp[k]=p[i];
            i++,k++;
        }
        while(p[i].a>p[j].a&&j<=r)
        {
            tmp[k]=p[j];
            j++,k++;
        }
    }
    while(i<=mid)
    {
        tmp[k]=p[i];
        i++,k++;
    }
    while(j<=r)
    {
        tmp[k]=p[j];
        j++,k++;
    }
    for(i=l;i<=r;i++)
        p[i]=tmp[i];
}
void cdq(int l,int r)
{
    if(l==r)return ;
    int mid = (l+r)>>1;
    cdq(l,mid),cdq(mid+1,r);
    Sort(l,mid),Sort(mid+1,r);
    int i,j;
    j = l;
    for(i=mid+1;i<=r;i++)
    {
        while(j<=mid&&p[i].a>=p[j].a)up(p[j].b,p[j].d),j++;
        ans[p[i].c]+=p[i].d*(down(n)-down(p[i].b));
    }
    for(j=j-1;j>=l;j--)up(p[j].b,-p[j].d);
    j = mid;
    for(i=r;i>mid;i--)
    {
        while(j>=l&&p[i].a<=p[j].a)up(p[j].b,p[j].d),j--;
        ans[p[i].c]+=p[i].d*down(p[i].b-1);
    }
    for(j=j+1;j<=mid;j++)up(p[j].b,-p[j].d);
}
int main()
{
    n=rd(),m=rd();
    for(int x,i=1;i<=n;i++)
    {
        x=rd();
        p[i].a=i,p[i].b=x,p[i].c=0,p[i].d=1;
        pos[x]=i;
    }
    for(int x,j,i=1;i<=m;i++)
    {
        x=rd();j=i+n;
        p[j].a=pos[x],p[j].b=x,p[j].c=i,p[j].d=-1;
    }
    cdq(1,n+m);
    for(int i=1;i<=m;i++)ans[i]+=ans[i-1];
    for(int i=0;i<m;i++)printf("%lld\n",ans[i]);
    return 0;
}
//pos val tim wgt 

 

转载于:https://www.cnblogs.com/LiGuanlin1124/p/10143340.html

# P3157 [CQOI2011] 动态逆序对 ## 题目描述 对于序列 $a$,它的逆序对数定义为集合 $$\{(i,j)| i<j \wedge a_i > a_j \}$$ 中的元素个数。 现在给出 $1\sim n$ 的一个排列,按照某种顺序依次删除 $m$ 个元素,你的任务是在每次删除一个元素**之前**统计整个序列的逆序对数。 ## 输入格式 第一行包含两个整数 $n$ 和 $m$,即初始元素的个数和删除的元素个数。 以下 $n$ 行,每行包含一个 $1 \sim n$ 之间的正整数,即初始排列。 接下来 $m$ 行,每行一个正整数,依次为每次删除的元素。 ## 输出格式 输出包含 $m$ 行,依次为删除每个元素之前,逆序对的个数。 ## 输入输出样例 #1 ### 输入 #1 ``` 5 4 1 5 3 4 2 5 1 4 2 ``` ### 输出 #1 ``` 5 2 2 1 ``` ## 说明/提示 【数据范围】 对于 $100\%$ 的数据,$1\le n \le 10^5$,$1\le m \le 50000$。 【样例解释】 删除每个元素之前的序列依次为: $$1,5,3,4,2$$ $$1,3,4,2$$ $$3,4,2$$ $$3,2$$ 为什么WA了 ```cpp #include <iostream> #include <algorithm> #include <vector> using namespace std; struct BIT { #define lowbit(x) x & (-x) vector<int> tr; BIT(int maxn) {tr.assign(maxn + 1, 0); } void add(int idx, int val) { for (; idx < tr.size(); idx += lowbit(idx)) tr[idx] += val; } int query(int idx) { int res = 0; for (; idx; idx -= lowbit(idx)) res += tr[idx]; return res; } }; class CDQ { public: void add(int x, int y, int z) { a.push_back(Node{x, y, z, 0, 0}); } void work(int ans[], int maxn) { b.assign(a.begin(), a.end()); sort(b.begin(), b.end()); for (int i = 0; i < b.size(); i++) { if (i == 0 || b[i - 1] < b[i]) c.push_back(b[i]); c.back().cnt++; } BIT tr(maxn); cdq(tr, 0, c.size() - 1); sort(c.begin(), c.end()); for (int i = 0; i < a.size(); i++) { auto tmp = lower_bound(c.begin(), c.end(), a[i]); ans[i + 1] += tmp->ans + tmp->cnt - 1; } } private: void cdq(BIT& tr, int l, int r) { if (l >= r) return ; int mid = l + r >> 1; cdq(tr, l, mid), cdq(tr, mid + 1, r); sort(&c[l], &c[mid] + 1, [](Node& p, Node& q) { return p.y < q.y; }); sort(&c[mid + 1], &c[r] + 1, [](Node& p, Node& q) { return p.y < q.y; }); int i = l, j = mid + 1; for (; j <= r; j++) { for (; i <= mid && c[i].y <= c[j].y; i++) tr.add(c[i].z, c[i].cnt); c[j].ans += tr.query(c[j].z); } for (int k = l; k < i; k++) tr.add(c[k].z, -c[k].cnt); } struct Node { int x, y, z, cnt, ans; bool operator < (const Node& rhs) const { if (x != rhs.x) return x < rhs.x; return y == rhs.y ? z < rhs.z : y < rhs.y; } }; vector<Node> a, b, c; }; const int MAX_N = 1e5 + 50; int n, m, a[MAX_N], p[MAX_N]; int tim[MAX_N], cnt[MAX_N], ans[MAX_N]; int main() { cin >> n >> m; for (int i = 1; i <= n; i++) cin >> a[i]; fill(tim + 1, tim + n + 1, m); for (int i = 1; i <= m; i++) cin >> p[i], tim[p[i]] = i; CDQ cdq; for (int i = 1; i <= n; i++) cdq.add(i, n - a[i], m - tim[i] + 1); cdq.work(cnt, m); for (int i = 1; i <= n; i++) ans[tim[i]] += cnt[i]; for (int i = m; i > 1; i--) ans[i - 1] += ans[i]; for (int i = 1; i <= m; i++) cout << ans[i] << '\n'; return 0; } ```
08-12
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值