【纪中集训】2019.08.02【NOIP提高组】模拟 A 组TJ
\(\newcommand{\RNum}[1]{\uppercase\expandafter{\romannumeral #1\relax}}\)
T1
- 一道可以暴力撵标算的题……
Description
- 给定二维平面上\(N(≤60000)\)个有权值的点\((X_i,Y_i)(\in[0,10^9])\),点权\(Z_i\in[0,10^9]\)。
- 有\(M(≤10000)\)次操作,操作\(Ⅰ\)是询问一个边平行于坐标轴的矩形中权值第\(K\)小的点,操作\(Ⅱ\)是交换两个点的权值。
- 时限10s。
SolutionⅠ
- 既然时限10s,那肯定要想到暴力。
- 最朴素暴力应该是找出所有在矩形中的点,快排,取第\(K\)小。这样是\(O(NM\log_2N)\)的,实测会\(TLE\),只有52.4pts。
- 考虑\(O(N)\)找第\(K\)小。可以先将权值离散化,然后把快排改为桶牌,这样就变成\(O(N\log_2N+NM)\)了。直接交似乎是3~4s;吸臭氧后可以达到1s+。
Code
#include <cstdio>
#include <algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define O3 __attribute((optimize("-O3")))
#define NO {puts("It doesn't exist."); continue;}
using namespace std;
const int N=81e3;
int i,j,n,m,n1,Z[N],pos[N],x,y,x1,y1,k,l,r,mid,cnt[N],ss;
struct city{int x,y,z,i;}a[N];
O3 void read(int&x)
{
char c=getchar(); x=0;
for(;c<48|c>57;c=getchar());
for(;c>47&c<58;c=getchar()) x=x*10+(c^48);
}
O3 bool cmpx(city a,city b) {return a.x<b.x;}
O3 bool cmpz(int x,int y) {return a[x].z<a[y].z;}
O3 int main()
{
read(n),read(m);
fo(i,1,n) read(a[a[i].i=i].x),read(a[i].y),read(a[i].z),Z[i]=a[i].z;
sort(Z+1,Z+n+1), n1=unique(Z+1,Z+n+1)-Z-1;
sort(a+1,a+n+1,cmpx);
fo(i,1,n) pos[a[i].i]=i, a[i].z=lower_bound(Z+1,Z+n1+1,a[i].z)-Z;
while(m--)
{
char c=getchar();
while(c!='Q'&c!='S') c=getchar();
read(x),read(y);
if(c=='Q')
{
read(x1),read(y1),read(k);
if(x>x1) swap(x,x1);
if(y>y1) swap(y,y1);
l=1,r=n;
while(l<r)
{
mid=l+r>>1;
a[mid].x>=x?r=mid:l=mid+1;
}
if(n-l+1<k) NO
fo(i,0,n1) cnt[i]=0;
fo(i,l,n)
{
if(a[i].x>x1) break;
if(y<=a[i].y&a[i].y<=y1) cnt[0]++,cnt[a[i].z]++;
}
if(cnt[0]<k) NO
fo(i,1,n1)
{
if(cnt[i]>=k) break;
k-=cnt[i];
}
printf("%d\n",Z[i]);
}
else swap(a[pos[x+1]].z,a[pos[y+1]].z);
}
}
SolutionⅡ
- 这题实际上是李超12年出的,他当时的做法是分块+远古数据结构划分树。
- 我们可以将点按\(x\)坐标分\(\sqrt N\)块,同一块内按\(y\)坐标排序并构建划分树;这样一来,对于一个询问,它至多覆盖了\(\sqrt N\)个整块和2个散块。我们可以同时处理所有整块,就在划分树上跑;散块则暴力处理。
- 这样一来,我们就可以达到\(O(N^{1.5}+MN^{0.5}\log_2N^{0.5})\)的高效率。
- 代码复杂度翻若干倍。
SolutionⅢ
- 既然是求第\(K\)小,自然可以想到整体二分……
- 类比一下求区间第\(K\)小的做法:我们先离线,把所有操作放到一起进行整体二分答案,这就变成了一个一维偏序问题;那这题也可以类似地转化为整体二分+二维偏序。再打一个带修主席树!
- 时间复杂度达到了惊人的\(O((N+M)\log_2^3 N)\)!
- 代码复杂度翻若干倍。
T2
Description
- 有个好(song)玩(ming)的游戏,有\(N(≤10^8)\)个关卡,初始有\(Q(≤5)\)条命。
- 每通过一个关卡,会得到\(u\)分和1条命,生命上限为\(Q\)。其中\(u=min(\)最近一次连续通过的关数\(,R)\)。
- 设通过一关的概率为\(p(\in[0,1])\),求最小的\(p\)使得期望得分超过\(S\)。
Solution
- 我原本以为如果\(p\)很小,算到后面会爆精度,需要什么奇技淫巧……
- 结果不需要考虑这些问题。
- 首先,我们可以预处理出所有可能的状态(当前的\(HP\)与当前的\(u\))。极限数据时也就36种。
- 可以想到二分答案+\(DP\)判可行:设\(f[i][s]\)表示当前过了第\(i\)关,状态为\(s\)的概率。这样的话,我们要一边转移一边更新答案,所以可以把答案设为状态0加入转移。
- 但是\(N\)太大,所以矩乘优化。
- 设二分次数为\(L\),状态数为\(S\),则时间复杂度:\(O(LS^3\log_2N)\)。
Code
#include <cstdio>
#include <cstring>
#define min(x,y) (x<y?x:y)
#define S(a) memset(a,0,sizeof a);
#define C(a,b) memcpy(a,b,sizeof a);
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef double db;
const int S=37;
int i,j,k,x,y,n,R,Q,s,rain,g[S][S],l,r,mid,tmp;
db p,t[S][S],t1[S][S],a[S],a1[S];
void work()
{
S(t) S(a)
t[0][0]=a[g[Q][0]]=1;
fo(i,1,Q)
fo(j,0,R)
if(x=g[i][j])
{
t[0][x]=p*min(j+1,R);
if(y=g[min(i+1,Q)][min(j+1,R)]) t[y][x]=p;
t[g[i-1][0]][x]=1-p;
}
for(tmp=n; tmp; tmp>>=1)
{
if(tmp&1)
{
C(a1,a)
fo(i,0,rain)
{
a[i]=0;
fo(j,0,rain) a[i]+=t[i][j]*a1[j];
}
}
C(t1,t)
fo(i,0,rain) fo(j,0,rain)
{
t[i][j]=0;
fo(k,0,rain) t[i][j]+=t1[i][k]*t1[k][j];
}
}
}
int main()
{
scanf("%d%d%d%d",&n,&R,&Q,&s);
fo(i,0,Q)
fo(j,0,R)
if(i>=j||i==Q) g[i][j]=++rain;
l=0, r=1e7;
while(l<r)
{
mid=l+r>>1;
p=mid/1e7;
work();
a[0]>s ? r=mid : l=mid+1;
}
p=l/1e7;
work();
a[0]>s ? printf("%.6lf",p) : puts("Impossible.");
}
T3
Description
- 给定二维平面上\(N(≤10^5)\)个点\((X_i,Y_i)(\in[0,10^8])\),选3个点,求它们两两之间的曼哈顿距离和的最大值和最小值。
Solution
- 3个点的曼哈顿距离和实际上是\(2*(X_{max}-X_{min}+Y_{max}-Y_{min})\),即3点的外接矩形的边长。那么1个点可以确定其中1~2项。
- 最大值其实很好求。因为对于一个点而言,它可以确定的总共有8种。我们求出这8种的最大值,配对一下即可。而由于是找最大值,所以在\(N>3\)的时候,一定不会算重复点。
- 难点就在求最小值了。
- 我们可以将其划分为两种情况:情况Ⅰ是1个点确定了2项,剩下2个点各确定1项;情况Ⅱ是2个点确定2项,剩下1个点什么也没确定。
- 情况Ⅰ又可细分为4种小情况,这里只考虑1个点确定右上角(即该点为\((X_{max},Y_{max})\))的情况(剩下的可以通过旋转转化而来)。那么还有剩下2个点分别是\((X_{min},Y)\)、\((Y,X_{min})\)。
- 我们按\(x\)坐标对点排序,并以\(x\)轴建立线段树,维护三个值:\(X_{min},Y_{min},min(X_{min}+Y_{min})\)。扫描线,当扫到一个点\((X_i,Y_i)\)时,先查询它的答案,再用\(Y_i\)区间修改,最后用\(X_i\)单点修改。注意区间修改时,如果某区间没有点则不修改;并且只有区间修改时才更新答案。这样就不会自己影响到自己。
- 情况Ⅱ也可细分为2种小情况,也是可以旋转转化。这个比较简单,在此不讲了。
- 总时间复杂度:\(O(N\log_2N)\)。
Code
#include <cstdio>
#include <cstring>
#include <algorithm>
#define A v*2
#define B A+1
#define L A,l,m
#define R B,m+1,r
#define MAX(x,y) if(x<y)x=y
#define fo(i,a,b) for(i=a;i<=b;i++)
#define MS(a,x) memset(a,x,sizeof a);
using namespace std;
const int N=11e4,S=N<<3,inf=0x33333333;
int i,n,x,y,nX,X[N],nY,Y[N],a1,a2,a3,a4,a5,a6,a7,a8,tx[S],ty[S],tag[S],t[S],mt[S];
bool tv[S];
struct rain{int x,y;}a[N];
inline int min(const int&x,const int&y) {return x<y?x:y;}
inline int max(const int&x,const int&y) {return x>y?x:y;}
inline bool cmp(const rain&a,const rain&b) {return a.x<b.x|a.x==b.x&a.y<b.y;}
inline void MIN(int&a,const int&b) {if(a>b)a=b;}
inline void New(const int&v) {if(!tv[v]) tv[v]=1;}
inline void update(const int&v)
{
if( tv[A]&!tv[B]) tx[v]=tx[A], ty[v]=ty[A], t[v]=t[A], mt[v]=mt[A];
else
if(!tv[A]& tv[B]) tx[v]=tx[B], ty[v]=ty[B], t[v]=t[B], mt[v]=mt[B];
else
tx[v]=min(tx[A],tx[B]), ty[v]=min(ty[A],ty[B]), t[v]=min(t[A],t[B]), mt[v]=max(mt[A],mt[B]);
}
inline void mask(const int&v,const int&y)
{
if(mt[v]>tx[v]+y) mt[v]=tx[v]+(ty[v]=tag[v]=y), MIN(t[v],mt[v]);
}
inline void push(const int&v)
{
if(tag[v]==inf) return;
New(A), mask(A,tag[v]);
New(B), mask(B,tag[v]);
tag[v]=inf;
}
int que(int v,int l,int r)
{
if(!tv[v]|tx[v]==inf|ty[v]==inf|a[i].y<l) return inf;
if(a[i].y>=r) return t[v];
push(v);
int m=l+r>>1;
return min(que(L),que(R));
}
void mdf(int v,int l,int r)
{
if(!tv[v]|a[i].y>r) return;
if(a[i].y<=l) {mask(v,-y); return;}
push(v);
int m=l+r>>1;
mdf(L), mdf(R);
update(v);
}
void add(int v,int l,int r)
{
New(v);
if(l==r) {tx[v]=-x; return;}
push(v);
int m=l+r>>1;
a[i].y<=m ? add(L) : add(R);
update(v);
}
void work1()
{
sort(a+1,a+n+1,cmp);
MS(tv,0) MS(tx,51) MS(ty,51) MS(tag,51) MS(t,51) MS(mt,50)
fo(i,1,n)
{
x=X[a[i].x], y=Y[a[i].y];
MIN(a1,2*(que(1,1,nY)+x+y));
mdf(1,1,nY);
add(1,1,nY);
}
}
int Que(int v,int l,int r,bool k)
{
if(!tv[v]|a[i].y<l) return inf;
if(a[i].y>=r) return k?t[v]:tx[v];
int m=l+r>>1;
return min(Que(L,k),Que(R,k));
}
void Mdf(int v,int l,int r,int pv,bool k)
{
New(v);
if(l==r) {k?MIN(t[v],pv):MIN(tx[v],pv); return;}
int m=l+r>>1;
a[i].y<=m ? Mdf(L,pv,k) : Mdf(R,pv,k);
tx[v]=min(tx[A],tx[B]), t[v]=min(t[A],t[B]);
}
void work2()
{
MS(tv,0) MS(tx,51) MS(t,51)
fo(i,1,n)
{
x=X[a[i].x], y=Y[a[i].y];
MIN(a1,2*(Que(1,1,nY,1)+x+y));
Mdf(1,1,nY,Que(1,1,nY,0),1);
Mdf(1,1,nY,-x-y,0);
}
}
void rotate()
{
fo(i,1,n) a[i]={-Y[a[i].y],X[a[i].x]};
fo(i,1,n) X[i]=a[i].x, Y[i]=a[i].y;
sort(X+1,X+n+1), nX=unique(X+1,X+n+1)-X-1;
sort(Y+1,Y+n+1), nY=unique(Y+1,Y+n+1)-Y-1;
fo(i,1,n) a[i]={lower_bound(X+1,X+nX+1,a[i].x)-X,lower_bound(Y+1,Y+nY+1,a[i].y)-Y};
}
int main()
{
scanf("%d",&n);
a1=a2=a3=a4=a5=a6=a7=a8=-inf;
fo(i,1,n)
{
scanf("%d%d",&X[i],&Y[i]), a[i]={X[i],Y[i]};
MAX(a1, X[i]+Y[i]);
MAX(a2,-X[i]+Y[i]);
MAX(a3,-X[i]-Y[i]);
MAX(a4, X[i]-Y[i]);
MAX(a5,-X[i]); MAX(a6,X[i]);
MAX(a7,-Y[i]); MAX(a8,Y[i]);
}
a1=2*max(max(a1+a3,a2+a4),max(max(a1+a5+a7,a2+a6+a7),max(a3+a6+a8,a4+a5+a8)));
printf("%d\n",a1);
a1=inf;
sort(X+1,X+n+1), nX=unique(X+1,X+n+1)-X-1;
sort(Y+1,Y+n+1), nY=unique(Y+1,Y+n+1)-Y-1;
fo(i,1,n) a[i]={lower_bound(X+1,X+nX+1,a[i].x)-X,lower_bound(Y+1,Y+nY+1,a[i].y)-Y};
work1(), work2(), rotate();
work1(), work2(), rotate();
work1(), rotate(), work1();
printf("%d",a1);
}
【纪中集训】2019.08.02【NOIP提高组】模拟 A 组TJ的更多相关文章
- 【纪中集训2019.3.27】【集训队互测2018】小A的旅行(白)
题目 描述 \(0-n-1\)的图,满足\(n\)是\(2\)的整数次幂, $ i \to j $ 有 $ A_{i,j} $ 条路径: 一条路径的愉悦值定义为起点和终点编号的\(and\)值 ...
- 【纪中集训2019.3.23】Deadline
题意 描述 一个二分图\((A,B)\),每个点额外有一个颜色0或者1: 匹配时,只能相同颜色的点匹配: 给出\(A\)中的颜色,问如何分配\(B\)种的颜色使得\((A,B)\)的最大匹配最小: 范 ...
- 【纪中集训2019.3.12】Mas的仙人掌
题意: 给出一棵\(n\)个点的树,需要加\(m\)条边,每条边脱落的概率为\(p_{i}\) ,求加入的边在最后形成图中仅在一个简单环上的边数的期望: \(1 \le n \ , m \le 1 ...
- 【纪中集训2019.3.23】IOer
题目 描述 你要在\(m\)天内,刷\(n\)道题,每天可以刷的题的数目不限: 第\(i\)天可以刷的题目的种类是\(ui+v\): 两种刷题的方案不同当且仅当某天刷题的数量不同或者依次刷题的种类不同 ...
- 【纪中集训2019.3.11】Cubelia
题目: 描述 给出长度为\(n\)的数组\(a\)和\(q\)个询问\(l,r\). 求区间\([l,r]\)的所有子区间的前缀和的最大值之和: 范围: $n \le 2 \times 10^5 , ...
- 【纪中集训2019.3.13】fft
题意: 描述 一共有\(n+m\)道题,其中\(n\)道答案是\(A\),\(m\)道答案是\(B\): 你事先知道\(n和m\),问在最优情况下的期望答错次数,对\(998244353\)取模: 范 ...
- 【纪中集训2019.3.12】Z的礼物
题意 已知\(a_{i} = \sum_{j=1}^{i} \{^{i} _{j} \}b_{j}\), 给出\(a_{1} 到 a_{n}\) : 求\(b_{l} 到 b_{r}\)在\(1e9+ ...
- 「中山纪中集训省选组D1T1」最大收益 贪心
题目描述 给出\(N\)件单位时间任务,对于第\(i\)件任务,如果要完成该任务,需要占用\([S_i, T_i]\)间的某个时刻,且完成后会有\(V_i\)的收益.求最大收益. 澄清:一个时刻只能做 ...
- 2018.12.08【NOIP提高组】模拟B组总结(未完成)
2018.12.08[NOIP提高组]模拟B组总结 diyiti 保留道路 进化序列 B diyiti Description 给定n 根直的木棍,要从中选出6 根木棍,满足:能用这6 根木棍拼出一个 ...
- 纪中集训总结 && 新学期目标
于是紧接着又发了第二篇. 关于这次去完纪中以后的感想,写完后总觉得少了些什么,因此就发一篇小目标集合来凑数补充一下吧. Part I:图论 这方面我去之前就是很有自信,事实证明像基础的最短路.生成树什 ...
随机推荐
- springMVC的controller更改了,如何不重启,而自动刷新的解决办法(亲测,一招解决)
Tomcat con/ service.xml 配置如下一行代码: <Context reloadable="true"/> </Host> 然后以de ...
- uiautomator python版本
摘要: 利用jsonrpc技术直接包装uiautomator,调用uiautomator的api在本地直接编写脚本,然后运行测试case,这样比用Java写,然后再打包,runcase要简单的多,关键 ...
- 英语单词operand
operand 来源 [root@centos7 ~]# mkdir mkdir: missing operand 缺少操作数,也就是要创建的目录 Try 'mkdir --help' for mo ...
- 4412 PWM
一.PWM原理 1.有源蜂鸣器和无源蜂鸣器的概念 有源蜂鸣器高电平就响,无源蜂鸣器需要PWM波才响. 2.PWM脉冲波 PWM = 定时器 + 定时器中断(重载) + IO输出(翻转) 3.分析原理图 ...
- The Rotation Game
题目链接 题意:有八种操作棋盘进行移动,使得中间8个数字一样,问·最短移动步数及如何移动. 思路:dfs,因为当中间八个数字中有m个数字不同时,至少需要m次操作,将这个m作为估值.
- HTTP返回码中200,302,304,404,500得意思
状态码的职责是当客户端向服务器端发送请求时,描述返回请求结果.借助状态码,用户可以知道服务器端是正常处理了请求,还是出现了什么错误. 2开头的,响应成功,客户端请求服务器正常响应处理了. 3开头的,响 ...
- 用js实现摇一摇功能
function init(){ if (window.DeviceMotionEvent) { // 移动浏览器支持运动传感事件 window.addEventListener('devicemot ...
- left、pixelLeft、posLeft的区别
yexj00.style.pixelLeft=50yexj00.style.left=50pxyexj00.style.posLeft=50he.style.pixelLeft=39he.style. ...
- 3天教你掌握Python必备常用英语词汇
第一天 path [ pɑ:θ ] 路径 unexpected [ˌʌnɪkˈspektɪd] 不期望的 class [klɑ:s] 类 usage [ˈju:sɪdʒ] 使用 public ['p ...
- Python笔记(四)_字符串的方法
字符串的方法 []表示该参数时可选的,start和end参数表示范围 count(sub[, start[, end]]) 返回sub在字符串里边出现的次数 find(sub[, start[, en ...