NOIP2018 模拟 9.11
ISIJ 2018 很多序列(Training Round D4T3)
题目名称:很多序列
文件名:sequences.in / sequences.out
题目描述
假设 $ x_1 < x_2 < \dots < x_n $ 且 $ x_1 $ 与 $ x_2 $ 互质。
考虑所有的单调递增序列,其首项为 $ 0 $ 且相邻两项之差是 $ x_1,x_2 \dots x_n $ 之一。
例如,当 $ n=2, x_1 = 4 , x_2 = 7 $ 时,序列可以是 $ 0, 4, 8, 15, 19, 26, 33, 40, 44 $ 。
那么请问不出现在任何序列中最大的数是多少?
限制
$ 1s \quad 256M $
对于 40% 的数据,$ 1 < n < 6 , x_1 > 1 , x_n <1000 $ ;
对于另外 30% 的数据,$ n=2 , x_n < 10^ 9 $ ;
对于另外 30% 的数据,$ 1 < n < 6 , 1 < x_1 < 10^{6-n} , x_2 > 10^{n+11} , x_n < 10^{n+12} $ 。
输入格式
第一行,一个整数 $ n $
第二行,一共 $ n $ 个整数 $ x_1 , x_2 \dots x_n $
输出格式
一个整数,表示不出现在任何序列中最大的数
输入样例
2
4 7
输出样例
17
题解
$ x_1 < 10^6 $ 按照对 $ x_1 $ 取模建图,从每个点 $ u $ 按边权为 $ x_i $ 向 $ (u+x_i) \quad mod \quad x_1 $ 连边,
从 $ 0 $ 出发到 $ u $ 的最短路径长表示,序列中最小的 $ mod \quad x_1 = u $ 的数是多少。
当然,这个数(跑 $ SPFA $ 求最短路) - $ x_1 $ 即为最大的不可取到的数。
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
#define int long long
#define N 1000005
int n,x[10],ans;
void read(int &x){
char ch;x=0;
while(ch=getchar(),ch<'0'||ch>'9');x=ch-48;
while(ch=getchar(),ch>='0'&&ch<='9')x=10*x+ch-48;
}
queue<int>q;
int dis[N];
bool vis[N];
inline void spfa(){
memset(dis,0x6f,sizeof(dis));
q.push(0); dis[0]=0;
while(!q.empty()){
int u=q.front(); q.pop(); vis[u]=0;
for(int i=2;i<=n;++i){
int v=(u+x[i]%x[1])%x[1];
if(dis[v]>dis[u]+x[i]){
dis[v]=dis[u]+x[i];
if(!vis[v]){ vis[v]=1; q.push(v); }
}
}
}
}
signed main(){
freopen("sequence.in","r",stdin);
freopen("sequence.out","w",stdout);
read(n);
for(int i=1;i<=n;++i) read(x[i]);
if(n==2) printf("%lld",x[1]*x[2]-x[1]-x[2]);
else {
spfa();
for(int i=1;i<x[1];++i) ans=max(ans,dis[i]);
printf("%lld",ans-x[1]);
}
return 0;
}
ISIJ 2018 移动光标(Cup T2)
题目名称:移动光标
文件名:cusor.in / cusor.out
题目描述
小明是一名优秀的打字员,他正在检查一份文本文件,现在已知第 $ i ( 1 \le i \le n ) $ 行有 $ L_i $ 个字符。
每时每刻光标会指向一个字符,小明可以通过键盘上的 4 个键移动光标位置。
按下 “ ↑ ” 时,光标会直接移动到上一行对应的字符处,如果已经在第一行或上一行没有同列的字符,则不作移动。
按下 “ ↓ ” 时,光标会直接移动到下一行对应的字符处,如果已经在第 行或下一行没有同列的字符,则不作移动。
按下 “ ← ” 时,光标会直接移动到同行左边的字符处,如果左边已没有字符,则不作移动。
按下 “ → ” 时,光标会直接移动到同行右边的字符处,如果右边已没有字符,则不作移动。
现在小明有 $ q $ 次询问 $ x_1 , y_1 , x_2 ,y_2 $ 表示光标需要从第 $ x_1 $ 行第 $ y_1 $ 个字符移动到第 $ x_2 $ 行第 $ y_2 $ 个字符,
求出 最少按几次键盘(保证 $ 1 \le y_1 \le L_{x_1}, 1\ le y_2 \le L_{x_2} $ )。
限制
$ 2s \quad 256M $
$ 1 \le n, L_i ,q \le 10^5 $
输入格式
第一行,一个整数 $ n $
接下来 $ n $ 行,其中第 $ i $ 行一个整数 $ L_i $
接下来一行,一个整数 $ q $
接下来 $ q $ 行,每行 4 个整数 $ x_1 , y_1 , x_2 , y_2 $
输出格式
一共 $ q $ 行,每行表示对应询问的答案
输入样例
4
3
2
4
3
3
1 1 3 2
3 3 4 2
1 3 3 4
输出样例
3
2
5
题解
大致路径是先横再往上/下再横着走,
如果 $ x_1 $ 到 $ x_2 $ 之间的 $ L $ 最小值(线段树维护区间最小值)$ m $ 比 $ y_1 $ 和 $ y+2 $ 都 小,
那么需要额外从 $ y_1 $ 走到 $ m $ 并从 $ m $ 走到 $ y_2 $ ;其他情况都是 $ |x_1-x_2| + | y_1-y_2| $ 。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,q,sum[100005<<2],x1,x2,y1,y2;
void read(int &x){
char ch;x=0;
while(ch=getchar(),ch<'0'||ch>'9');x=ch-48;
while(ch=getchar(),ch>='0'&&ch<='9')x=10*x+ch-48;
}
void build(int o,int l,int r){
if(l==r){
read(sum[o]);
return;
}
int mid=l+r>>1;
build(o<<1,l,mid); build(o<<1|1,mid+1,r);
sum[o]=min(sum[o<<1],sum[o<<1|1]);
}
int query(int o,int l,int r,int L,int R){
if(L<=l&&r<=R) return sum[o];
int mid=l+r>>1;
if(L>mid) return query(o<<1|1,mid+1,r,L,R);
else if(R<=mid) return query(o<<1,l,mid,L,R);
else return min(query(o<<1,l,mid,L,R),query(o<<1|1,mid+1,r,L,R));
}
int main(){
freopen("cusor.in","r",stdin);
freopen("cusor.out","w",stdout);
read(n);
build(1,1,n);
read(q);
while(q--){
read(x1); read(y1); read(x2); read(y2);
int tmp=query(1,1,n,min(x1,x2),max(x1,x2));
if(y1<=tmp&&y2<=tmp) printf("%d\n",abs(x2-x1)+abs(y2-y1));
else printf("%d\n",abs(y1-tmp)+abs(y2-tmp)+abs(x2-x1));
}
return 0;
}
ISIJ 2018 假期旅行(Training Round D6T2)
题目描述
这个假期,小明打算乘火车游览风光,沿途一共经过 $ n $ 个城市。
从第 $ i $ 个城市设有第 $ i $ 条铁路到达第 $ i+1 $ 个城市,这连成一条铁路链。
小明乘坐的这班火车一共有 $ k $ 个座位,从第 $ 1 $ 个城市开到第 $ n $ 个城市,但在买票时遇到 了困难,因为火车上的部分座位被订掉了。
有 $ m $ 位乘客的订单可以用各自的 $ (s,t,a) $ 来描述,
表示从第 $ s $ 个城市坐车到第 $ t $ 个城市,订了这段中的第 $ a $ 个座位(不保证乘客之间是否存在冲突,但这不会影响到小明)。
小明沮丧地发现可能没有座位从头到尾都没人预订的,不过他马上意识到自己可以订不同的座位,
只需要保证第 $ i $ 条铁路中坐的座位是没人预订的。
小明会一共乘坐 $ q $ 次火车,从第 $ l $ 到第 $ r $ 个城市。
你需要求出期间最少换几个座位(第 $ i $ 和第 $ i+1 $ 条铁路中座位不同的个数),如果无法从 $ l $ 抵达 $ r $ 则输出 $ -1 $ 。
限制
$ 2s \quad 256M $
对于 30% 的数据,$ n\le 100 , m \le 100 , k\le 100 , q=1 $
对于 60% 的数据,$ n \le 200,000 , m\le 200,000 , k \le 200,000, q=1 $
对于 100% 的数据,$ 1\le n \le 200,000 ,0 \le m \le 200,000 , 1 \le k \le 200,000 , 1 \le q \le 200,000 $
输入格式
第一行,$ 3 $ 个整数 $ n,m $ 和 $ k $
接下来 $ m $ 行,每行 3 个正整数表示这个乘客的 $ s, t $ 和 $ a ( 1 \le s < t \le n , 1 \le a \le k ) $
接下来一行,$ 1 $ 个正整数 $ q $
接下来 $ q $ 行,每行 $ 2 $ 个正整数 $ l $ 和 $ r ( 1 \le l < r \le n) $
输出格式
一共 $ q $ 行,每行一个整数表示最少要换几次座位,若无法到达则输出 $ -1 $
输入样例
5 4 3
1 4 1
2 5 3
2 3 2
4 5 2
3
1 5
3 5
4 5
输出样例
-1
2
1
样例解释
第 $ 2 $ 条轨道上所有座位都被预订了,因此从第 $ 1 $ 到第 $ 5 $ 个城市是不可能的。
从第 $ 3 $ 到第 $ 5 $ 个城市可以先买座位 $ 2 $ 的票、再换到座位 $ 1 $ 。
从第 $ 4 $ 到第 $ 5 $ 个城市只需要买座位 $ 1 $ 的票即可。
题解
对于每个城市 $ i $ ,记录 $ a[i] $ 表示 $ i $ 向后最远可以只用一种车票走到哪里。
先将 $ m $ 个订单离线,处理同个座位的预订区间(若有相交的区间则合并,可以扫描线处理区间),
相邻两个区间之间的部分是可以使用这种车票的,用线段树(维护最大值)更新这段的 $ a[i] $ 为右端点(即原来的下一个区间左端点)。
由 $ i $ 指向 $ a[i] $ 即构成了森林(因为 $ i \le a[i] $ ),求从 $ l $ 到 $ r $ 的路径长度时使用倍增即可。
预处理 $ O (n \times log_n $ ,询问在线每次 $ O(log_n) $ 。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
#define N 200005
vector<pair<int,int> >val[N];
void read(int &x){
char ch;x=0;
while(ch=getchar(),ch<'0'||ch>'9');x=ch-48;
while(ch=getchar(),ch>='0'&&ch<='9')x=10*x+ch-48;
}
int sum[N<<2],lzy[N<<2];
inline void pushdown(int o){
sum[o<<1]=max(sum[o<<1],lzy[o]);
sum[o<<1|1]=max(sum[o<<1|1],lzy[o]);
lzy[o<<1]=max(lzy[o<<1],lzy[o]);
lzy[o<<1|1]=max(lzy[o<<1|1],lzy[o]);
lzy[o]=0;
}
void modify(int o,int l,int r,int L,int R,int v){
if(L<=l&&r<=R){
sum[o]=max(sum[o],v);
lzy[o]=max(lzy[o],v);
return;
}
if(lzy[o]) pushdown(o);
int mid=l+r>>1;
if(L>mid) modify(o<<1|1,mid+1,r,L,R,v);
else if(R<=mid) modify(o<<1,l,mid,L,R,v);
else {
modify(o<<1,l,mid,L,R,v);
modify(o<<1|1,mid+1,r,L,R,v);
}
sum[o]=max(sum[o<<1],sum[o<<1|1]);
}
int query(int o,int l,int r,int x){
if(l==r) return sum[o];
if(lzy[o]) pushdown(o);
int mid=l+r>>1;
if(x>mid) return query(o<<1|1,mid+1,r,x);
else return query(o<<1,l,mid,x);
sum[o]=max(sum[o<<1],sum[o<<1|1]);
}
int f[N][21],n,m,k,ans,q;
int main(){
freopen("trip.in","r",stdin);
freopen("trip.out","w",stdout);
read(n); read(m); read(k);
for(int i=1;i<=m;++i){
int s,t,a;
read(s); read(t); read(a);
val[a].push_back(make_pair(s,t-1));
}
for(int i=1;i<=k;++i){
sort(val[i].begin(),val[i].end());
int L=0,R=-1;
for(int j=0;j<val[i].size();++j)
if(val[i][j].first<=R+1) R=max(R,val[i][j].second);
else{
modify(1,0,n,R+1,val[i][j].first-1,val[i][j].first);
L=val[i][j].first; R=val[i][j].second;
}
modify(1,0,n,R+1,n,n);
}
for(int i=1;i<n;++i){
int tmp=query(1,0,n,i);
if(!tmp) tmp=n+1;
f[i][0]=tmp;
}
f[n][0]=n; f[n+1][0]=n+1;
for(int j=1;j<=20;++j)
for(int i=n+1;i>=1;--i)
f[i][j]=f[f[i][j-1]][j-1];
read(q);
while(q--){
int l,r; ans=0;
read(l); read(r);
for(int i=20;~i;--i) if(f[l][i]<r){ l=f[l][i]; ans+=(1<<i); }
l=f[l][0]; ++ans;
if(l==n+1||l<r) puts("-1");
else printf("%d\n",ans);
}
return 0;
}