许久没打cf果然会变的好捞
C. Two Arrays
题目链接:https://codeforces.com/contest/1288/problem/C
题目大意 有两个长度为m的数组 a,b,a 非降序,b非升序 bi>=ai 1<=ai,bi<=n
问满足条件的 a b数组有多少个
将B数组翻转一下 bm>=am
则原题变为长度为2m的非降序数组有多少个,这就是个十分煞笔的DP
#include
using namespace std;
typedef long long ll;
const int N=3e5+7;
const ll mod=1e9+7;
int dp[1007][22];
int main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
dp[i][1]=1;
for(int j=1;j<=n;j++)
{
for(int i=2;i<=m*2;i++)
{
dp[j][i]=(dp[j][i-1]+dp[j-1][i-1])%mod;
}
}
ll sum=0;
for(int i=1;i<=n;i++)
{
sum+=dp[i][m*2];
sum%=mod;
}
cout<
当然也有其他的很多好的方法,比如
翻转+公式法 C(2m,2m+n-1);直接就是答案
比如,不翻转
用二维数组保存答案,二维差分维护 dp[i][j] i表示第i位,j表示第i位放的值
则递推公式为 dp[i][j]=dp[i-1][j]+dp[i][j-1]-dp[i-1][j-1]+1;
D. Minimax Problem
题目链接:https://codeforces.com/contest/1288/problem/D
题目大意:选两个数组 每个位上的数字取两个数组该位置较大值 得到新的数组B 要使B数组最小值最大 输出你要选择的两个数组
思路 二分枚举B数组的最小值 然后比较每个数组和mid 会得到100010 类似的字符串s 二进制值记为sss 然后我们想要这个字符串1出现位置的子集,枚举1-2^m 如果 i&sss==i 则说明i是sss的子集,怎么判断mid成立与否呢,那就是例如 101101,只要010010存在就可以了,前面的子集部分都用一个数组存放的话就可以了,然后保存最后一个成立的数组的下标,和B数组最小值,然后暴力找前面的另一个满足条件的数组就可以了
代码有些乱,懒得优化了
好吧!还是整理了下,毕竟懒得已经只会去找容易的事情做了
#include
using namespace std;
typedef long long ll;
const int N=3e5+7;
const ll mod=1e9+7;
int di[N][10];
int n,m,ma,ans,tt;
int solve(int x)
{
int f=0;
int mid=x;
int pan[300]={0};
pan[0]=1;
for(int i=0;i=mid)
{
sss+=(1<=l)
{
sss+=(1<
赛前报名一时爽,赛后补题火葬场
比赛我唯唯诺诺,赛后我重拳出击
E. Messenger Simulator
题目链接:http://codeforces.com/contest/1288/problem/E
题目大意:就是发消息,本来n个人是以1-n的顺序,谁发一个消息就瞬间到首位置,给你他们发消息的序列,求每个人出现的最大位置和最小位置
大体思路:比如i 出现了两次 分别在103 和255 处 (n=500的话) 那么i的最大位置,就是初始值,103-255中出现的数的种数 255-n中出现的数的种数。三者中的最大值,多次和一次类似,没有出现过,位置的最大值就是比他大的数出现的种数+i,
现在的问题是怎样维护该数据,用什么样的数据结构
ans1- 线段树 or 树状数组 维护区间和
(以线段树为例,树状数组和这差不多的)
开m*2的线段树,所有数先放在m+1-2m处,l变量维护左边界,每当有人发消息的话,原位置变0,l变1,原位置到l区间和就更新一次答案。特殊处理没出现过的数即可
ans2- 莫队
存放每个数值出现的位置,生成一堆属于各个数的线段,求的是这些线段中数的种类个数,满足莫队算法要求,离线 O1修改
如果不会莫队就去学一下,不难的很快的,sort下就好
这个的时间复杂度是比ans1 高一些的,但是题目没卡这种算法
具体代码不放了,因为有了思路这题就不难了,有兴趣自己敲下吧!