NOIP2018 提高组B组模拟 9.22

【jzoj 1207】遥控车 (Standard IO)

Time Limits: 1000 ms $ \quad $ Memory Limits: 65536 KB Detailed Limits

Description

平平带着韵韵来到了游乐园,看到了 $ n $ 辆漂亮的遥控车,每辆车上都有一个唯一的名字 $ name[i] $ 。
韵韵早就迫不及待地想玩名字是 $ s $ 的遥控车。
可是韵韵毕竟还小,她想象的名字可能是一辆车名字的前缀
(也就是说能确定一个 $ i $,使 $ s $ 是 $ name[i] $ 的前缀),
这时她就能玩第 $ i $ 辆车;或者是一个无中生有的名字,即 $ s $ 不是任何一辆车名字的前缀,这时候她什么也不能玩。
你需要完成下面的任务:
1.韵韵想了 $ m $ 个她想要的名字,请告诉她能玩多少次。
2.由于管理员粗心的操作,导致每辆车的摆放位置都可能出现微小的差错,
原来第 $ i $ 辆车现在的位置可能是 $ i-1 、i、i+1 $ 中的任意一个
(第 $ 1 $ 辆车的位置不可能是 $ 0 $ ,第 $ n $ 辆车的位置不可能是 $ n+1 $ )。
请你计算出共有多少种可能的排列。
注:数据保证当 $ s $ 是 $ name[i] $ 的前缀时,$ i $ 是唯一确定的。一辆车可以玩多次。
 

Input

第一行是 $ 2 $ 个正整数 $ n、m $ 。
接下来 $ n $ 行,每行 $ 1 $ 个字符串 $ name[i] $,表示第 $ i $ 辆车的名字。
接下来 $ m $ 行,每行 $ 1 $ 个字符串 $ s $ ,表示韵韵想要的名字。
 

Output

第一行输出韵韵能玩的次数。
第二行输出共有多少种可能的排列。
 

Sample Input

 4 4  
 Abcd 
 DeF 
 AAa 
 aBccc 
 Ab 
 AA 
 AbC 
 aBcc

Sample Output

 3 
 5 

Data Constraint

【数据规模和约定】
对于题目涉及到的字符串严格区分大小写,且长度小于 $ 255 $ 。
对于 $ 20 $%的数据 $ n≤10,m≤10 $ ;
对于 $ 40 $%的数据 $ n≤1000,m≤1000 $ ;
对于 $ 100 $ %的数据 $ n≤10000,m≤10000 $
 

Solution

【题意简述】
本题2问。

  1. 给出 $ N+M $ 个字符串,问后 $ M $ 个字符串中有多少是前 $ N $ 个字符串中某个的前缀。
  2. 数字 $ 1~N $ ,对于 $ i $ 可以放在 $ i-1,i,i+1 $ 中的一个位置上。问有多少种放法。

【解决方法】
第一问,我们看到数据规模:$ N、M $ 为 $ 10000 $ 以内的整数,简单模拟逐个去匹配显然是会超时的。
我们只需要将 $ N $ 个名字串进行一次快速排序,再利用二分查找来检索便可。
第二问,这一问其实是找递推关系。如果用搜索来做的话是会超时的,因为答案的值是以指数增长的。
记 $ f(i) $ 为长度为的可能组合数。
显然,$ f(1)=1;f(2)=2 $ 。
如果搜索出小数据的答案,就能发现递推关系 $ f(n)=f(n-1)+f(n-2) $ 。
事实上,假设我们已经求出 $ f(1)~f(i-1) $ 的值。显而易见的,此时第 $ i $ 位仅能放在第 $ i-1 $ 或第 $ i $ 位。
1.如果第 $ i $ 个数放在第 $ i $ 位,那么此时答案为 $ f(i-1) $ ;
2.如果第 $ i $ 个数在第 $ i-1 $ 位上,那么第 $ i-1 $ 个数只能放在第 $ i $ 位
(否则第 $ i $ 位放的是第 $ 1~i-2 $个数,与题意不符),此时答案为 $ f(i-2) $。
所以当 $ n≥3 $ 时 $ f(n)=f(n-1)+f(n-2) $ 。
另外,由于答案巨大,需要高精度加法 ,这里不再赘言。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 10005
int read(){
	char ch; int x=0;
	while(ch=getchar(),ch<'0'||ch>'9');x=ch-48;
	while(ch=getchar(),ch>='0'&&ch<='9')x=10*x+ch-48;
	return x;
}
struct Num{
	int len,a[100005];
	void print(){
		for(int i=len-1;~i;--i) printf("%d",a[i]);
		puts("");
	}
}f1,f2,F;
string s[N],p;
int n,m,ans;
int main(){
	n=read(); m=read();
	for(int i=1;i<=n;++i) cin>>s[i];
	sort(s+1,s+1+n);
	for(int i=1;i<=m;++i){
		cin>>p;
		int l=1,r=n;
		while(l<=r){
			int mid=l+r>>1;
			if(s[mid]<p) l=mid+1; 
			else r=mid-1;
		}
		if(s[l].find(p,0)==0) ++ans;
	}
	printf("%d\n",ans);
	if(n<=2) printf("%d",n);
	else {
		f1.len=1; f1.a[0]=1; 
		f2.len=1; f2.a[0]=2;
		for(int i=3;i<=n;++i){
			F.len=max(f1.len,f2.len);
			memset(F.a,0,sizeof(F.a));
			for(int j=0;j<F.len;++j){
				F.a[j]+=f1.a[j]+f2.a[j];
				if(F.a[j]>9){
					F.a[j]%=10;
					++F.a[j+1];
				}
			}
			if(F.a[F.len]) ++F.len; 
			f1.len=f2.len; for(int j=0;j<f2.len;++j) f1.a[j]=f2.a[j];
			f2.len=F.len; for(int j=0;j<F.len;++j) f2.a[j]=F.a[j];
		}
		F.print(); 
	}
	return 0;
}

--

【jzoj 1208】车展 (Standard IO)

Time Limits: 1000 ms $ \quad $ Memory Limits: 65536 KB Detailed Limits

Description

遥控车是在是太漂亮了,韵韵的好朋友都想来参观,所以游乐园决定举办 $ m $ 次车展。
车库里共有 $ n $ 辆 车,从左到右依次编号为 $ 1,2,…,n $ ,每辆车都有一个展台。
刚开始每个展台都有一个唯一的高度 $ h[i] $ 。主管已经列好一张单子:
$ L_1 R_1 $
$ L_2 R_2 $

$ L_m R_m $
单子上的 $ (L_i,R_i) $ 表示第 $ i $ 次车展将要展出编号从 $ L_i $ 到 $ R_i $ 的车。
为了更加美观,展览时需要调整展台的高度,使参展所有展台的高度相等。
展台的高度增加或减少 $ 1 $ 都 需花费 $ 1 $ 秒时间。由于管理员只有一个人,所以只好对每个展台依次操作。
每次展览结束后,展台高度 自动恢复到初始高度。
请告诉管理员为了举办所有展览,他最少需要花多少时间将展台调整好。
 

Input

第一行为两个正整数 $ n、m $ 。
第二行共 $ n $ 个非负整数,表示第 $ i $ 辆车展台的高度 $ h[i] $ 。
接下来 $ m $ 行每行 $ 2 $ 个整数 $ L_i、R_i(L_i≤R_i)$ 。
 

Output

一个正整数,调整展台总用时的最小值。
 

Sample Input

 6 4 
 4 1 2 13 0 9 
 1 5 
 2 6 
 3 4 
 2 2

Sample Output

 48 

 

Data Constraint

【数据规模和约定】
对于 $ 50 $ %的数据 $ n≤500,m≤1000 $;
对于 $ 80 $ %的数据 $ n≤1000,m≤100000 $ ;
对于 $ 100 $ %的数据 $ n≤1000,m≤200000 $ ;
答案小于 $ 2^64 $ 。
 

Solution

将所有h[i]对应于数轴上,问题转化为找一个点,使所有的点到这个点的距离和最小。
至此,很多人已经发现中位数所对应的点可以取到最小值(具体的证明在这里不再详述,很多书籍上都可以找到),
因此所有展台的高度应为区间[l,r]中的中位数。
因为要求最小的代价,所以还需要知道区间[l,r]所有数的和,以及小于中位数的所有数的和。
求中位数的方法有很多,下面介绍几种方法。

  • 方法一:
    对区间 $ [l,r] $ 排序,然后线性扫描得到答案。
    复杂度:$ O(MNlogN) $
    期望得分:50

  • 方法二:
    将所有的高度排序,记录展台的编号。线性扫描后同样可以得到答案
    复杂度:$ O(NlogN+MN) $
    期望得分:70~80

  • 方法三:
    先预处理出每个区间的答案,读入后直接输出。
    这时,需要维护一个大根堆和一个小根堆,其中小根堆中每个数都不小于大根堆中的最大值。
    枚举区间的起点,依次向后加入数,只需控制大根堆中数的个数小于等于小根堆的个数。
    那么大根堆中最大的数就是中位数,大根堆中所有数的和就是小于中位数的所有数的和。具体实现请看标程。
    复杂度:$ O(N2logN+M) $
    期望得分:100

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
const long long N=1010;
vector<long long> vec[N];
long long n,m,a[N],ans;
int main(){
	scanf("%lld%lld",&n,&m);
	for(long long i=1;i<=n;i++)scanf("%lld",&a[i]);
	for(long long i=1;i<=m;i++){
		long long l,r;
		scanf("%lld%lld",&l,&r);
		vec[l].push_back(r);
	}
	for(long long i=1;i<=n;i++){
		if(vec[i].size()==0)continue;
		sort(vec[i].begin(),vec[i].end());
	}
	for(long long i=1;i<=n;i++){
		if(vec[i].size()==0)continue;
		priority_queue<long long> q;
		priority_queue<long long,vector<long long>,greater<long long> > Q;
		long long L=i;
		q.push(a[L]);
		long long sum1=a[L];
		long long sum2=0;
		L++;
		for(long long j=0;j<=vec[i].size()-1;j++){
			while(L<=vec[i][j]){
				if(a[L]>q.top()){
					Q.push(a[L]);
					sum2+=a[L];
				}
				else {
					q.push(a[L]);
					sum1+=a[L];
				}
				if(q.size()>Q.size()){
					while(((L-i+1)+1)/2<q.size()){
						Q.push(q.top());
						sum2+=q.top();
						sum1-=q.top();
						q.pop();
					}
				}
				else{
					while(((L-i+1)+1)/2>q.size()){
						q.push(Q.top());
						sum2-=Q.top();
						sum1+=Q.top();
						Q.pop();
					}
				}
				L++;
			}
			ans+=q.top()*(q.size()-Q.size())-sum1+sum2;
		}
	}
	printf("%lld",ans);
	return 0;
}

【jzoj 1209】拉力赛 (Standard IO)

Time Limits: 1000 ms $ \quad $ Memory Limits: 65536 KB Detailed Limits

Description

车展结束后,游乐园决定举办一次盛大的山道拉力赛,平平和韵韵自然也要来参加大赛。
赛场上共有 $ n $ 个连通的计时点,$ n-1 $ 条赛道(构成了一棵树)。
每个计时点的高度都不相同(父结点的高 度必然大于子结点),相邻计时点间由赛道相连。
由于马力不够,所以韵韵的遥控车只能从高处驶向低 处。而且韵韵的车跑完每条赛道都需花费一定的时间。
举办方共拟举办 m 个赛段的比赛,每次从第 $ u $ 个计时点到第 $ v $ 个计时点,
当然其中有不少比赛韵韵的遥控车是不能参加的(因为要上坡)。
平平想知道他能参加多少个赛段的比赛,并且想知道他完成这些赛段的总用时。
 

Input

第一行两个整数 $ n,m $ 。
接下来 $ n-1 $ 行每行 $ 3 $ 个整数 $ a、b、t $ 。
表示韵韵的遥控车可以花 $ t $ 秒从第 $ a $ 个计时点到第 $ b $ 个计时点。
接下来 $ m $ 行每行 $ 2 $ 个整数 $ u、v $ ,意义如描述所示。
 

Output

第一行输出一个正整数,表示能参加的赛段数。
第二行输出一个正整数,表示总用时。
 

Sample Input

 6 2 
 1 2 1 
 2 4 1 
 2 5 1 
 5 6 1 
 1 3 1 
 2 6 
 4 5

Sample Output

 1 
 2

 

Data Constraint

【数据规模和约定】

第一个计时点的高度是最高的;
$ u≠v $ ;
对于 $ 50 $ %的数据 $ n≤1000 m≤1000 $ ;
对于 $ 100 $ %的数据 $ n≤10000 m≤100000 $ ;
答案小于 $ 2^64 $ 。
 

Solution

【题意简述】
对于给定的 $ u、v $ ,判断 $ v $ 是否在以 $ u $ 为根的子树内,并统计“是”的各数。

对图进行一次 $ dfs $ ,记录每个点第一次被访问到的时间戳 $ begin[i] $ 和完成以这个点围根的树的时间戳 $ finish[i] $ 。
则 $ u $ 是 $ v $ 的祖先的充要条件是
$ Begin[u]<begin[v]且finish[v]<finish[u] $
复杂度:$ O(M+N) $
期望得分:100

但是,这题我直接用 $ lca $ 水了,因为 $ v $ 在 $ u $ 的子树内,那么它的 $ lca $ 一定是 $ u $ 啊!

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
#define ll long long
int read(){
	char ch; int x=0;
	while(ch=getchar(),ch<'0'||ch>'9');x=ch-48;
	while(ch=getchar(),ch>='0'&&ch<='9')x=10*x+ch-48;
	return x;
}
vector<pair<int,int> >e[10005];
int n,m,dep[10005],f[10005][21];
ll dis[10005],ans1,ans2;
void dfs(int u,int fa){
	dep[u]=dep[fa]+1;
	f[u][0]=fa;
	for(int i=1;i<=20;++i)
		f[u][i]=f[f[u][i-1]][i-1];
	for(int i=0;i<e[u].size();++i){
		if(e[u][i].first==fa) continue;
		dis[e[u][i].first]=dis[u]+e[u][i].second;
		dfs(e[u][i].first,u);
	}
}
inline int lca(int u,int v){
	if(dep[u]>dep[v]) swap(u,v);
	for(int i=20;~i;--i)
		if(dep[u]<=dep[v]-(1<<i)) v=f[v][i];
	if(u==v) return u;
	for(int i=20;~i;--i)
		if(f[u][i]!=f[v][i]){ u=f[u][i]; v=f[v][i]; }
	return f[u][0];
}
int main(){
	n=read(); m=read();
	for(int i=1;i<n;++i){
		int a=read(),b=read(),t=read();
		e[a].push_back(make_pair(b,t));
		e[b].push_back(make_pair(a,t));
	}
	dfs(1,0);
	while(m--){
		int u=read(),v=read(); 
		if(lca(u,v)==u){
			++ans1;
			ans2+=dis[v]-dis[u];
		}
	}
	printf("%lld\n%lld",ans1,ans2);
	return 0;
}
posted @ 2018-09-28 19:40  potrem  阅读(300)  评论(0编辑  收藏  举报