NOIP复习模拟赛day1
首先先bb一下day1的题目名字怎么都这么鬼才......(连起来叫两情若是长久时)
1.两情
(sweethearts.pas/c/cpp)
【问题描述】小 W 将要去和小 K 约会啦!但聪(ao)明(jiao)的小 K 并不想让小 W 那么容易知道他们的约会地点。于是小 W 收到了一条信息:“给定两个数的和 n,请你求出这两个数的最小公倍数的可能值的最大值,作为交换,如果你给出了正确答案,我将会把你和小 K的约会地点告诉你。”众所周知,小 W 是个数学弱渣,他只好求助数学巨佬小 H,但小 H 并不屑于做这种简单题,于是帮助小 W 的任务就交给你啦!
【输入】输入文件的第一行一个整数 T 表示数据组数。接下来 T 行每行一个整数 n ,表示给定的两个数的和 n。
【输出】共 T 行,每行一个整数表示和为 n 的两个数的最小公倍数的可能值的最大值。
【输入样例】3
2
3
4
【输出样例】1
2
3
【数据范围】30%的数据满足 T<=10,n<=1000
100% 的数据满足 T<=10000 ,n<=109
官方题解:
算法描述
100%
若n为奇数,则a和b相差 1 最优
若n为偶数,令m = n/2,若m - 1和m + 1均为奇数,则a = m - 1, b = m + 1
否则a = m - 2, b = m + 2
时间复杂度O(1)
考察内容 特判,分类讨论
难度:简单
(是不是感觉题解没什么用,鬼才能想出来)
自己bb的题解:
其实,按照题目说的,要求max{lcm(x,y)}并且x+y=n。
那么可以有: y=n-x;
并且lcm(x,y)=lcm(x,n-x);
根据一个玄学的定理:lcm(x,y)=x*y/gcd(x,y)=x*(n-x)/gcd(x,n-x);
也就是说当x*(n-x)越大,gcd(x,n-x)越小时,lcm(x,y)越大;
还有一个初中的定理:那就是当周长相同时,越接近正方形的面积越大;
那么根据这个定理,我们就可以知道,当x跟y相差的值越小x*y越大;
另外一个,当x,y互质的时候gcd(x,y)=1最小;(互质是啥意思呢???就是x,y的公约数只有一个1。是不是很luozhi)
OK,那么我们刚开始可以令x=y=n/2;
然后x不断的减1,y不断的加1,在这个过程中我们要不断的求gcd(x,y),看它是否为1,如果它为1,我们就退出循环,最后max{lcm(x,y)}=x*y;
最后一定要记得开long long
一定要记得开long long
一定要记得开long long
好了,上代码......
1 //NOIPRP++ 2 #include<bits/stdc++.h> 3 #define Re register int 4 #define LL long long 5 using namespace std; 6 LL T,x,l,r,Mid; 7 inline void read(LL &x){ 8 x=0; char c=getchar(); bool p=1; 9 for (;'0'>c||c>'9';c=getchar()) if (c=='-') p=0; 10 for (;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48); 11 p?:x=-x; 12 } 13 int main(){ 14 Re i,j; 15 read(T); 16 while (T--){ 17 read(x); 18 l=x>>1;r=x-l; 19 while (l<=r){ 20 if (__gcd(l,r)==1){//C++自带函数gcd...... 21 printf("%lld\n",l*r); 22 break; 23 } 24 l--;r++; 25 } 26 } 27 return 0; 28 } 29 //NOIPRP++
2.若是
(supposing.pas/c/cpp)
【问题描述】
“一生至少该有一次,为了某个人而忘了自己, 不求有结果 ,不求同行,不求曾经拥有,甚至不求你爱我, 只求在我最美的年华里,遇到你。” ——徐志摩
原来小 W 和小 K 要到到 Q 镇去游玩。Q 镇是一个非常浪漫的约会圣地,同时它也是一个很特别的城镇。小镇中有很多道路,四通八达。它有 n+1 条的小路为南北方向,有 m+1 条的小路为东西方向,这些道路将 Q 镇划分成了 m×n 个区域,而这些区域,从北南、从西到东的坐标标识为从坐标 (1,1) 到坐标 (m,n) 。小 W 和小 K 在网上找到了情侣们对这 m×n 个区域的打分 V(i,j)(分数可正可负)。分数越高表示那个区域越适合情侣们出没,越低表示不适合情侣游玩。为了方便游玩,小 W 和小 K 决定选定一个连续的区域集合作为他们的游玩范围。例如,如果他们选择了最西北的区域(m1,n1)和最东南(m2,n2)区域 (m1≤m2,n1≤n2) ,那么他们的活动范围是 {D(i,j) (m1≤i≤m2,n1≤j≤n2)} ,他们游玩的欢乐值则为这些活动范围的区域评分总和。小 W 和小 K 希望他们游玩范围内的区域的欢乐值最大。而身为单身狗的你的任务是编写一个程序,求出他们的活动范围(m1,n1),(m2,n2)的欢乐值的最大值。
【输入】输入第一行为整数 m,n,用空格隔开接下来有 m 行,每行有 n 列整数,其中第 i 行第 j 列的整数,代表 V(i,j),一个整数之间用空格隔开。输入数据保证这些整数中,至少存在一个正整数。
【输出】输出只有一行,为最高的欢乐值。
【输入样例】4 5
1 -2 3 -4 5
6 7 8 9 10
-11 12 13 14 -15
16 17 18 19 20
【输出样例】146
【数据范围】对于 100%的数据,1 ≤ N, M ≤ 200 ,且 V(i,j)∈[-200000,200000]。
官方题解:
观察题目,也许有人会毫不犹豫的选择枚举,将所有的 m1,n1,m2,n2 的情况全部求出来,但哪怕将查询区间 {D(i,j) (m1≤i≤m2,n1≤j≤n2)} 的分值的速度优化到 O(1),这都是过不了的。因此,我们需要寻找一种更优秀的暴力方法。
首先,我们可以很容易知道,对于一个正确的 m1,n1,m2,n2 满足条件,该区域每个边上的所有分值和必然大于0.因为如果其某边的分值和小于0,则可以通过删除该边以得到更优的解。扩展一下,该区域必然可以分成两个矩形,并且两个矩形的分值都会大于0,否则我们将删除小于0的分值的矩形以得到更高的分数。那么,我们就可以得到一个优秀的条件,就是我们在搜索的过程中,得到的任意一个矩形的分值都大于0,否则我们就抛弃它。
因此,我们可以通过这个特殊的条件,来减少复杂度。
我们可以枚举它的最左边的位置和最右边的位置,也就是n1,n2。
因为,假设存在这样一个m1,n1,m2,n2,我们枚举的n1,n2必然有一种情况会与最优解相同。
那么,由于我们再枚举m1,m2必然会导致时间复杂度过高,那么,我们就可以选择只枚举一遍 m’,在枚举的过程中进行计算。我们使得枚举过程中,maxn记录下存在枚举的m’行所形成的最大值,若是maxn大于0,我们就继续枚举,并且考虑它是否是最优解,也就是将maxn与答案ans进行对比。
如果maxn小于0,根据我们刚才上述的条件,上面枚举的矩形都将直接抛弃,maxn重新设为0,继续寻找合适的矩形。
通过这种方法,复杂度会变成 O(n^3),就可以解决本题了。
(这个题解太完美了,我感觉没什么好bb的......)
自己bb的题解:
题解直接上二维的最大子矩阵的和是不是有点♂(大佬可以跳过)我们来学习一下一维的吧
先来个链接(2014年初赛完善程序第二题原题):https://wenku.baidu.com/view/f4bd35aa83c4bb4cf7ecd1a8.html
先举个例子
(有点丑)
那我们怎么求这个序列的最大子段和呢???
首先我们要求出这个序列的前缀和;
我们可以先有两个循环,来枚举当前子段的起点终点,设一个Max来记录当前子段的值,然后用Ans来记录当前最大子段和;
然后...就一直枚举...
until Max<0 then Max=0; (中文式英语)
为什么呢???因为一旦Max<0那么前面的子段的值就可以不用取,因为它是来帮倒忙的......
最终的答案就是15;
上本题代码......
1 //NOIPRP++ 2 #include<bits/stdc++.h> 3 #define Re register int 4 using namespace std; 5 int a[205][205],N,M,f[205][205],ans,Max; 6 inline void read(int &x){ 7 x=0; char c=getchar(); bool p=1; 8 for (;'0'>c||c>'9';c=getchar()) if (c=='-') p=0; 9 for (;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48); 10 p?:x=-x; 11 } 12 int main(){ 13 Re i,j,k; 14 read(N);read(M); 15 for (i=1;i<=N;i++) 16 for (j=1;j<=M;j++) read(a[i][j]),f[i][j]+=f[i][j-1]+a[i][j]; 17 for (i=1;i<=M;i++) 18 for (j=i;j<=M;j++){ 19 Max=0; 20 for (k=1;k<=N;k++){ 21 Max+=f[k][j]-f[k][i-1]; 22 if (Max<0) Max=0; 23 if (ans<Max) ans=Max; 24 } 25 } 26 printf("%d",ans); 27 return 0; 28 } 29 //NOIPRP++
3.长久时
(lifelong.pas/c/cpp)
【问题描述】
“往事无可回首,余生请多指教。今世愿无背离,来世仍多包涵。” ——黄伟文
小 W 和小 K 躺在小山丘的草地上看星空,广阔无垠的星空上,不时会出现一些星星,在天穹明亮地闪烁。小 K 突发奇想,如果把星空比作一个无限大的坐标平面,对于每个时刻,都有可能有一颗星星出现在(x,y)这个点上。众所周知,小 K 十分喜欢***(tiao)难(xi)小 W,所以在某个时刻,小 K 会问小 W 在一个由(x,y) ,(x+d,y) ,(x,y+d) 三点围成的等腰直角三角形中,一共有多少个星星。数数对于小 W 完全是噩梦,所以他来请教你,聪明的你快来帮他解决这个难题吧!
【输入】第一行一个整数 n,表示时刻数。接下来 n 行,每行 3 个非负整数 x , y , d。若 d=0 表示该时刻有一颗星星出现在(x,y)这个点。否则表示小 K 的询问
【输出】对每个询问输出一个整数,表示当时询问的三角形内的星星的个数。
【输入输出样例】8
1 3 0
1 5 0
3 6 0
4 4 0
2 6 0
1 5 3
1 5 4
1 1 1
3
3
0
4
1 5 0
3 7 0
2 5 6
2 3 4
1
0
【数据范围】对于 30%的数据 n<=3333 。另 30% 的数据 小 K 只会在所有的星星出现后才开始询问,且 xi,yi<=333333 。对于 100%的数据 1<=n<=88888,xi,yi<=3333333
官方题解:
lifelong
算法描述
30%
直接暴力枚举已经添加的点,判断是否在等腰直角三角形内。时间复杂度 O(m2)
另 30%
由于询问前已经添加完了,不存在先后顺序的问题,可直接按(x + y)排序后扫描线扫过去。对于三角形内的点数,可以发现三角形可用 夹在两条扫描线内的所有点 减去两侧直角边与扫描线构成的平行四边形区域。
如图,两条扫描线中间夹着的部分减去绿色部分后就能得到蓝色部分内的点的数量了。而这可以用前缀和相减的方法轻松地用树状数组统计。
时间复杂度 O(nlogn) (特殊数据)
100%
从另 30%的方法中我们发现,如果可以去掉添加和询问的先后关系,我们可以用nlogn 的时间复杂度完成统计。而冬令营上 gyz 讲的分治则为我们提供了去掉先后关系的工具。
递归左边,递归右边,统计左边对右边的贡献,而这一步统计就是前面的情况!
时间复杂度 O(nlog2n)
考察内容
分治,树状数组,简单容斥。(树套树)
难度:中等
题目来源
30%的情况 http://www.codechef.com/FEB13/problems/TRIQUER
100%的由原题加强而来
自己bb的题解:
太难了不会,听说是要CDQ,树套树......
先贴上大佬的代码......(怎么有点水)
1 #include <cstdio> 2 #include <algorithm> 3 using namespace std; 4 int read() 5 { 6 int x=0,f=1; 7 char c=getchar(); 8 while (c<'0' || c>'9') 9 { 10 if (c=='-') f=-1; 11 c=getchar(); 12 } 13 while (c>='0' && c<='9') 14 x=x*10+c-'0',c=getchar(); 15 return x*f; 16 } 17 const int N=2e5,M=4e6; 18 struct _ 19 { 20 int x,y,v,id; 21 }a[N],q[N],p[N]; 22 int n,t=0,c[N],b[M],cnt,Q,ans[N],ma; 23 int lowbit(int x) 24 { 25 return x&(-x); 26 } 27 void add(int x,int v) 28 { 29 while (x) 30 b[x]+=v,x-=lowbit(x); 31 } 32 int find(int x) 33 { 34 int ans=0; 35 while (x<=ma) 36 ans+=b[x],x+=lowbit(x); 37 return ans; 38 } 39 void CDQ(int l,int r) 40 { 41 if (l==r) return; 42 int mid=(l+r)>>1; 43 CDQ(l,mid),CDQ(mid+1,r); 44 int i=l,j=mid+1; 45 t=0; 46 while (i<=mid && j<=r) 47 { 48 if (p[i].x>=p[j].x) 49 { 50 q[++t]=p[i]; 51 if (p[i].v==0) 52 add(p[i].y,1); 53 i++; 54 } 55 else 56 { 57 q[++t]=p[j]; 58 if (p[j].v) 59 ans[p[j].id]+=find(p[j].y); 60 j++; 61 } 62 } 63 while (j<=r) 64 q[++t]=p[j],ans[p[j].id]+=find(p[j].y),j++; 65 for (int d=l;d<i;++d) 66 if (p[d].v==0) 67 add(p[d].y,-1); 68 while (i<=mid) 69 q[++t]=p[i],i++; 70 for (int i=1;i<=t;++i) 71 p[l+i-1]=q[i]; 72 } 73 void cdq(int l,int r) 74 { 75 if (l==r) return; 76 int mid=(l+r)>>1; 77 cdq(l,mid),cdq(mid+1,r); 78 int i=l,j=mid+1; 79 t=0; 80 int k=0; 81 while (i<=mid && j<=r) 82 { 83 if (a[i].x+a[i].y+a[i].v<=a[j].x+a[j].y+a[j].v) 84 { 85 q[++t]=a[i]; 86 if (a[i].v==0) 87 p[++k]=a[i]; 88 i++; 89 } 90 else 91 { 92 q[++t]=a[j]; 93 if (a[j].v) 94 p[++k]=a[j]; 95 j++; 96 } 97 } 98 while (i<=mid) 99 q[++t]=a[i],i++; 100 while (j<=r) 101 { 102 q[++t]=a[j]; 103 if (a[j].v) 104 p[++k]=a[j]; 105 j++; 106 } 107 for (int i=1;i<=t;++i) 108 a[l+i-1]=q[i]; 109 if (k) 110 CDQ(1,k); 111 } 112 int main() 113 { 114 n=read(); 115 for (int i=1;i<=n;++i) 116 { 117 a[i].x=read(),a[i].y=read(),a[i].v=read(); 118 ma=max(ma,a[i].y); 119 if (a[i].v) 120 a[i].id=++Q; 121 } 122 cdq(1,n); 123 for (int i=1;i<=Q;++i) 124 printf("%d\n",ans[i]); 125 return 0; 126 }