2025-10-8模拟测验

结果:\(\texttt{90 + 100 + 80 + 90 = 360 pts, rank 2}\)

T1 HYH的线段

算法:计算几何(?)。

描述

HYH 第一类平面是一个无穷大的平面,另外 HYH 同学在平面上作出两条线段 \(AB\)\(CD\)

HYH 同学发现,两条线段可能相交,也可能不相交。HYH 同学觉得好神奇哦!所以他想要知道,两条线段的距离是多少。

线段 \(AB\)\(CD\) 的距离定义为,线段 \(AB\) 上的点 \(P\) 和线段 \(CD\) 上的点 \(Q\)(均可与线段端点重合)的连线 \(PQ\) 长度的最小值。

现在给定两条线段的端点坐标,要求计算线段的距离。

输入

输入只有四行,每行两个整数(在 \([-100,100]\) 内),分别表示点 \(A,B,C,D\) 的坐标。

输出

输出只有一个四位小数,表示线段 \(AB\)\(CD\) 的距离。

样例

\(1\) 组,不包含大样例。

样例 \(1\) 输入

0 5
0 4
-1 0
1 0

样例 \(1\) 输出

4.0000

题解

首先,对于线段相交的情况,直接输出 \(0.0000\) 即可。

反之,我们写一个函数,用于求出点到线段的最小距离。显然,这个距离要么是垂线(如果可以),要么与某个端点相连。

而不相交的情况下,我们只需要对于两条线段的四个端点分别计算到另一条线段的距离,然后四个距离取最小值即可。

但是垂线、平行等情况有可能出现除 \(0\) 导致 RE,所以要大力分讨一下。

#include<bits/stdc++.h>
#define y1 y_1
using namespace std;
inline double dis(double x1, double y1, double x2, double y2){
    return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
inline double dtoxd(double x1, double y1, double xax1, double xay1, double xbx1, double xby1){
    if(xax1 == xbx1){
        if(min(xay1, xby1) <= y1 && y1 <= max(xay1, xby1)){
            return fabs(xax1 - x1);
        }
        return min(dis(x1, y1, xax1, xay1), dis(x1, y1, xbx1, xby1));
    }else{
        double k = (xay1 - xby1) / (xax1 - xbx1);
        double b = xay1 - k * xax1;
        if(!k){
            if(min(xax1, xbx1) <= x1 && x1 <= max(xax1, xbx1)){
                return fabs(xay1 - y1);
            }
            return min(dis(x1, y1, xax1, xay1), dis(x1, y1, xbx1, xby1));
        }else{
            double czk = -1.0 / k;
            double czb = y1 - czk * x1;
            double x0 = (b-czb) / (czk-k);
            double y0 = k * x0 + b;
            if(min(xay1, xby1) <= y0 && y0 <= max(xay1, xby1)){
                return dis(x1, y1, x0, y0);
            }
        }
    }
    return min(dis(x1, y1, xax1, xay1), dis(x1, y1, xbx1, xby1));
}
double a1x, a1y, b1x, b1y, c1x, c1y, d1x, d1y;
inline double fab(double x){
    if(a1x == b1x) return 101;
    double k = (a1y - b1y) / (a1x - b1x);
    double b = a1y - k * a1x;
    return k * x + b;
}
inline double fcd(double x){
    if(c1x == d1x) return 101;
    double k = (c1y - d1y) / (c1x - d1x);
    double b = c1y - k * c1x;
    return k * x + b;
}
inline bool cross(){
    // cout << a1x << " " << b1x << endl;
    if(a1x == b1x){
        return min(a1y, b1y) <= fcd(a1x) && fcd(a1x) <= max(a1y, b1y);
    }
    // cout << "Here1";
    // cout<<fab(b1x) - fcd(b1x);exit(0);
    double cross1 = (fab(a1x) - fcd(a1x)) * (fab(b1x) - fcd(b1x));
    double cross2 = (fab(c1x) - fcd(c1x)) * (fab(d1x) - fcd(d1x));
    // cout << cross1 << " " << cross2 << endl;
    // if(!cross1 || ! cross2){
    //     // cout << "duandian" << endl;
    //     return true;
    // }
    return cross1 <= 0 && cross2 <= 0;
}
// inline void Print(){
//     cout << a1x << " " << a1y << endl;
//     cout << b1x << " " << b1y << endl;
//     cout << c1x << " " << c1y << endl;
//     cout << d1x << " " << d1y << endl;
//     return;
// }
signed main(){
    // freopen("segment.in", "r", stdin);
    // freopen("segment.out", "w", stdout);
    cin >> a1x >> a1y;
    cin >> b1x >> b1y;
    cin >> c1x >> c1y;
    cin >> d1x >> d1y;
    // cout << "kkkkk";exit(0);
    if(a1x == b1x && c1x == d1x){
        if(min(a1y, b1y) <= c1y && c1y <= max(a1y, b1y)){
            cout << fixed << setprecision(4) << fabs(a1x-c1x) << '\n';
        }else if(min(a1y, b1y) <= d1y && d1y <= max(a1y, b1y)){
            cout << fixed << setprecision(4) << fabs(a1x-c1x) << '\n';
        }else{
            // Print();
            // cout << dis(1, 4, -1, 3) << endl;exit(0);
            double ans = min({
                dis(a1x, a1y, c1x, c1y)  ,
                dis(a1x, a1y, d1x, d1y)  ,
                dis(b1x, b1y, c1x, c1y)  ,
                dis(b1x, b1y, d1x, d1y)});
            cout << fixed << setprecision(4) << ans << '\n';
        }
        return 0;
    }
    if(c1x == d1x){
        swap(a1x, c1x), swap(a1y, c1y);
        swap(b1x, d1x), swap(b1y, d1y);
    }
    if(cross()){
        cout << "0.0000" << '\n';
        return 0;
    }
    // Print();
    // cout << dtoxd(a1x, a1y, c1x, c1y, d1x, d1y) << endl;
    // cout << dtoxd(b1x, b1y, c1x, c1y, d1x, d1y) << endl;
    // cout << dtoxd(c1x, c1y, a1x, a1y, b1x, b1y) << endl;
    // cout << dtoxd(d1x, d1y, a1x, a1y, b1x, b1y) << endl;
    // cout << "jbjbjb";
    double ans = min({
        dtoxd(a1x, a1y, c1x, c1y, d1x, d1y)  ,
        dtoxd(b1x, b1y, c1x, c1y, d1x, d1y)  ,
        dtoxd(c1x, c1y, a1x, a1y, b1x, b1y)  ,
        dtoxd(d1x, d1y, a1x, a1y, b1x, b1y)});
    cout << fixed << setprecision(4) << ans << '\n';
    return 0;
}

T2 HYH的逻辑电路

算法:树形 DP。

描述

HYH 逻辑电路是 HYH 最新发明的新型逻辑电路。

这个电路由三大元件组成,“And”(与)元件,“Or”(或)元件,“Xor”(异或)元件。每个元件都有两个元件通过电路向它输入信号,元件进行相应的处理后输出到下一个元件上,如图:

如果看到这行字,说明图片炸了,请联系作者,感谢!

(红点为元件,蓝线为电路)

其中,信号只有两种:0和1,每个元件对信号进行的操作与普通逻辑运行规则相同:

And:与,都为1信号输出1,其他情况输出0.

Or:或,两个信号中至少有一个1输出1,其他情况输出0。

Xor:异或,两个信号相同输出0,否则输出1。

HYH 逻辑电路是一个设计好的逻辑电路,由用户输入一些信号,经过囧囧,啊不是,种种处理,就能在唯一的输出端上得到一个信号。可是,大部分用户发现,HYH 逻辑电路无法对他们的信号得到他们想要的结果(?),于是准备投诉 HYH。HYH 很怕,所以他决定篡改最少数量的初始信号(越多越容易被发现嘛),使输出端信号改变。HYH 的标程不用说又是萝莉控语言的,请您帮他设计出一个能在普通电脑上运行的程序。

输入

首先第一行是两个数 \(n\)\(m\),表示有 \(n\) 个元件,其中 \(m\) 个元件没有输入信号。元件被编号为 \(1 \sim n\)

接下来的 \(n\) 行,第 \(i\) 行表示 \(i\) 号元件的情况,以 x y z a 表示输入信号的是 \(x\)\(y\) 号元件,输出信号到 \(z\) 号元件,元件的种类是 \(a\)。假如没有输入或没有输出的元件则以 \(0\) 表示。

种类以 \(1 \sim 3\) 表示,\(1\)表示 And,\(2\) 表示 Or,\(3\) 表示 Xor。

再接下来是 \(m\) 行,每行以 \(x,y\) 表示一个无输入信号的元件 \(x\) 的初始信号为 \(y\)。不用检验数据正确与否,信号保证只有 \(0,1\) 两种可能。

输出

输出只有一行,表示改变输出端信号最少要改变多少个初始元件。

样例

\(1\) 组,不包含大样例。

样例 \(1\) 输入

7 4
2 3 0 3
4 5 1 2
6 7 1 1
0 0 2 3
0 0 2 3
0 0 3 3
0 0 3 3
4 0
5 1
6 1
7 0

样例 \(1\) 输出

1

题解

我们令 \(dp_{u,0/1}\) 表示把 \(u\) 的输出值修改为 \(0/1\) 至少需要修改几个初始值。转移的时候大力分讨即可。时间复杂度 \(\mathcal{O}(n)\)

#include<bits/stdc++.h>
using namespace std;
inline int read(){
    int x = 0, f = 1;
    char ch = getchar();
    while(!isdigit(ch)){
        if(ch == '-') f = -1;
        ch = getchar();
    }
    while(isdigit(ch)){
        x = (x<<1) + (x<<3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}
const int N = 200005;
int n, m, root, ls[N], rs[N], type[N];
bool chu[N], haschu[N];
int dp[N][2];
inline void dfs1(int u){
    if(!u || haschu[u]) return;
    dfs1(ls[u]), dfs1(rs[u]);
    if(type[u] == 1){
        chu[u] = chu[ls[u]] & chu[rs[u]];
    }else if(type[u] == 2){
        chu[u] = chu[ls[u]] | chu[rs[u]];
    }else{
        chu[u] = chu[ls[u]] ^ chu[rs[u]];
    }
    return;
}
inline void dfs2(int u){
    if(!u) return;
    if(haschu[u]){
        dp[u][chu[u]] = 0, dp[u][!chu[u]] = 1;
        return;
    }
    dfs2(ls[u]), dfs2(rs[u]);
    if(type[u] == 1){
        dp[u][1] = dp[ls[u]][1] + dp[rs[u]][1];
        dp[u][0] = min({
            dp[ls[u]][0] + dp[rs[u]][0],
            dp[ls[u]][0] + dp[rs[u]][1],
            dp[ls[u]][1] + dp[rs[u]][0]}
        );
    }else if(type[u] == 2){
        dp[u][0] = dp[ls[u]][0] + dp[rs[u]][0];
        dp[u][1] = min({
            dp[ls[u]][1] + dp[rs[u]][1],
            dp[ls[u]][1] + dp[rs[u]][0],
            dp[ls[u]][0] + dp[rs[u]][1]}
        );
    }else{
        dp[u][0] = min(
                dp[ls[u]][0] + dp[rs[u]][0]
            ,
                dp[ls[u]][1] + dp[rs[u]][1]
        );
        dp[u][1] = min(
                dp[ls[u]][1] + dp[rs[u]][0]
            ,
                dp[ls[u]][0] + dp[rs[u]][1]
        );
    }
    return;
}
signed main(){
    // freopen("logical.in", "r", stdin);
    // freopen("logical.out", "w", stdout);
    n = read(), m = read();
    for(int i = 1; i <= n; i++){
        ls[i] = read(), rs[i] = read();
        int fa = read();
        if(!fa) root = i;
        type[i] = read();
    }
    for(int i = 1; i <= m; i++){
        int u = read();
        chu[u] = read(), haschu[u] = true;
    }
    dfs1(root);
    // for(int i = 1; i <= n; i++) cout << chu[i] << ' ';
    dfs2(root);
    cout << dp[root][!chu[root]] << '\n';
    return 0;
}

T3 HYH的查找树

算法:图论、dfs 序。

描述

我们假定 HYH 查找树是(以下简称 HST,HYH's Search Tree)一颗最多有 \(n\) 个节点的有根树。

HYH 希望你能为 HST 设计一个程序,能够快速地得到某两个给定节点的关系。具体地说,希望你能返回某个节点是否为另外一个节点的祖先。

HST的描述方式比较奇怪:节点按层次从 \(1\) 编号到 \(n\),编号为 \(1\) 的节点总是 HST 的根。每个节点的儿子个数以一个函数 \(s(x) = ( s5 \times x ^ 4 + s4 \times x ^ 3 + s3 \times x ^ 2 + s2 \times x + s1 ) \bmod p\) 给出(后面若干个节点可选择的节点个数(即编号已经大于 \(n\))不足 \(s(x)\)个,则它的儿子数为剩下的节点个数)。

举例来说,一颗 \(5\) 个节点的 HST 的 \(s[1 \sim 5]=(1,2,3,4,5)\),则 HST 的形态如图。

如果看到这行字,说明图片炸了,请联系作者,感谢!

特别地,如果某时无法继续扩展,则剩下的节点作废,比如说 \(1\) 号节点有 \(0\) 个儿子,那么整棵树只有一个节点。

HYH 同学希望你回答 \(q\) 个询问,第 \(i\) 个询问以数对 \(( x , y ) = ( (( a3 \times i ^ 2 + a2 \times i + a1 ) \bmod n) + 1 , (( b3 \times i ^ 2 + b2 \times i + b1 ) \bmod n) + 1 )\) 的形式给出,表示询问编号为 \(x\) 的节点是否编号为 \(y\) 的节点的祖先。

输入

第一行,两个整数 \(n\)\(q\)\(1 \leq n \leq 10^6,1 \leq q \leq 10^5\))。

第二行,\(6\) 个整数,表示 \(s5、s4、s3、s2、s1\)\(p\)

第三行,\(6\) 个整数,表示 \(a3、a2、a1、b3、b2\)\(b1\)

输出

输出一个整数,表示一共有多少对询问 \((x,y)\) 使节点 \(x\) 是节点 \(y\) 的祖先。

样例

\(1\) 组,不包含大样例。

样例 \(1\) 输入

5 1
0 0 0 1 0 10000
0 0 1 0 0 4

样例 \(1\) 输出

1

题解

首先,我们可以根据题意暴力建树。

然后我们对树进行 dfs。这里有一个结论:假设 \(in_u\) 表示访问到 \(u\) 节点的时间,\(out_u\) 表示离开 \(u\) 的时间。那么当 \(in_u < in_v\)\(out_u > out_v\) 时,\(u\)\(v\) 的祖先。

于是 dfs 的时候维护时间戳就做完了。

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n, q, s[6], p;
inline int S(int x){
    return (((((s[5] % p * x % p * x % p * x % p * x % p +
                s[4] % p * x % p * x % p * x % p) % p +
                    s[3] % p * x % p * x % p) % p +
                        s[2] % p * x % p) % p) % p +
                            s[1] % p) % p;
}
int fa[1000005], in[1000005], out[1000005];
vector<int> e[1000005];
int tot = 0;
inline void dfs(int u){
    in[u] = ++tot;
    for(auto v : e[u]) dfs(v);
    out[u] = ++tot;
    return;
}
signed main(){
    // freopen("tree.in", "r", stdin);
    // freopen("tree.out", "w", stdout);
    cin >> n >> q;
    for(int i = 5; i >= 1; i--) cin >> s[i];
    cin >> p;
    // for(int i = 1; i <= n; i++) cout << S(i) << " ";
    // cout << endl;
    int cnt = 1; fa[1] = -1;
    for(int i = 1; i <= n && cnt <= n; i++){
        if(!fa[i]) break;
        int neww = min(S(i), n - cnt);
        for(int j = 1; j <= neww; j++) fa[++cnt] = i;
    }
    // cout << cnt << endl;return 0;
    int nn = n;
    n = cnt;
    // for(int i = 1; i <= n; i++) cout << fa[i] << " ";
    // cout << endl;
    for(int i = 2; i <= n; i++) e[fa[i]].push_back(i);
    dfs(1);
    // for(int i = 1; i <= n; i++) cout << in[i] << " ";
    // cout << endl;
    // for(int i = 1; i <= n; i++) cout << out[i] << " ";
    int a3, a2, a1, b3, b2, b1;
    cin >> a3 >> a2 >> a1 >> b3 >> b2 >> b1;
    int ans = 0;
    for(int i = 1; i <= q; i++){
        int mod = nn;
        int x = ((a3 % mod * i % mod * i % mod +
            a2 % mod * i % mod) % mod +
                a1 % mod
        ) % mod + 1;
        int y = ((b3 % mod * i % mod * i % mod +
            b2 % mod * i % mod) % mod +
                b1 % mod
        ) % mod + 1;
        // cout << x << " " << y << endl;
        if(1 <= x && x <= n && 1 <= y && y <= n){
            if(in[x] < in[y] && out[x] > out[y]){
            	// cout << x << " " << y << endl;
                ans++;
            }
        }
    }
    cout << ans << '\n';
    return 0;
}

T4 HYH的歌词

纯模拟。。。

随便放个题面和 \(90\) 分的代码,不修 \(\LaTeX\) 了。

描述
题目背景
HYH同学喜欢听歌,尤其喜欢看歌词……由于个人问题,他开始对LRC歌词进行研究……

题目描述
众所周知地,听音乐想看歌词可以安装《千千静听》或者Winamp+歌词显示插件等等软件实现。而现在的歌词,大多是以lrc为后缀名的歌词文件。
HYH同学最近在使用此类软件的时候,萌发了想制作一个同类软件的念头。可是由于他只有囧力没有编程力,所以只好靠你了。
附Lrc格式说明:

LRC 歌词是一种包含着“[:]”形式的“标签(tag)”的、基于纯文本的歌词专用格式。最早由郭祥祥先生(Djohan)提出并在其程序中得到应用。这种歌词文件既可以用来实现卡拉OK功能(需要专门程序),又能以普通的文字处理软件查看、编辑。当然,实际操作时通常是用专门的LRC歌词编辑软件进行高效编辑的。以下具体介绍LRC格式中的“标签”。

时间标签(Time-tag)
形式为"[mm:ss]"或"[mm:ss.fff]"(分钟数:秒数)。数字须为非负整数,比如"[12:34.5]"是有效的,而"[0x0C:-34.5]"无效。
它可以位于某行歌词中的任意位置。一行歌词可以包含多个时间标签(比如歌词中的迭句部分)。根据这些时间标签,用户端程序会按顺序依次高亮显示歌词,从而实现卡拉OK功能。

标识标签(ID-tags)
其格式为"[标识名:值]"。大小写等价。以下是预定义的标签。
[ar:艺人名]
[ti:曲名]
[al:专辑名]
[by:编者(指编辑LRC歌词的人)]
[offset:时间补偿值] 其单位是毫秒,正值表示整体提前,负值相反。这是用于总体调整显示快慢的。

以下列出了开发支持LRC格式的软件时应遵守的一些标准。
无论是否在行首,行内凡具有“[:]”形式的都应认为是标签。(注意:其中的冒号并非全角字符“:”)
凡是标签都不应显示。
凡是标签,且被冒号分隔的两部分都为非负数,则应认为是时间标签。因此,对于非标准形式(非“[mm:ss]”)的时间标签也应能识别(如“[0:0]”)。
凡是标签,且非时间标签的,应认为是标识标签。
标识名中大小写等价。
为了向后兼容,应对未定义的新标签作忽略处理。另应对标识标签后的同一行内容作忽略处理。
应允许一行中存在多个标签,并能正确处理。为简化本题,一行中不会同时存在标识标签和时间标签。
应能正确处理未排序的标签。
注意:为简化本题,数据保证正确,不存在未定义标签等等格式问题。并且保证时间没有相同的。

输入
第一行一个整数n,表示输入的行数。
接下来有n行,每行有一个字符串,整个输入文件是一个lrc文件。
字符串长度有可能超过255,请注意处理。
时间标签保证不超过100个。

输出
输出歌词显示时的结果(即按照时间标签排序好的歌词)

样例
输入
3
[ar:HYH]
[00:01][00:03]Oh Yeah~
[00:02]HYH is A BIGCOW!
输出
Oh Yeah~
HYH is A BIGCOW!
Oh Yeah~

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n;
string s, geci[60005];
vector<string> tag[60005];
double FENZHONG, MIAOSHU;
inline bool istimetag(string str){
    if(str.length() < 3) return false;
    if(str[0] != '[' || str[str.length()-1] != ']') return false;
    int mhpos = -1;
    for(int i = 0; i < str.size(); i++){
        if(str[i] == ':'){
            mhpos = i;
        }
    }
    if(mhpos == -1) return false;
    FENZHONG = 0; bool dian = false; double base = 0.1;
    for(int i = 1; i < mhpos; i++){
        if(str[i] == '.'){
            if(dian) return false;
            dian = true;
            continue;
        }
        if(dian){
            FENZHONG += base * (str[i] - '0');
            base /= 10;
            continue;
        }
        if(!isdigit(str[i])) return false;
        FENZHONG = FENZHONG * 10 + str[i] - '0';
    }
    MIAOSHU = 0; dian = false; base = 0.1;
    for(int i = mhpos + 1; i < str.length() - 1; i++){
        if(str[i] == '.'){
            if(dian) return false;
            dian = true;
            continue;
        }
        if(dian){
            MIAOSHU += base * (str[i] - '0');
            base /= 10;
            continue;
        }
        if(!isdigit(str[i])) return false;
        MIAOSHU = MIAOSHU * 10 + str[i] - '0';
    }
    return true;
}
signed main(){
    // freopen("lrc.in", "r", stdin);
    // freopen("lrc.out", "w", stdout);
    cin >> n; getline(cin, s);
    for(int i = 1; i <= n; i++){
        getline(cin, s);
        // cout << s << endl;
        int mxl = -1;
        for(int j = 0; j < s.length(); j++){
            if(s[j] == '['){
                for(int k = j + 1; k < s.length(); k++){
                    if(s[k] == ':'){
                        for(int l = k + 1; l < s.length(); l++){
                            if(s[l] == ']'){
                                // j = l;
                                string str = "";
                                for(int m = j; m <= l; m++) str += s[m];
                                tag[i].push_back(str);
                                mxl = max(mxl, l);
                                j = l;
                                goto nxt;
                            }
                        }
                    }
                }
            }
            nxt: continue;
        }
        string gc;
        for(int j = mxl + 1; j < s.length(); j++){
            gc += s[j];
        }
        geci[i] = gc;
    }
    // for(int i = 1; i <= n; i++){
    //     for(auto p : tag[i]){
    //         cout << p << endl;
    //     }
    //     cout << geci[i] << endl;
    //     cout << endl;
    // }
    vector<pair<double, string> > ans;
    for(int i = 1; i <= n; i++){
        for(auto p : tag[i]){
            if(istimetag(p)){
                // cout << p << " " << FENZHONG << " " << MIAOSHU << endl;
                ans.push_back({FENZHONG*60+MIAOSHU, geci[i]});
            }
        }
    }
    sort(ans.begin(), ans.end());
    for(auto p : ans) cout << p.second << '\n';
    return 0;
}
posted @ 2025-10-08 15:37  zhang_kevin  阅读(15)  评论(0)    收藏  举报