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;
}
posted @ 2018-09-25 21:01  potrem  阅读(407)  评论(0编辑  收藏  举报