2017-11-3 题解

T1

Description
求由 1 到 n 一共 n 个数字组成的所有排列中,逆序对个数为 k 的有多少个

Hint
乍一看是数学题
我们考虑递推 用f[i][j]表示1~i的所有排列中逆序对为k的有多少个
我们注意到把i加进去由于1~i-1都比i小,所以i可以产生0~i个逆序对
得到公式:$$f[i][j]=\sum_{k=0}^{i} {f[i-1][j-k]}$$
即$f[i][j]=f[i-1][j]+f[i-1][j-1]+…..+f[i-1][j-i]$
而我们注意到:$$f[i][j-1]=\sum_{k=0}^{i} {f[i-1][j-1-k]}$$
即$f[i][j-1]=f[i-1][j-1]+…..+f[i-1][j-1-k]+f[i-1][j-i]$
所以:$$f[i][j]=f[i][j-1]+f[i-1][j]-f[i-1][j-i]$$
这就是一个很简单很普遍的递推(动归)优化
而上式条件:$j<i$,同样的当$j≥i$时$$f[i][j]=\sum_{k=0}^{j} {f[i-1][j-k]}$$
通过一样的推导我们能得到:$$f[i][j]=f[i][j-1]+f[i-1][j]$$
最后我们输出f[n][k]-f[n][k-1]

Code


T2

Description
一个长度为$n(n\leq2000)$的序列,对于每个位置$i$的数$a_i$都有一个优美值,其定义是:找到序列中最长的一段$[l,r]$,满足$l≤i≤r$,且$[l,r]$中位数为$a_i$(我们比较序列中两个位置的数的大小时,以数值为第一关键字,下标为第二关键字比较.这样的话$[l,r]$的长度只有可能是奇数)$r-l+1$就是$i$的优美值.有Q个询问,每个询问$[l,r]$表示查询区间$[l,r]$内优美值的最大值

精简题意:对于任何一个$a_i$,它的优美值就是最大的包含它且以它的为中位数的区间的长度(这里要注意题意要求以双关键字排序),给定Q个询问求$[l,r]$中优美值的最大值

Hint
我们注意到对于一个特定的序列每个元素的优美值一定,所以我们只要求出每个优美值,问题就变成了求特定区间的最大值,就可以用RMQ解决
所以我们关键是求出每个元素的优美值,注意到数据范围我们只能$O(n^2)$做
然后我就打炸了
然后就很简单了,向左把大于$a_i$的视为1,小于$a_i$的视为-1,向右把大于的视为-1小于的视为1
(如果不懂为什么要这么做可以去做一下“非常男女”计划)

Code

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#include<vector>
#include<cmath>
#define siz 2100
using namespace std;
int n,m,s,t,cnt;
int a[siz],fir[siz*2],beauty_[siz],T[siz][20];
int main(){

    scanf("%d",&n);
    for(int i=1;i<=n;++i) scanf("%d",&a[i]),beauty_[i]=1;
    for(int i=1;i<=n;++i) {
        memset(fir,0,sizeof(fir));
        fir[n]=i; //i=1时第一个循环不会进行要先赋值
        cnt=0;
        for(int j=i-1;j;--j) {
            if(a[j]<=a[i]) cnt--; //注意这道题的"大于"和"小于"被重定义了
            else cnt++;
            fir[cnt+n]=j;
        }
        beauty_[i]=i-fir[n]+1; //i=n第二个循环不会进行要先赋值
        cnt=0;
        for(int j=i+1;j<=n;++j) {
            if(a[j]>=a[i]) cnt--;
            else cnt++;
            if(fir[cnt+n]) beauty_[i]=max(beauty_[i],j-fir[cnt+n]+1);
        }
        T[i][0]=beauty_[i];
    }
    for(int j=1;(1<<j)<=n;++j)
     for(int i=1;(i+(1<<j)-1)<=n;++i)
      T[i][j]=T[i][j-1]>T[i+(1<<(j-1))][j-1]?T[i][j-1]:T[i+(1<<(j-1))][j-1];
    scanf("%d",&m);
    while(m--) {
        scanf("%d%d",&s,&t);
        int j=log2(t-s+1);
        printf("%d\n",T[s][j]>T[t-(1<<j)+1][j]?T[s][j]:T[t-(1<<j)+1][j]);
    }
    return 0;
}

T3

Description
一开始你有一个空集,集合可以出现重复元素,然后有 Q 个操作

  1. add s
    在集合中加入数字 s。
  2. del s
    在集合中删除数字 s。保证 s 存在
  3. cnt s
    查询满足a&s=a条件的 a 的个数

Hint
这看上去是一道数据结构题
我考场上分析:
要使a&s=a,则s一定要大于等于a(根据&定义很好分析),所以我们每次只要找比s小的a就好了
于是我打了一个二叉排序树希望多水一些分……
然后和暴力一样的分…..

正解:分块
//表示作为没打过分块的蒟蒻,先自学完再来打题解

Code
//待补充

posted @ 2018-02-08 08:39  Nepenthe  阅读(159)  评论(0)    收藏  举报


删边加边,浮生建模