冲刺Noip2017模拟赛6 解题报告——五十岚芒果酱
1.ksum(ksum)
【问题描述】
Peter喜欢玩数组。NOIP这天,他从Jason手里得到了大小为n的一个正整数
数组。
Peter求出了这个数组的所有子段和,并将这n(n+)/2个数降序排序,他想
知道前k个数是什么。
【输入格式】
输入文件名为 ksum.in。
输入数据的第一行包含两个整数 n 和 k。
接下来一行包含 n 个正整数,代表数组。
【输出格式】
输出文件名为 ksum.out。
输出 k 个数,代表降序之后的前 k 个数,用空格隔开。
【输入输出样例】
ksum.in ksum.out 【数据规模与约定】
对于所有数据,满足 ai≤^,k≤n(n+)/,n≤,k≤
测试点编号 n≤ k≤
题目
tag:贪心
思路:每一个数都是正整数,意味着子段的大小一定小于父段,那我们必须先选择父段再选择子段。于是我们从整个数组1~n开始贪心,将每次选择的子段l~r再分成l+1~r和l~r-1两个更小的子段放入优先队列。但是,有可能会选择到重复的子段,我的处理是将左右端点哈希。记得求前缀和。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#define maxn 100010
#define ll long long
using namespace std;
map<ll,int> Hash;
ll sum[maxn],ans[maxn];
int a[maxn],n,k,tot;
struct NUM
{
int l,r;
ll val;
bool operator<(const NUM &x)const{return val<x.val;}
};
priority_queue<NUM>Q;
void Push(NUM x)
{
ll num=1ll*x.l+1ll*x.r*;
if(!Hash.count(num)){
Hash[num]=;
Q.push(x);
}
}
int main()
{
//freopen("ksum.in","r",stdin);
//freopen("ksum.out","w",stdout);
scanf("%d%d",&n,&k);
for(int i=;i<=n;++i){
scanf("%d",&a[i]);
sum[i]=sum[i-]+a[i];
}
NUM x;
x.l=,x.r=n,x.val=sum[n];
Push(x);
while(){
NUM now=Q.top();
Q.pop();
ans[++tot]=now.val;
if(tot==k) break;
int l=now.l,r=now.r;
NUM x,y;
x.l=l+,x.r=r;
y.l=l,y.r=r-;
x.val=sum[x.r]-sum[x.l-];
y.val=sum[y.r]-sum[y.l-];
Push(x);
Push(y);
}
for(int i=;i<k;++i) cout<<ans[i]<<" ";
cout<<ans[k]<<endl;
return ;
}
2.奇袭(raid)
【问题描述】
由于各种原因,桐人现在被困在Under World(以下简称UW)中,而UW马上
要迎来最终的压力测试——魔界入侵。
唯一一个神一般存在的Administrator被消灭了,靠原本的整合骑士的力量
是远远不够的。所以爱丽丝动员了UW全体人民,与整合骑士一起抗击魔族。
在UW的驻地可以隐约看见魔族军队的大本营。整合骑士们打算在魔族入侵
前发动一次奇袭,袭击魔族大本营!
为了降低风险,爱丽丝找到了你,一名优秀斥候,希望你能在奇袭前对魔
族大本营进行侦查,并计算出袭击的难度。
经过侦查,你绘制出了魔族大本营的地图,然后发现,魔族大本营是一个N
×N的网格图,一共有N支军队驻扎在一些网格中(不会有两只军队驻扎在一起)。
在大本营中,每有一个k×k(≤k≤N)的子网格图包含恰好k支军队,我们
袭击的难度就会增加1点。
现在请你根据绘制出的地图,告诉爱丽丝这次的袭击行动难度有多大。
【输入格式】
第一行,一个正整数N,表示网格图的大小以及军队数量。
接下来N行,每行两个整数,Xi,Yi,表示第i支军队的坐标。
保证每一行和每一列都恰有一只军队,即每一个Xi和每一个Yi都是不一样
的。
【输出格式】
一行,一个整数表示袭击的难度。
【输入输出样例】
raid.in raid.out 【样例解释】
显然,分别以(,)和(,)为左上,右下顶点的一个子网格图中有3支军队,
这为我们的难度贡献了1点。
类似的子网格图在原图中能找出10个。
【数据范围】
对于30%的数据,N ≤
对于60%的数据,N ≤
对于100%的数据,N ≤
题目
tag:分治、单调栈、玄学(雾)
思路:暴力可以二维前缀和或树状数组。这道题的模型有一定特殊性,每行每列都只有一个点,为了达到O(nlogn)的水平我们缩成一维。通过仔细观察发现,每一个符合条件的子段(子矩阵)都满足maxx-minx=len-1=r-l,原来是RMQ问题。
之后是玄学分治+单调栈,对于每个分治区间,用mid分成左右两侧,有四种情况,最小值和最大值在左左,左右,右左,右右,如果把子段反转,可以把右左和右右变成左右和左左,最后需要讨论两种情况,我们始终抓住函数的单调性来考虑,先以mid为分界线求出“前后缀最值”(min单减 ,max单增),既然要快速的查找,将公式移项,maxx-r=minx-l,我们造一个桶,将maxx-r放进去,让minx-l去找它对应的桶(建议结合代码理解)。接着是求“左左”的情况,为什么要满足j>mid?(注意,下面的几行我想了2h)像(1)、(1,2,3)这样的序列虽然满足条件,但不符合j>mid,他们会在分治的时候被解决,真正需要计算的是(1,5,2,3,4)这样,mid在2那里,1+(5-1)=5,1和5需要中间的2,3,4来补充,那么我们发现,所谓“左左”的情况并不是整个区间都在左哦。
接下来就是重头戏“左右”,也就是最小值在左最大值在右,之前求的前后缀最值就派上用场了,以最左端为标准求出满足条件的区间,最大值向右单增,也就是说越往右越大,越容易满足Rmax>Lmax,最小值相反(有些细节写在注释里了),遍历左区间,不断修改原来的范围,一一对应。
数组反转用stl的reserve,细节——(1,2,3,4,5)的mid在3,左(1,2,3),右(4,5),反转后(5,4,3,2,1)mid应该移到4的位置,此时左(5,4),右(3,2,1)。处理了另外两种情况。
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<stack>
#define maxn 100010
#define inf 1<<29
#define ll long long
using namespace std;
int Lmax[maxn],Lmin[maxn],Rmax[maxn],Rmin[maxn],tong[maxn],a[maxn],n;
ll cal(int l,int r,int mid)
{
ll ret();
Lmax[mid]=Lmin[mid]=a[mid];
Rmax[mid+]=Rmin[mid+]=a[mid+];
for(int i=mid-;i>=l;--i){
Lmax[i]=max(Lmax[i+],a[i]);
Lmin[i]=min(Lmin[i+],a[i]);
}
for(int i=mid+;i<=r;++i){
Rmax[i]=max(Rmax[i-],a[i]);
Rmin[i]=min(Rmin[i-],a[i]);
}
for(int i=l;i<=mid;++i){
int j=i+Lmax[i]-Lmin[i];
if(j>mid&&Rmax[j]<Lmax[i]&&Rmin[j]>Lmin[i]) ret++;//最大值和最小值都在左边 由于j可能比n还大就导致数组需要开2倍
}
int p1=mid+,p2=mid;//p1 max指针 p2 min指针
while(p1<=r&&Rmax[p1]<Lmax[l]) tong[Rmax[p1]-p1]--,p1++;//左不符合 右符合 最后指到第一个符合的
while(p2<r&&Rmin[p2+]>Lmin[l]) p2++,tong[Rmax[p2]-p2]++;//左符合 右不符合 最后指到最后一个不符合的
for(int i=l;i<=mid;++i){
while(p1>mid+&&Rmax[p1-]>Lmax[i]) p1--,tong[Rmax[p1]-p1]++;//将原来不符合变为符合
while(p2>mid&&Rmin[p2]<Lmin[i]) tong[Rmax[p2]-p2]--,p2--;//将符合变为不符合
ret+=max(tong[Lmin[i]-i],);//万一是负的
}
for(int i=mid+;i<=r;++i) tong[Rmax[i]-i]=;//区间清零 不要脑子一热memset了
return ret;
}
ll solve(int l,int r)
{
ll ret();
if(l==r) return 1ll;
int mid=(l+r)>>;
ret=solve(l,mid)+solve(mid+,r);
ret+=cal(l,r,mid);
reverse(a+l,a+r+);//区间反转
if((r-l+)&) mid--;//mid位置修正
ret+=cal(l,r,mid);
reverse(a+l,a+r+);
return ret;
}
int main()
{
//freopen("raid.in","r",stdin);
//freopen("raid.out","w",stdout);
int x,y;
scanf("%d",&n);
for(int i=;i<=n;++i){
scanf("%d%d",&x,&y);
a[x]=y;//转一维
}
printf("%I64d\n",solve(,n));
return ;
}
3.十五数码(fifteen)
【题目描述】
给出起始顺序,要求通过 的移动(与上下左右交换),排成以下顺序: 【输入格式】
从文件 fiften.in 中读入数据,四个数一行,共四行。
【输出格式】
输出到文件 fifteen.out 中。
输出最少移动次数。如果无解输出 No。
【样例 输入】 【样例 输出】 【样例 输入】 【样例 输出】 【数据范围】
对于 %的数据,保证有解并且 Ans <= ;
对于 %的数据,保证有解并且 Ans <= ;
存在 %的数据无解。
对于 %的数据,如果有解,Ans <= ;
题目
tag:搜索
思路:状态很多,数字很多,哈希很困难。可以用双向广搜,然后正解是一堆位运算的IDA*,因为有点想法我尽量说自己的思路……IDA*嘛,我做了一些估价,初始状态到末状态每个数字的“曼哈顿距离”(坐标距),加起来就是最少要移动的次数(重要剪枝!),作为我们枚举deep的起点,终点是50,到50可以不做直接输出结果(但是没骗到分)。
dfs的过程是我们需要重点研究的。估价分单个数字和整体(感觉整体的更重要),简要来说,如果明显无法在剩余步数找到答案就不动了。有int全部套register,有if的尽量减少,有函数的尽量不用(重要剪枝!亲测swap能直接拖1s),小函数define掉(不过据说考试的时候不太保险?),还有个“手刀保护sdbh”避免来回移动(重要剪枝!从4^x到3^x的变化)。还有dalao教的exit(0)直接结束程序,biao脸的卡时还特判最后才A掉……
无解的情况需要求逆序数+空位到(4,4)的坐标距(因为0还要移过去啊……另外之后提到的逆序数包括空位的情况),那么问题来了为什么要求逆序数,我们看末状态的逆序数是15,一个奇数,而每次移动无论横纵的改变量都是偶数,奇+-偶=奇,初状态也必须是奇数才行。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<ctime>
#define aabs(x) (x>=0?x:-(x))
#define ok(x,y) x>0&&x<5&&y>0&&y<5
#define ll long long
using namespace std;
int dx[]={,,,-},dy[]={,,-,},mp[][],zb[][],dis[],deep,S,T,tot,start,sum;
int cal(register int x,register int y){return (x-)*+y;}
void dfs(register int f,register int DIS,register int x,register int y,register int sdbh)
{
if(clock()-start>=){
if(deep<) printf("");
else printf("");
exit();
}
if(f==deep){
if(!DIS){
printf("%d\n",deep);
exit();
}
return;
}
for(register int i=;i<;++i){
register int X=dx[i]+x,Y=dy[i]+y,num=mp[X][Y];
if(num!=sdbh&&ok(X,Y)&&deep-f>=dis[num]){
mp[x][y]=num;
mp[X][Y]=;
register int change=aabs(x-zb[num][])+aabs(y-zb[num][])-aabs(X-zb[num][])-aabs(Y-zb[num][]);
dis[num]+=change;
if(DIS+change<=deep-f-) dfs(f+,DIS+change,X,Y,num);
dis[num]-=change;
mp[x][y]=;
mp[X][Y]=num;
}
}
}
int main()
{
//freopen("fifteen.in","r",stdin);
//freopen("fifteen.out","w",stdout);
for(int i=;i<=;++i)
for(int j=;j<=;++j)
zb[cal(i,j)][]=i,zb[cal(i,j)][]=j;
for(int i=;i<=;++i)
for(int j=;j<=;++j){
scanf("%d",&mp[i][j]);
if(!mp[i][j]) S=i,T=j;
else{
dis[mp[i][j]]=aabs(i-zb[mp[i][j]][])+aabs(j-zb[mp[i][j]][]);
tot+=dis[mp[i][j]];
}
}
start=clock();
deep=tot;
for(int i=;i<=;++i)
for(int j=;j<=;++j)
for(int k=;k<=;++k)
for(int l=;l<=;++l)
if(cal(k,l)<cal(i,j))
if(mp[k][l]>mp[i][j]) sum++;
sum+=-S+-T;
if(!(sum&)){
puts("No");
return ;
}
while(){
if(deep==){
printf("");
return ;
}
dfs(,tot,S,T,);
deep++;
}
return ;
}
——————————并不华丽的分割线————————————
芒果君:这次考试好像没翻车然后刚才CF炸了……所以说自己不努力能怪谁呢……
冲刺Noip2017模拟赛6 解题报告——五十岚芒果酱的更多相关文章
- 冲刺Noip2017模拟赛2 解题报告——五十岚芒果酱
题1 牛跑步(running) [题目描述] 新牛到部队,CG 要求它们每天早上搞晨跑,从 A 农场跑到 B 农场.从 A 农场到 B 农场中有 n- 个路口,分别标上号,A 农场为 号,B 农场为 ...
- 冲刺Noip2017模拟赛1 解题报告——五十岚芒果酱
题1 国际象棋(chess) [问题描述] 有N个人要参加国际象棋比赛,该比赛要进行K场对弈.每个人最多参加2场对弈,最少参加0场对弈.每个人都有一个与其他人都不相同的等级(用一个正整数来表示).在对 ...
- 冲刺Noip2017模拟赛5 解题报告——五十岚芒果酱
1. 公约数(gcd) [问题描述] 给定一个正整数,在[,n]的范围内,求出有多少个无序数对(a,b)满足 gcd(a,b)=a xor b. [输入格式] 输入共一行,一个正整数n. [输出格式] ...
- 冲刺Noip2017模拟赛3 解题报告——五十岚芒果酱
题1 素数 [问题描述] 给定一个正整数N,询问1到N中有多少个素数. [输入格式]primenum.in 一个正整数N. [输出格式]primenum.out 一个数Ans,表示1到N中有多少个素 ...
- 冲刺Noip2017模拟赛8 解题报告——五十岚芒果酱
1.鼎纹 [问题描述] 据说鼎纹的 种制造 式是 铜模印出来的,这是我国古代劳动 智慧 的结晶.铜模印过的地 ,会留下深深的印记,经过时间的炼化,洗 练成历史的遗存. 聪明的古代劳动人民拥有一个 a ...
- 冲刺Noip2017模拟赛7 解题报告——五十岚芒果酱
1.二叉树(binary) .二叉树 (binary.cpp/c/pas) [问题描述] 二叉排序树或者是一棵空树,或者是具有下列性质的二叉树: ()若左子树不空,则左子树上所有结点的值均小于它的根结 ...
- 冲刺Noip2017模拟赛4 解题报告——五十岚芒果酱
题1 韬韬抢苹果(apple) [问题描述] 又到了收获的季节,树上结了许多韬韬,错了,是许多苹果,有很多个小韬韬都来摘苹 果.每个韬韬都想要最大的苹果,所以发生了争执,为了解决他们的矛盾,出题人定了 ...
- 20161005 NOIP 模拟赛 T2 解题报告
beautiful 2.1 题目描述 一个长度为 n 的序列,对于每个位置 i 的数 ai 都有一个优美值,其定义是:找到序列中最 长的一段 [l, r],满足 l ≤ i ≤ r,且 [l, r] ...
- 【HHHOJ】NOIP模拟赛 玖 解题报告
点此进入比赛 得分: \(100+20+100=220\)(还不错) 排名: \(Rank\ 16\) \(Rating\):\(+20\) \(T1\):[HHHOJ263]「NOIP模拟赛 玖」三 ...
随机推荐
- Activation HDU - 4089 (概率DP)
kuangbin的博客 强 #include <bits/stdc++.h> using namespace std; const int MAXN = 2005; const doubl ...
- sql server 存储过程和视图的区别
视图 要把视图看做是一张表,包含了一张表的部分数据或者多个表的综合数据,视图的使用和普通表一样: 视图建立并存储在服务器,有效减少网络数据流量,提高安全性: 视图中不存放数据,数据依然存放在视图引用的 ...
- PHP基础--traits的应用
Traits 在PHP中实现在方法的重复使用:Traits与Class相似,但是它能够在Class中使用自己的方法而不用继承: Traits在Class中优先于原Class中的方法,引用PHP Doc ...
- [ZJOI2009]假期的宿舍 二分图匹配匈牙利
[ZJOI2009]假期的宿舍 二分图匹配匈牙利 一个人对应一张床,每个人对床可能不止一种选择,可以猜出是二分图匹配. 床只能由本校的学生提供,而需要床的有住校并且本校和外校两种人.最后统计二分图匹配 ...
- classpath详解
在dos下编译java程序,就要用到classpath这个概念,尤其是在没有设置环境变量的时候.classpath就是存放.class等编译后文件的路径. javac:如果当前你要编译的java文件中 ...
- javascript中稀疏数组和密集数组
密集数组 数组是一片连续的存储空间,有着固定的长度.加入数组其实位置是address,长度为n,那么占用的存储空间是address[0],address[1],address[2].......add ...
- Linux设备驱动程序 之 kmalloc
原型 kmalloc的原型如下: void *kmalloc(size_t size, gfp_t flags) 第一个参数是要分配的块的大小,第二个参数是分片标志: flags标志 最常用的标志是G ...
- springboot项目报Loading class `com.mysql.jdbc.Driver'. This is deprecated. The new driver class is`...解
参考文章:https://blog.csdn.net/qq_42815754/article/details/83652253 <!-- MySql驱动 --> <dependenc ...
- ArcGIS超级工具SPTOOLS-按属性裁剪,矢量数据批量裁剪,矢量数据批量合库
1.1 按属性裁剪 操作视频: https://weibo.com/tv/v/HwaZRoosq?fid=1034:4376687438183117 按属性裁剪:可以图形表,也可以是非图形表,字段值 ...
- python笔记4 内置函数,匿名函数.递归函数 面向对象(基础, 组合,继承)
内置函数 eval和exec eval :执行字符串中的代码并将结果返回给执行者,有返回值 exec:执行字符串中的代码,往往用于执行流程语句,没有返回值. s1 = '1+2' s2 = 'prin ...