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;
}

转载于:https://www.cnblogs.com/PotremZ/p/Test20180928.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值