JYOI 四月普及模拟赛 题解

这场比赛是本蒟蒻出的。

第一次出比赛,给原来四中校内的同学做一做,当然很多题不是原创的是搬的,毕竟水平有限

当然整场比赛还是比较简单的,毕竟难度只到普及,不知道大家自我感觉如何,如果对比赛有什么建议也可以在评论区留言

题解如下:

T1 方块游戏(game)

改编自[NOI2002] 银河英雄传说

一道水题 说实话挺简单的不知道为啥洛谷是蓝题,我觉得就普及+难度

数据范围很大,暴力模拟显然是不能直接做出来的我们考虑并查集优化

这道题实质不就是一道带权并查集对于每个点,分别记录所属链的头结点、该点到头结点的距离以及它所在集合的大小。

每次合并将y接在x的尾部,改变y头的权值和所属链的头结点,同时改变x的尾节点。

注意:每次查找的时候也要维护每个节点的权值。(很重要

每次查询时计算两点的权值差。标程放在下面,这应该很详细了,看下一题吧

#include <iostream>
#include <cstring>
#include <algorithm>
#define For(i,m,n) for(int i=m;i<=n;i++)
#define sc scanf
#define pr printf
const int N = 1e5 + 10;
using namespace std;
int n,m,k,t,p[N],ret[N],cnt[N];
char op[2];
int find(int x)
{
    if(x == p[x])return x;
    int y = p[x];
    p[x] = find(p[x]);
    ret[x] += ret[y];
    return p[x];
}
int main()
{
    freopen("game.in", "r", stdin);
    freopen("game.out", "w", stdout);  
    sc("%d",&n);
    For(i,1,n)p[i] = i,cnt[i] = 1;
    For(i,1,n)
    {
        int j;
        sc("%s%d",op,&j);
        if(op[0] == 'M')
        {
            sc("%d",&k);
            int x = find(j),y = find(k);
            p[x] = y;
            ret[x] += cnt[y];
            cnt[y] += cnt[x];
        }
        else
        {
            find(j);
            pr("%d
",ret[j]);
        }
    }
    return 0;
}

T2 城市联盟(city)

原题来自一本通题目友好城市

题目有两个条件:1.每个城市只能开辟一个高速 2.所有的铁路之间不能相交、

求最多可以建多少条铁路

先讲O(N2)的做法

我们可以这样想,只固定考虑一个阵营的坐标

容易发现 我们要想让铁路不相交,就必须让一个阵营的所有城市排序后,求出另外一边的LCS(最长上升子序列),可以用反证法

然后O(N log N)的做法 就是基于二分对LCS进行优化,优化为一个log;

只不过我数据范围应该大一点——这题才5000,真的是良心,N2都可以过,不想说啥了;

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 5010;

typedef pair<int,int> PII;

int f[N],q[N];

PII a[N];
int main()
{
    freopen("city.in","r",stdin);
    freopen("city.out","w",stdout);
    int n;
    cin  >> n;
    for(int i = 0; i < n; ++ i) scanf("%d%d",&a[i].first,&a[i].second);
    sort(a,a + n);
    int len = 0;
    q[0] = 10;
    for(int i = 0; i < n; ++ i)
    {
        int l = 0,r = len;
        while(l < r)
        {
            int mid = l + r  + 1 >> 1;
            if(q[mid] < a[i].second) l = mid;
            else r = mid - 1;
        }
        q[r + 1] = a[i].second;
        len = max(len,r + 1);
    }
    
    cout << len << endl;
    return 0;
    

}//二分优化LCS

T3 苍穹之路(sky)

这题我稍微改了下题面

翻译一下就是:

给定一个无向图,有n个顶点,m条边,每条边有一个权值t,但当当前走过的边数大于等于了这条路的分值h,这条路就不能再走了。

求顶点s到终点z所走的最小权值和,若走不到,则输出impossible!

这道题一看就是个最短路,多了关键字罢了

标程(伪):

#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
#define maxn 3000010
//#define int long long
#define INF 0x3f3f3f3f

using namespace std;

bool vis[maxn];
int n,m,s,t,tot,ans,now;
int Step[maxn],Dis[maxn],h[maxn];
struct node{int pos,Step,Dis;};
struct edge{int fr,to,dis,cost,nxt;}e[maxn];

inline int read(){
    int s=0,w=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') s=(s<<1)+(s<<3)+ch-'0',ch=getchar();
    return s*w;
}

void add(int fr,int to,int dis,int cost){
    e[++tot].to=to;e[tot].cost=cost;
    e[tot].dis=dis;e[tot].nxt=h[fr];
    h[fr]=tot;
}

void spfa(int st){
    memset(Dis,INF,sizeof Dis);
    Dis[st]=Step[st]=0;queue<node> d;//vis[st]=1;
    d.push((node){st,0,0});
    while(!d.empty()){
        node u=d.front();d.pop();//vis[u.pos]=0;
        for(int i=h[u.pos];i != -1;i=e[i].nxt){
            if(u.Step>=e[i].cost) continue;int to=e[i].to;
            if(Dis[to]>u.Dis+e[i].dis||Step[to]>u.Step+1){
                Dis[to]=u.Dis+e[i].dis,Step[to]=u.Step+1;
                d.push((node){to,Step[to],Dis[to]});
            }
        }
    }
}

signed main(){
    freopen("sky.in","r",stdin);
    freopen("sky.out","w",stdout);
    memset(h,-1,sizeof h);
    scanf("%d%d%d%d",&n,&m,&s,&t);
    for(int i=1,fr,to,dis,cost;i<=m;i++){
        scanf("%d%d%d%d",&fr,&to,&dis,&cost);
        add(fr,to,dis,cost);add(to,fr,dis,cost);
    }
    spfa(s);
    if(Dis[t]==INF) printf("bao 0
");
    else printf("%d
",Dis[t]);
    return 0;
}

一场考察较容易基础算法的比赛就这样end

 

THANKS

原文地址:https://www.cnblogs.com/yjyl0098/p/14620286.html