T1
Description
求由 1 到 n 一共 n 个数字组成的所有排列中,逆序对个数为 k 的有多少个
Hint
乍一看是数学题
我们考虑递推 用f[i][j]表示1~i的所有排列中逆序对为k的有多少个
我们注意到把i加进去由于1~i-1都比i小,所以i可以产生0~i个逆序对
得到公式:
即 f[i][j]=f[i−1][j]+f[i−1][j−1]+.....+f[i−1][j−i]
而我们注意到:
即 f[i][j−1]=f[i−1][j−1]+.....+f[i−1][j−1−k]+f[i−1][j−i]
所以:
这就是一个很简单很普遍的递推(动归)优化
而上式条件: j<i ,同样的当 j≥i 时
通过一样的推导我们能得到:
最后我们输出f[n][k]-f[n][k-1]
Code
T2
Description
一个长度为
n(n≤2000)
的序列,对于每个位置
i
的数
精简题意:对于任何一个 ai ,它的优美值就是最大的包含它且以它的为中位数的区间的长度(这里要注意题意要求以双关键字排序),给定Q个询问求 [l,r] 中优美值的最大值
Hint
我们注意到对于一个特定的序列每个元素的优美值一定,所以我们只要求出每个优美值,问题就变成了求特定区间的最大值,就可以用RMQ解决
所以我们关键是求出每个元素的优美值,注意到数据范围我们只能
O(n2)
做
然后我就打炸了
然后就很简单了,向左把大于
ai
的视为1,小于
ai
的视为-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
//待补充