noip2013火柴排队--乱搞??

链接:https://www.luogu.org/problemnew/show/P1966

这题我糊了一个假结论就过去了,也不知道这个结论为什么是对的。。首先sigma((ai-bi)^2)可以拆开,那么ai^2+bi^2是个常量,而求这个最小就是sigma(-1*ai*bi)最小即sigma(ai*bi)最大,那么贪心的想一想a数组中最大的数让它被计算尽可能多次,也就是想必要和b数组中最大的对应,那么次大、次次大...也是同理,也就是要让a数组某一排名的和b数组某一排名的对上(注意可能有相同的排名)。

接下来就是我和标算不一样的地方。。

标算离散化a,b数组,然后按a为关键字排b,显然之后为了一一对应就是求逆序对数了,虽然个人觉得有点难想到,但正确性一目了然,很好很优越。。

我的呢,先猜了一波想必在a里交换和在b里交换是一样的,那么就只用考虑a序列。然后呢,再猜一波可以贪心从左往右考虑,就是从第一位开始向右枚举,找到a序列中这个位置及右侧第一个与b这个位置对应的,计算它交换过来的贡献,然后因为它交换的路径上的点右移了1,就用树状数组记录一下,计算到它们时查询被右移了多少加上去。。。虽然觉得很假,但手模一波发现貌似自己hack不掉,就信仰打了,没想到真能a。。。

// luogu-judger-enable-o2
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=100010;
const ll mod=99999997;
struct moha{
    ll x;int id;
}c[N],d[N];
int n,ps[100010];ll a[N],b[N],tr[100010];
vector<int>mp[100010];
bool cmp(moha x,moha y)
{return x.x==y.x?x.id<y.id:x.x<y.x;}
int ask(int x)
{
    int res=0;
    for(int i=x;i;i-=i&(-i))
        res+=tr[i];
    return res;	
}
void add(int r)
{
    for(int i=1;i<=n;i+=i&(-i))
        tr[i]++;
    for(int i=r+1;i<=n;i+=i&(-i))
        tr[i]--;	
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%lld",&a[i]),c[i].x=a[i],c[i].id=i;
    for(int i=1;i<=n;i++)
        scanf("%lld",&b[i]),d[i].x=b[i],d[i].id=i;
    sort(c+1,c+n+1,cmp),sort(d+1,d+n+1,cmp);
    int tp1=0,tp2=0,nw;ll ans=0;
    for(int i=1;i<=n;i++)
    {
        a[c[i].id]=++tp1;
        b[d[i].id]=++tp2;
    }
    for(int i=1;i<=n;i++)ps[a[i]]=i;
    for(int i=1;i<=n;i++)
    {
        nw=ps[b[i]];
        nw+=ask(nw);
        ans=(ans+(nw-i))%mod;
        add(ps[b[i]]);
    }
    cout<<ans;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值