首页 > 其他分享 >P7650 题解

P7650 题解

时间:2023-10-27 21:55:36浏览次数:32  
标签:P7650 cnt limits int 题解 sum 位置 maxn

非常好题目,第一步都想不出来。

可以观察出来最优方案必定是从大往小将 \(x\) 放到 \(x+1\) 前,有可能不动,中间的比他小的一定要放到前面去。考虑用 dp 计算最小值。

这里是这道题最重要的一步:相对位置的变化非常不好描述,考虑将所有数固定。一次操作改为:不影响其他其他数的位置,将一个数放到任意位置,一个位置上可能有多个数。稍微分析一下可以发现,如果从大到小考虑的话,那么一个数的实际位置就是在他之前的它小的数。这个实际位置的计算还是有些问题的,比如假设当前数是 \(x\) ,如果之前我们将一些数往前换的话,会将一些比 \(x\) 大的数放在 \(x\) 之前,从而影响 \(x\) 的实际位置,但是这种情况十分局限,我们可以直接在前面确定这些比 \(x\) 大的数放到的位置时计算 \(x\) 越过的比他大的数的个数。

有了这种计算方式,我们可以设计 \(dp\),跟据我们之前推导的交换方法,我们可以设计 \(f_{i,j}\) 表示当前考虑完 \(i,i+1,\dots,n\),且 \(i\) 放在位置 \(j\) 的最小代价,对于一个 \(i\),我们有两种决策,第一种是将其放置在 \(i+1\) 的位置上。设原序列上 \(i\) 的位置是 \(j\),\(i+1\) 的位置是 \(k\),则若忽略那些大于 \(i\) 且在 \(i\) 之前的数,\(i\) 的实际位置是 \(\sum\limits_{t<j}{[a_t<a_i]+1}\),\(i+1\) 位置同理,稍微修点细节可以得出转移:

\[f_{i,k} \leftarrow f_{i+1,k} + \sum\limits_{t<j}{[a_t<a_i]} + \sum\limits_{t<k}{[a_t<a_i]} + 2 \]

我们考虑第二种决策,即不改变 \(i\) 的位置。此时我们需要考虑第一种转移时漏下的贡献,按照第一种转移时的定义,不难得出少算的贡献为 \(\sum\limits_{j<t<k}{[a_t<a_i](a_i-a_t)}\),得出转移:

\[f_{i,j} \leftarrow f_{i+1,k} + \sum\limits_{j<t<k}{[a_t<a_i](a_i-a_t)} \]

构造方案的话,倒推就行了。

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e3+10,inf=0x3f3f3f3f;
int n,a[maxn],f[maxn][maxn],g[maxn][maxn],t[maxn],pos[maxn];
int sum[maxn][maxn],cnt[maxn][maxn];
int main()
{
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    cin>>n;
    for(int i=1;i<=n;i++)
        cin>>a[i],
        t[i]=a[i];
    sort(t+1,t+1+n);
    for(int i=1;i<=n;i++)
    {
        a[i]=lower_bound(t+1,t+1+n,a[i])-t;
        a[i]=n+1-a[i];
        pos[a[i]]=i;
    }
    a[n+1]=n+1;
    for(int i=1;i<=n+1;i++)
    {
        for(int j=1;j<=n+1;j++)sum[i][j]=sum[i-1][j],cnt[i][j]=cnt[i-1][j];
        for(int k=a[i];k<=n+1;k++)sum[i][k]+=a[i],cnt[i][k]++;
    }
    memset(f,0x3f,sizeof f);
    f[n+1][n+1]=0;
    for(int x=n;x>=0;x--)
        for(int q=n+1;q>=1;q--)
            if(f[x+1][q]!=inf)
            {
                if(!x)continue;
                int p=pos[x];
                if(f[x][q]>f[x+1][q]+cnt[p-1][x-1]+cnt[q-1][x-1]+2)
                    g[x][q]=q,f[x][q]=f[x+1][q]+cnt[p-1][x-1]+cnt[q-1][x-1]+2;
                if(p<q&&f[x][p]>f[x+1][q]+(cnt[q-1][x-1]-cnt[p][x-1])*x-(sum[q-1][x-1]-sum[p][x-1]))
                    g[x][p]=q,f[x][p]=f[x+1][q]+(cnt[q-1][x-1]-cnt[p][x-1])*x-(sum[q-1][x-1]-sum[p][x-1]);
            }
    vector< int > Ans;
    int mn=inf,q=0;
    for(int i=1;i<=n+1;i++)if(f[1][i]<mn)mn=f[1][i],q=i;
    for(int x=1;x<=n;x++)Ans.push_back(q),q=g[x][q];
    reverse(Ans.begin(),Ans.end());
    vector< pair<int,int> > val;
    for(int i=0;i<Ans.size();i++)
        if(Ans[i]!=pos[n-i])
        {
            int pnow=pos[n-i],qnow=Ans[i];
            int rp=cnt[pnow-1][n-i-1]+1,rq=cnt[qnow-1][n-i-1]+1;
            for(int j=0;j<i;j++)rp+=Ans[j]<pnow;
            for(int j=0;j<i;j++)rq+=Ans[j]<qnow;
            if(rp==n+1)rp--;
            if(rq==n+1)rq--;
            val.push_back({rp,rq});
        }
    cout<<val.size()<<'\n';
    for(auto [x,y]:val)cout<<x<<' '<<y<<'\n';
}

标签:P7650,cnt,limits,int,题解,sum,位置,maxn
From: https://www.cnblogs.com/hikkio/p/17793210.html

相关文章

  • 题解 P4285 [SHOI2008] 汉诺塔
    具体思路设\(f_{i,x}\)表示\(i\)个盘子从\(x\)柱子出发的步数。设\(g_{i,x}\)表示\(i\)个盘子从\(x\)柱子出发到哪个柱子。记\(y=g_{i-1,x}\),\(z=6-x-y\)。其中,\(y\)代表将前\(i-1\)个盘子从\(x\)柱子移到的柱子,\(z\)代表剩下的那个柱子。分类讨论。若......
  • P2230 Tinux系统 题解
    传送门提供一种基于贪心的解法。首先是将\(p\)从小到大排序考虑到该系统是一棵树,所以对于系统中的每个点,我们记:\(tr_{son}\)表示该点(目录)的儿子的位置\(tr_{fa}\)表示该点(目录)的父亲的位置\(tr_{siz}\)表示该点(目录)包含的点的个数\(tr_{cnt}\)表示该点(目录)有......
  • YACS 2023年10月月赛 甲组 题解
    目前只有T2,其他题目我在看。题目链接1题目链接2题目链接3T2很简单的一道题,将图分为若干个连通块,然后分别求最小生成树。从货车运输中得到的结论,最小生成树等价于最小边权上限生成树,也就是它也能够保证选出边中最大的边权最小。而题目中明确说了这个最小生成树的权值是其中......
  • [NOI2010] 超级钢琴 题解
    [NOI2010]超级钢琴题解说点闲话原本不想写这个题解的但是看到我的代码居然长度为2048B->刚好2KiB,然后还跟题号相同QAQ题目翻译给你一段序列,求出其中从第\(1\)大到第\(k\)大的子区间的和。思路解析首先可以想到一个简单的暴力,对于每一个区间开头\(i\),和区间结尾\(j\),都求......
  • 2023 CSP-J2 T1,2,3题解
    今年的\(CSP−J\)对本蒟蒻来说有点难度。。。A[CSP-J2023]小苹果题目描述小Y的桌子上放着\(n\)个苹果从左到右排成一列,编号为从\(1\)到\(n\)。小苞是小Y的好朋友,每天她都会从中拿走一些苹果。每天在拿的时候,小苞都是从左侧第\(1\)个苹果开始、每隔\(2\)个......
  • [TJOI2013] 松鼠聚会 题解
    [TJOI2013]松鼠聚会题解切比雪夫距离切比雪夫距离指的是在平面上的两个点\((x_1,y_1)\),\((x_2,y_2)\)之间横纵坐标之差绝对值中的大者。用公式表示则是\(f(a,b)=max(|x_a-x_b|,|y_a-y_b|)\)。切比雪夫距离与曼哈顿距离之间可以相互转换切比雪夫—>曼哈顿:\((x_1,y_1)\),\((x......
  • [NOIP 2013提高组]货车运输 题解
    [NOIP2013提高组]货车运输题解前置知识Kruskal重构树(内含讲解)+任意一种LCA题目翻译\(n\)座城市,\(m\)条道路,\(q\)次询问,每次求两个点\(x,y\)之间所有路径的最小值的最大值。题目分析其实学了Kruskal重构树差不多看到这个题目就知道怎么写了。其实就是Kruskal重构树的板子,......
  • 问题解决
    pip源问题解决使用pip安装pytorch出现WARNING:Retrying(Retry(total=4,connect=None,read=None,redirect=None,status=None))报错使用换源解决问题pip3configlistpip3configsetglobal.index-urlhttps://mirrors.aliyun.com/pypi/simple/pip3configlist国内......
  • 第四章苏格拉底问答、实践过程截图、遇到问题解决问题截图,代码链接
    代码#include<stdio.h>#include<stdlib.h>#include<pthread.h>#defineN4intA[N][N],sum[N];void*func(voidarg){intj,row;pthread_ttid=pthread_self();row=(int)arg;printf("Thread%d[%lu]computessumofrow%d\n"......
  • Python打不开问题解决方案大全
    在使用Python进行编程开发的过程中,我们不可避免会遇到Python打不开的问题。这些问题可能是由于环境配置、包管理和依赖文件等问题所导致的,但不管是何种原因,我们都需要解决它们才能顺利地进行工作。本文将从多个方面为大家详细介绍Python打不开问题的解决方法。一、Python环境配......