HGOI7.25集训题解

题解

讲道理,其实从算法的思路上来说,实现15年day2的题,在代码量上并没什么难度..但确实还是比较考验思维含量的orz。而我….日常沙雕。


第一题——跳石头(stone)

【题目描述】

  • 给出距离L,在0-L之间有n个点。先在删去m个点使得点之间(包括0和L)的最小距离差最大。

  • ok,这是道一眼就知道的二分题。二分出一个答案,线性扫描区间内的点位置。
  • 如果两点之间的差值大于这个答案,就保留后面一个点。统计出保留的点的个数和题目所需要保留的点的个数。(传统二分)
  • 然而首测只有70分。因为二分打的不熟练所以有三个点的最优解直接被略过了。感谢巨佬excitedfrog提醒。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
void fff(){
    freopen("stone.in","r",stdin);
    freopen("stone.out","w",stdout);
}
const int MAXN=50100;
int L,n,m,minn;
int a[MAXN];
int check(int x){
    int sum=0;
    int temp_pos=0;
    for (int i=1;i<=n;i++){
        if(a[i]-temp_pos>=x){
            sum++;
            temp_pos=a[i];
        }
    }
    if(L-temp_pos<x) sum--;
    return sum;
}
int main(){
    fff();
    minn=999999999;
    scanf("%d%d%d",&L,&n,&m);
    for (int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        minn=min(minn,a[i]-a[i-1]);
    }
    a[n+1]=L;
    if(n==m){
        printf("%d",L);
        return 0;
    }
    int l=minn-1,r=L,mid;
    while (l<=r){
        mid=(l+r)/2;
        int tmp=check(mid);
        if(tmp>n-m){
            l=mid+1;
        }else if(tmp<n-m){
            r=mid-1;
        }else{
            int temp_pos=0,ans=999999999;
            for (int i=1;i<=n;i++){
                if(a[i]-temp_pos>=mid){
                    ans=min(ans,a[i]-temp_pos);
                    temp_pos=a[i];
                }
            }
            ans=min(ans,L-temp_pos);
            printf("%d",ans);
            return 0;
        }
    }
    int temp_pos=0,ans=999999999;
    for (int i=1;i<=n;i++){
        if(a[i]-temp_pos>=r){
            ans=min(ans,a[i]-temp_pos);
            temp_pos=a[i];
        }
    }
    ans=min(ans,L-temp_pos);
    printf("%d",ans);

}

第二题——子串(substring)

【题目描述】

  • 给出字符串a,和b,要求在串a中求出k段相连与b匹配。求出所有匹配的方案个数对1000000007取模。

  • 其实这道题对思维含量还是比较考验的。对于内存的限制也是比较高。刚开始没打滚动数组,只测了70分。
  • 讲一下方程 f[i][j][k] f [ i ] [ j ] [ k ] 表示A段i字符与B段j字符匹配时取k段的方案数。那么就可以从 f[i1][j1][k] f [ i − 1 ] [ j − 1 ] [ k ] f[ij1][j1][k] f [ i 之 前 和 j − 1 位 匹 配 的 位 置 ] [ j − 1 ] [ k ] 的和。
  • 这里前置和就可以用 sum[j][k] s u m [ j ] [ k ] 来解决, jk j k 的意义和上面一样。
  • 但这个复杂度看着数据就知道肯定会爆。那么观察方程发现i只会从i-1转移过来。那么将j,k倒序循环,就可以省略i位的空间。
  • 代码真的特别短orz

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#define LL long long
#define MOD 1000000007
using namespace std;
void fff(){
    freopen("substring.in","r",stdin);
    freopen("substring.out","w",stdout);
}
int n,m,K;
char A[1110],B[220];
int dp[220][220]={1},sum[220][220];
int nxt[1100];
int main(){
    fff();
    scanf("%d%d%d",&n,&m,&K);
    memset(nxt,-1,sizeof(nxt));
    scanf("%s",A);
    scanf("%s",B);
    for (int i=1;i<=n;i++){
        for (int j=m;j>=1;j--){
            for (int k=1;k<=K;k++){
                if(A[i-1]==B[j-1]) sum[j][k]=(dp[j-1][k-1]+sum[j-1][k])%MOD;
                else sum[j][k]=0;
                dp[j][k]=(dp[j][k]+sum[j][k])%MOD;
            }
        }
    }
    cout<<dp[m][K];
}

第三题——运输计划(transport)

【题目描述】

  • 给出n个点的树,具有边权,并给出m对点,求去掉某一边权之后的所有对点上的路径权值最大值最小。

  • 又是最大值最小的题。首选还是二分。虽然我首测的时候实在是想不出来,看着数据打了把m=1的情况骗走二十分。但还是不是很满意orz。因为有40分的数据是一条链的没有打出来orz

  • 首先知道这个是一棵树,两点之间的距离就可以用到LCA算法来解决。边权就下沉到子节点上。

  • 再来解决去掉一条边上的问题。求出所有路径上的总和之后,根据二分答案进行判断和树上差分(这个自己百度吧….),记录下有多少对点需要进行减去权值的操作的。并且求出这些路径的最大值。
  • 如果一个点覆盖的点的个数刚好是需要减去操作点的个数,那就是需要判断这些点当中权值最大的。如果路径的最大值减去这个最大的权值小于二分的答案,那就是合法的情况。
  • 注意快读邻接表卡常。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <vector>
#define LL long long
#define MAXN 300010
#include <queue>
using namespace std;
void fff(){
    freopen("transport.in","r",stdin);
    freopen("transport.out","w",stdout);
}
int n,m,tot=0;
struct Edge{
    int to,nxt,w;
}edge[MAXN<<1];
int head[MAXN];
int readn(){
    char c=getchar();
    int x=0;
    while (c<'0'||c>'9') c=getchar();
    while (c=='-'||(c>='0'&&c<='9')) x=x*10+c-'0',c=getchar();
    return x;
}
void add(int from,int to,int w){
    edge[++tot].nxt=head[from];
    edge[tot].w=w;
    edge[tot].to=to;
    head[from]=tot;
}
struct query{
    int f,t,lca,cost;
}qu[MAXN];
bool visited[MAXN];
int depth[MAXN],fa[MAXN][20],dist[MAXN],dd[MAXN];
queue <int> q;
void bfs(){
    memset(visited,false,sizeof(visited));
    visited[1]=true;
    q.push(1);
    depth[1]=1;
    while (!q.empty()){
        int u=q.front();q.pop();
        for (int i=head[u];i;i=edge[i].nxt){
            if(!visited[edge[i].to]){
                q.push(edge[i].to);
                dist[edge[i].to]=dist[u]+edge[i].w;
                dd[edge[i].to]=edge[i].w;
                fa[edge[i].to][0]=u;
                visited[edge[i].to]=true;
                depth[edge[i].to]=depth[u]+1;
            }
        }
    }
}
int Lca(int x,int y){
    if(depth[x]<depth[y]){
        swap(x,y);
    }
    for(int i=19;i>=0;i--){
        if(depth[fa[x][i]]>=depth[y]){
            x=fa[x][i];
        }
    }
    if(x==y) return x;
    for (int i=19;i>=0;i--){
        if(fa[x][i]!=fa[y][i]){
            x=fa[x][i];
            y=fa[y][i];
        }
    }
    return fa[x][0];
}
int maxx=0;
int s[MAXN];
void dfs(int u){
    visited[u]=true;
    for (int i=head[u];i;i=edge[i].nxt){
        if(!visited[edge[i].to]){
            dfs(edge[i].to);
            s[u]+=s[edge[i].to];
        }
    }
}
bool judge(int x){
    int cnt=0,mxt=0;
    memset(s,0,sizeof(s));
    for (int i=1;i<=m;i++){
        if(qu[i].cost<=x) continue;
        cnt++;
        s[qu[i].f]++;
        s[qu[i].t]++;
        s[qu[i].lca]-=2;
        mxt=max(mxt,qu[i].cost-x);
    }
    memset(visited,false,sizeof(visited));
    dfs(1);
    int tar=0;
    for (int i=1;i<=n;i++){
        if(s[i]==cnt) tar=max(tar,dd[i]);
    }
    return mxt<=tar;
}
int main(){
    fff();
    n=readn(),m=readn();
    for (int i=1;i<n;i++){
        int f,t,w;
        f=readn(),t=readn(),w=readn();
        add(f,t,w);
        add(t,f,w);
    }
    for (int i=1;i<=m;i++){
        qu[i].f=readn();
        qu[i].t=readn();
    }
    bfs();
    for (int j=1;j<20;j++){
        for (int i=1;i<=n;i++){
            fa[i][j]=fa[fa[i][j-1]][j-1];
        }
    }
    for (int i=1;i<=m;i++){
        qu[i].lca=Lca(qu[i].f,qu[i].t);
        qu[i].cost=dist[qu[i].f]+dist[qu[i].t]-2*dist[qu[i].lca];
        maxx=max(maxx,qu[i].cost);
    }
    int l=-1,r=maxx+1;
    #define mid (r+l)/2
    while (r-l>1){
        if(judge(mid)) r=mid;
        else l=mid;
    }
    printf("%d",r);
    return 0;

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值