冲刺Noip2017模拟赛6 解题报告——五十岚芒果酱

1.ksum(ksum)

【问题描述】
Peter喜欢玩数组。NOIP这天,他从Jason手里得到了大小为n的一个正整数
数组。
Peter求出了这个数组的所有子段和,并将这n(n+1)/2个数降序排序,他想
知道前k个数是什么。
【输入格式】
输入文件名为 ksum.in。
输入数据的第一行包含两个整数 n 和 k。
接下来一行包含 n 个正整数,代表数组。
【输出格式】
输出文件名为 ksum.out。
输出 k 个数,代表降序之后的前 k 个数,用空格隔开。
【输入输出样例】
ksum.in
3 4
1 3 4

3 3
10 2 7
ksum.out
8 7 4 4

19 12 10
【数据规模与约定】
对于所有数据,满足 ai≤10^9,k≤n(n+1)/2,n≤100000,k≤100000
测试点编号 n≤         k≤
1              100       5000
2              500       100000
3              1000     80000
4              1000     100000
5              10000   50000
6              20000   80000
7              50000   80000
8              100000 80000
9              100000 100000
10            100000 100000

题目

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*1000000;
    if(!Hash.count(num)){
        Hash[num]=1;
        Q.push(x);
    }
}
int main()
{
    //freopen("ksum.in","r",stdin);
    //freopen("ksum.out","w",stdout);
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;++i){
        scanf("%d",&a[i]);
        sum[i]=sum[i-1]+a[i];
    }
    NUM x;
    x.l=1,x.r=n,x.val=sum[n];
    Push(x);
    while(1){
        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+1,x.r=r;
        y.l=l,y.r=r-1;
        x.val=sum[x.r]-sum[x.l-1];
        y.val=sum[y.r]-sum[y.l-1];
        Push(x);
        Push(y);
    }
    for(int i=1;i<k;++i) cout<<ans[i]<<" ";
    cout<<ans[k]<<endl;
    return 0;
}

2.奇袭(raid)

【问题描述】
由于各种原因,桐人现在被困在Under World(以下简称UW)中,而UW马上
要迎来最终的压力测试——魔界入侵。
唯一一个神一般存在的Administrator被消灭了,靠原本的整合骑士的力量
是远远不够的。所以爱丽丝动员了UW全体人民,与整合骑士一起抗击魔族。
在UW的驻地可以隐约看见魔族军队的大本营。整合骑士们打算在魔族入侵
前发动一次奇袭,袭击魔族大本营!
为了降低风险,爱丽丝找到了你,一名优秀斥候,希望你能在奇袭前对魔
族大本营进行侦查,并计算出袭击的难度。
经过侦查,你绘制出了魔族大本营的地图,然后发现,魔族大本营是一个N
×N的网格图,一共有N支军队驻扎在一些网格中(不会有两只军队驻扎在一起)。
在大本营中,每有一个k×k(1≤k≤N)的子网格图包含恰好k支军队,我们
袭击的难度就会增加1点。
现在请你根据绘制出的地图,告诉爱丽丝这次的袭击行动难度有多大。
【输入格式】
第一行,一个正整数N,表示网格图的大小以及军队数量。
接下来N行,每行两个整数,Xi,Yi,表示第i支军队的坐标。
保证每一行和每一列都恰有一只军队,即每一个Xi和每一个Yi都是不一样
的。
【输出格式】
一行,一个整数表示袭击的难度。
【输入输出样例】
raid.in
5
1 1
3 2
2 4
5 5
4 3

raid.out
10
【样例解释】
显然,分别以(2,2)和(4,4)为左上,右下顶点的一个子网格图中有3支军队,
这为我们的难度贡献了1点。
类似的子网格图在原图中能找出10个。
【数据范围】
对于30%的数据,N ≤ 100
对于60%的数据,N ≤ 5000
对于100%的数据,N ≤ 50000

题目

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)。处理了另外两种情况。

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<algorithm>
 4 #include<cmath>
 5 #include<stack>
 6 #define maxn 100010
 7 #define inf 1<<29
 8 #define ll long long
 9 using namespace std;
10 int Lmax[maxn],Lmin[maxn],Rmax[maxn],Rmin[maxn],tong[maxn],a[maxn],n;
11 ll cal(int l,int r,int mid)
12 {
13     ll ret(0);
14     Lmax[mid]=Lmin[mid]=a[mid];
15     Rmax[mid+1]=Rmin[mid+1]=a[mid+1];
16     for(int i=mid-1;i>=l;--i){
17         Lmax[i]=max(Lmax[i+1],a[i]);
18         Lmin[i]=min(Lmin[i+1],a[i]);
19     }
20     for(int i=mid+2;i<=r;++i){
21         Rmax[i]=max(Rmax[i-1],a[i]);
22         Rmin[i]=min(Rmin[i-1],a[i]);
23     }
24     for(int i=l;i<=mid;++i){
25         int j=i+Lmax[i]-Lmin[i];
26         if(j>mid&&Rmax[j]<Lmax[i]&&Rmin[j]>Lmin[i]) ret++;//最大值和最小值都在左边  由于j可能比n还大就导致数组需要开2倍
27     }
28     int p1=mid+1,p2=mid;//p1 max指针 p2 min指针
29     while(p1<=r&&Rmax[p1]<Lmax[l]) tong[Rmax[p1]-p1]--,p1++;//左不符合 右符合 最后指到第一个符合的
30     while(p2<r&&Rmin[p2+1]>Lmin[l]) p2++,tong[Rmax[p2]-p2]++;//左符合 右不符合 最后指到最后一个不符合的
31     for(int i=l;i<=mid;++i){
32         while(p1>mid+1&&Rmax[p1-1]>Lmax[i]) p1--,tong[Rmax[p1]-p1]++;//将原来不符合变为符合
33         while(p2>mid&&Rmin[p2]<Lmin[i]) tong[Rmax[p2]-p2]--,p2--;//将符合变为不符合
34         ret+=max(tong[Lmin[i]-i],0);//万一是负的
35     }
36     for(int i=mid+1;i<=r;++i) tong[Rmax[i]-i]=0;//区间清零 不要脑子一热memset了
37     return ret;
38 }
39 ll solve(int l,int r)
40 {
41     ll ret(0);
42     if(l==r) return 1ll;
43     int mid=(l+r)>>1;
44     ret=solve(l,mid)+solve(mid+1,r);
45     ret+=cal(l,r,mid);
46     reverse(a+l,a+r+1);//区间反转
47     if((r-l+1)&1) mid--;//mid位置修正
48     ret+=cal(l,r,mid);
49     reverse(a+l,a+r+1);
50     return ret;
51 }
52 int main()
53 {
54     //freopen("raid.in","r",stdin);
55     //freopen("raid.out","w",stdout);
56     int x,y;
57     scanf("%d",&n);
58     for(int i=1;i<=n;++i){
59         scanf("%d%d",&x,&y);
60         a[x]=y;//转一维
61     }
62     printf("%I64d\n",solve(1,n));
63     return 0;
64 }

3.十五数码(fifteen)

【题目描述】
给出起始顺序,要求通过 0 的移动(与上下左右交换),排成以下顺序:
1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 0
【输入格式】
从文件 fiften.in 中读入数据,四个数一行,共四行。
【输出格式】
输出到文件 fifteen.out 中。
输出最少移动次数。如果无解输出 No。
【样例 1 输入】
1 2 3 4
5 6 7 8
9 10 11 12
13 14 0 15
【样例 1 输出】
1
【样例 2 输入】
1 11 3 8
5 7 0 2
9 13 4 12
6 10 14 15
【样例 2 输出】
33
【数据范围】
对于 20%的数据,保证有解并且 Ans <= 12;
对于 50%的数据,保证有解并且 Ans <= 28;
存在 10%的数据无解。
对于 100%的数据,如果有解,Ans <= 50;

题目

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,一个奇数,而每次移动无论横纵的改变量都是偶数,奇+-偶=奇,初状态也必须是奇数才行。

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<iostream>
 4 #include<algorithm>
 5 #include<cmath>
 6 #include<ctime>
 7 #define aabs(x) (x>=0?x:-(x))
 8 #define ok(x,y) x>0&&x<5&&y>0&&y<5
 9 #define ll long long
10 using namespace std;
11 int dx[]={0,1,0,-1},dy[]={1,0,-1,0},mp[5][5],zb[20][2],dis[20],deep,S,T,tot,start,sum;
12 int cal(register int x,register int y){return (x-1)*4+y;}
13 void dfs(register int f,register int DIS,register int x,register int y,register int sdbh)
14 {
15     if(clock()-start>=1980){
16         if(deep<48) printf("48");
17         else printf("50");
18         exit(0);
19     }
20     if(f==deep){
21         if(!DIS){
22             printf("%d\n",deep);
23             exit(0);
24         }
25         return;
26     }
27     for(register int i=0;i<4;++i){
28         register int X=dx[i]+x,Y=dy[i]+y,num=mp[X][Y];
29         if(num!=sdbh&&ok(X,Y)&&deep-f>=dis[num]){
30             mp[x][y]=num;
31             mp[X][Y]=0;
32             register int change=aabs(x-zb[num][0])+aabs(y-zb[num][1])-aabs(X-zb[num][0])-aabs(Y-zb[num][1]);
33             dis[num]+=change;
34             if(DIS+change<=deep-f-1) dfs(f+1,DIS+change,X,Y,num);
35             dis[num]-=change;
36             mp[x][y]=0;
37             mp[X][Y]=num;
38         }
39     }
40 }
41 int main()
42 {
43     //freopen("fifteen.in","r",stdin);
44     //freopen("fifteen.out","w",stdout);
45     for(int i=1;i<=4;++i)
46         for(int j=1;j<=4;++j)
47             zb[cal(i,j)][0]=i,zb[cal(i,j)][1]=j;
48     for(int i=1;i<=4;++i)
49         for(int j=1;j<=4;++j){
50             scanf("%d",&mp[i][j]);
51             if(!mp[i][j]) S=i,T=j;
52             else{
53                 dis[mp[i][j]]=aabs(i-zb[mp[i][j]][0])+aabs(j-zb[mp[i][j]][1]);
54                 tot+=dis[mp[i][j]];
55             }
56         }
57     start=clock();
58     deep=tot;
59     for(int i=1;i<=4;++i)
60         for(int j=1;j<=4;++j)
61             for(int k=1;k<=4;++k)
62                 for(int l=1;l<=4;++l)
63                     if(cal(k,l)<cal(i,j))
64                         if(mp[k][l]>mp[i][j]) sum++;
65     sum+=4-S+4-T;
66     if(!(sum&1)){
67         puts("No");
68         return 0;
69     }
70     while(1){
71         if(deep==50){
72             printf("50");
73             return 0;
74         }
75         dfs(0,tot,S,T,0);
76         deep++;
77     }
78     return 0;
79 }

——————————并不华丽的分割线————————————

芒果君:这次考试好像没翻车然后刚才CF炸了……所以说自己不努力能怪谁呢……

时间: 2024-10-01 14:13:05

冲刺Noip2017模拟赛6 解题报告——五十岚芒果酱的相关文章

暑假第二次考试 冲刺Noip2017模拟赛2 解题报告——五十岚芒果酱

题1 牛跑步(running) [题目描述] 新牛到部队,CG 要求它们每天早上搞晨跑,从 A 农场跑到 B 农场.从 A 农场到 B 农场中有 n-2 个路口,分别标上号,A 农场为 1 号,B 农场为 n 号,路口分别为 2...n-1 号,从 A 农场到 B 农场有很多条路径可以到达,而 CG 发现有的路口是必须经过的,即每条路径都经过的路口,CG 要把它们记录下来,这样 CG 就可以先到那个路口,观察新牛们有没有偷懒,而你的任务就是找出所有必经路口. [输入格式] 第一行两个用空格隔开的

冲刺Noip2017模拟赛8 解题报告——五十岚芒果酱

1.鼎纹 [问题描述] 据说鼎纹的 种制造 式是 铜模印出来的,这是我国古代劳动 智慧 的结晶.铜模印过的地 ,会留下深深的印记,经过时间的炼化,洗 练成历史的遗存. 聪明的古代劳动人民拥有一个 a 行 b 列的铜模,每个位置要么是 0(代表 这个点是平的),要么是 1(代表这个点是凸起的).他们想造 个 n 行 m 列 的鼎 ,其中每个位置也都是 0 或 1,表示经过若干 次印后,每个位置的结果. 有一些要求.铜模是不能旋转和翻转的:在印的过程当中,铜模的凸起不 能出现在鼎面的外面(平的部分是

冲刺Noip2017模拟赛7 解题报告——五十岚芒果酱

1.二叉树(binary) 1.二叉树 (binary.cpp/c/pas) [问题描述] 二叉排序树或者是一棵空树,或者是具有下列性质的二叉树: (1)若左子树不空,则左子树上所有结点的值均小于它的根结点的值: (2)若右子树不空,则右子树上所有结点的值均大于它的根结点的值: (3)左.右子树也分别为二叉排序树: (4)没有键值相等的结点. 完全二叉树:只有最下面的两层结点度能够小于2,并且最下面一层的结点 都集中在该层最左边的若干位置的二叉树. 图1中,(a)和(b)是完全二叉树,(c)和(

暑假第四次考试 冲刺Noip模拟赛4 解题报告——五十岚芒果酱

题1 韬韬抢苹果(apple) [问题描述] 又到了收获的季节,树上结了许多韬韬,错了,是许多苹果,有很多个小韬韬都来摘苹 果.每个韬韬都想要最大的苹果,所以发生了争执,为了解决他们的矛盾,出题人定了一项 特殊的规则,按体重的大小来定顺序,每一轮都是先由胖的先摘(照顾胖子),每个韬韬都 是很聪明的,不会错过眼前最大的苹果.现在问题来了,一共有 n 个苹果,m 个韬韬,要你 按原顺序输出每个韬韬可以抢到的苹果的总大小. [输入格式]apple.in 第一行两个数 n,m. 接下来一行 n 个数,分

CH Round #55 - Streaming #6 (NOIP模拟赛day2)解题报告

T1九九归一 描述 萌蛋在练习模n意义下的乘法时发现,总有一些数,在自乘若干次以后,会变成1.例如n=7,那么5×5 mod 7=4,4×5 mod 7=6,6×5 mod 7=2,2×5 mod 7=3,3×5 mod 7=1.如果继续乘下去,就会陷入循环当中.萌蛋还发现,这个循环的长度经常会是φ(n),即小于n且与n互质的正整数的个数.例如,φ(7)=6,而上述循环的长度也是6,因为5,4,6,2,3,1共有6个数.再如n=6,那么5×5 mod 6=1.这个循环的长度很短,只有2,而恰好φ

【一个蒟蒻的挣扎】模拟赛2解题报告

---恢复内容开始--- 今天50分,我自闭 眼瞎看错了一道题,然后两道题的代码都出了点小毛病,自闭 真的竞赛我要这样可以收拾收拾退役了真的,眼睛不能这么瞎了考试莫得人帮忙改了 如果一切都没问题今天应该有130,啊!!!!!!!!!!!!!!!!! 今天两题可以用堆,一题模拟随便写写就过了,(所以我为什么考这么差) 进入正题 题目一览(其实就3道题)(CZR到底是谁这么爱数学和折磨我们) 24点 小游戏 中位数 题1. 24点 题目描述 1.1 Background CZR很喜欢学数学,但是他数

20161022 NOIP模拟赛 T2 解题报告

旅行者问题 [问题描述] lahub是一个旅行者的粉丝,他想成为一个真正的旅行者,所以他计划开始一段旅行.lahub想去参观n个目的地(都在一条直道上).lahub在起点开始他的旅行.第i个目的地和起点的距离为ai千米(ai为非负整数).不存在两个目的地和起点的距离相同. 从第i个目的地走到第j个目的地所走的路程为 |ai-aj|千米.我们把参观n个目的地的顺序称作一次“旅行”.lahub可以参观他想要参观的任意顺序,但是每个目的地有且只能被参观一次(参观顺序为n的排列). lahub把所有可能

CH Round #54 - Streaming #5 (NOIP模拟赛Day1)解题报告

最近参加了很多CH上的比赛呢~Rating--了..题目各种跪烂.各种膜拜大神OTZZZ T1珠 描述 萌蛋有n颗珠子,每一颗珠子都写有一个数字.萌蛋把它们用线串成了环.我们称一个数字串是有趣的,当且仅当它的第1位是2,且除了第1位以外的每一位都是3.例如,2,233,2333333都是有趣的数字串.现在,你可以从这串珠子的任意一颗开始读,沿着顺时针或逆时针方向,到任意一颗珠子停止.这样,你就可以读出一个数字串来.萌蛋想知道,所有能读出的有趣的数字串当中,最长的是哪一个数字串.当然,你也可能读不

2014 UESTC 暑前集训队内赛(1) 解题报告

A.Planting Trees 排序+模拟 常识问题,将耗时排一个序,时间长的先种,每次判断更新最后一天的时间. 代码: #include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #define Mod 1000000007 #define INT 2147483647 #define pi acos(-1.0)