CSP-S模拟赛五总结(实际难度远低于提高组)

T1

题面:

第一题一开始没看到是最短路径,然后硬控了我一小时(呜呜呜,下次一定仔细审题)。

因为是最短路径,所以上来肯定直接跑 Dijkstra。那这时有一种非常水的情况:如果从起点到终点的最短路径小于等于 K,那任意两个点都可以连边,直接输出 n ( n − 1 ) 2 \frac{n(n-1)}{2} 2n(n1)(但如果不判这种情况的话会 WA 一个点)。

另一种情况,就是最短路径比 K 大。这时我们就必须选两个点连起来,使得最短路径更短(当然,不一定是在最短路径上修改),我们假设从起点 s s s 走了 i i i 个单位长度,从终点 t t t 走了 j j j 个单位长度,那么要满足条件的话就要保证 i + j + L ≤ K i+j+L\le K i+j+LK,也就是 j ≤ K − L − i j\le K-L-i jKLi,那 j j j 能取的上限就是 K − L − i K-L-i KLi,因此在这个范围以内的我们都可以取,怎么算有多少个呢?很简单:排个序再二分一下不就对了。

代码:

#include<bits/stdc++.h>
#define int long long
#define code using
#define by namespace
#define plh std
#define INF 0x3f3f3f3f3f3f3f3f
code by plh;
int n,m,s,t,L,K,ans,dis1[200006],dis2[200006],vis[200006];
vector<pair<int,int>>v[200006];
void dijkstra(int st,int *dis)
{
   
   
    priority_queue<pair<int,int>,vector<pair<int,int>>,greater<pair<int,int>>>pq;
    for(int i=1;i<=n;i++)
    {
   
   
        dis[i]=INF;
        vis[i]=0;
    }
    dis[st]=0;
    pq.push(make_pair(dis[st],st));
    while(!pq.empty())
    {
   
   
        pair<int,int>p=pq.top();
        pq.pop();
        int u=p.second;
        if(vis[u])
        {
   
   
            continue;
        }
        vis[u]=1;
        for(auto i:v[u])
        {
   
   
            if(dis[i.first]>dis[u]+i.second)
            {
   
   
                dis[i.first]=dis[u]+i.second;
                pq.push(make_pair(dis[i.first],i.first));
            }
        }
    }
}
signed main()
{
   
   
    // freopen("portal.in","r",stdin);
    // freopen("portal.out","w",stdout);
    cin>>n>>m>>s>>t>>L>>K;
    for(int i=1,x,y,z;i<=m;i++)
    {
   
   
        cin>>x>>y>>z;
        v[x].push_back(make_pair(y,z));
        v[y].push_back(make_pair(x,z));
    }
    dijkstra(s,dis1);
    if(dis1[t]<=K)
    {
   
   
        cout<<n*(n-1)/2;
        return 0;
    }
    dijkstra(t,dis2);
    sort(dis1+1,dis1+1+n);
    sort(dis2+1,dis2+1+n);
    K-=L;
    for(int i=1;i<=n;i++)
    {
   
   
        if(K<dis1[i])
        {
   
   
            break;
        }
        int cnt=upper_bound(dis2+1,dis2+n+1,K-dis1[i])-dis2-1;
        ans+=cnt;
    }
    cout<<ans;
    return 0;
}

T2

题面:

这题让我想到了这道题,只不过比那道题似乎还要好写一点。

首先看到后缀表达式,明显用栈建树,接下来就是要考虑反转表达式的值了。

首先,有两种无可奈何的情况(即必须把值改了):

  1. 1 ∣ x = 1 1\mid x=1 1x=
# P8815 [CSP-J 2022] 逻辑表达式 ## 目描述 逻辑表达式是计算机科学中的重要概念和工具,包含逻辑值、逻辑运算、逻辑运算优先级等内容。 在一个逻辑表达式中,元素的值只有两种可能:$0$(表示假)和 $1$(表示真)。元素之间有多种可能的逻辑运算,本中只需考虑如下两种:“与”(符号为 `&`)和“或”(符号为 `|`)。其运算规则如下: $0 \mathbin{\&} 0 = 0 \mathbin{\&} 1 = 1 \mathbin{\&} 0 = 0$,$1 \mathbin{\&} 1 = 1$; $0 \mathbin{|} 0 = 0$,$0 \mathbin{|} 1 = 1 \mathbin{|} 0 = 1 \mathbin{|} 1 = 1$。 在一个逻辑表达式中还可能有括号。规定在运算时,括号内的部分先运算;两种运算并列时,`&` 运算优先于 `|` 运算;同种运算并列时,从左向右运算。 比如,表达式 `0|1&0` 的运算顺序等同于 `0|(1&0)`;表达式 `0&1&0|1` 的运算顺序等同于 `((0&1)&0)|1`。 此外,在 C++ 等语言的有些编译器中,对逻辑表达式的计算会采用一种“短路”的策略:在形如 `a&b` 的逻辑表达式中,会先计算 `a` 部分的值,如果 $a = 0$,那么整个逻辑表达式的值就一定为 $0$,故无需再计算 `b` 部分的值;同理,在形如 `a|b` 的逻辑表达式中,会先计算 `a` 部分的值,如果 $a = 1$,那么整个逻辑表达式的值就一定为 $1$,无需再计算 `b` 部分的值。 现在给你一个逻辑表达式,你需要计算出它的值,并且统计出在计算过程中,两种类型的“短路”各出现了多少次。需要注意的是,如果某处“短路”包含在更外层被“短路”的部分内则不被统计,如表达式 `1|(0&1)` 中,尽管 `0&1` 是一处“短路”,但由于外层的 `1|(0&1)` 本身就是一处“短路”,无需再计算 `0&1` 部分的值,因此不应当把这里的 `0&1` 计入一处“短路”。 ## 输入格式 输入共一行,一个非空字符串 $s$ 表示待计算的逻辑表达式。 ## 输出格式 输出共两行,第一行输出一个字符 `0` 或 `1`,表示这个逻辑表达式的值;第二行输出两个非负整数,分别表示计算上述逻辑表达式的过程中,形如 `a&b` 和 `a|b` 的“短路”各出现了多少次。 ## 输入输出样例 #1 ### 输入 #1 ``` 0&(1|0)|(1|1|1&0) ``` ### 输出 #1 ``` 1 1 2 ``` ## 输入输出样例 #2 ### 输入 #2 ``` (0|1&0|1|1|(1|1))&(0&1&(1|0)|0|1|0)&0 ``` ### 输出 #2 ``` 0 2 3 ``` ## 说明/提示 **【样例解释 \#1】** 该逻辑表达式的计算过程如下,每一行的注释表示上一行计算的过程: ```plain 0&(1|0)|(1|1|1&0) =(0&(1|0))|((1|1)|(1&0)) //用括号标明计算顺序 =0|((1|1)|(1&0)) //先计算最左侧的 &,是一次形如 a&b 的“短路” =0|(1|(1&0)) //再计算中间的 |,是一次形如 a|b 的“短路” =0|1 //再计算中间的 |,是一次形如 a|b 的“短路” =1 ``` **【样例 \#3】** 见附件中的 `expr/expr3.in` 与 `expr/expr3.ans`。 **【样例 \#4】** 见附件中的 `expr/expr4.in` 与 `expr/expr4.ans`。 **【数据范围】** 设 $\lvert s \rvert$ 为字符串 $s$ 的长度。 对于所有数据,$1 \le \lvert s \rvert \le {10}^6$。保证 $s$ 中仅含有字符 `0`、`1`、`&`、`|`、`(`、`)` 且是一个符合规范的逻辑表达式。保证输入字符串的开头、中间和结尾均无额外的空格。保证 $s$ 中没有重复的括号嵌套(即没有形如 `((a))` 形式的子串,其中 `a` 是符合规范的逻辑表达式)。 | 测试点编号 | $\lvert s \rvert \le$ | 特殊条件 | |:-:|:-:|:-:| | $1 \sim 2$ | $3$ | 无 | | $3 \sim 4$ | $5$ | 无 | | $5$ | $2000$ | 1 | | $6$ | $2000$ | 2 | | $7$ | $2000$ | 3 | | $8 \sim 10$ | $2000$ | 无 | | $11 \sim 12$ | ${10}^6$ | 1 | | $13 \sim 14$ | ${10}^6$ | 2 | | $15 \sim 17$ | ${10}^6$ | 3 | | $18 \sim 20$ | ${10}^6$ | 无 | 其中: 特殊性质 1 为:保证 $s$ 中没有字符 `&`。 特殊性质 2 为:保证 $s$ 中没有字符 `|`。 特殊性质 3 为:保证 $s$ 中没有字符 `(` 和 `)`。 **【提示】** 以下给出一个“符合规范的逻辑表达式”的形式化定义: - 字符串 `0` 和 `1` 是符合规范的; - 如果字符串 `s` 是符合规范的,且 `s` 不是形如 `(t)` 的字符串(其中 `t` 是符合规范的),那么字符串 `(s)` 也是符合规范的; - 如果字符串 `a` 和 `b` 均是符合规范的,那么字符串 `a&b`、`a|b` 均是符合规范的; - 所有符合规范的逻辑表达式均可由以上方法生成。 请给下列代码加上注释#include<bits/stdc++.h> #define int long long using namespace std; int And,Or,si; string s; int n; bool val; signed main(){ cin>>s; si=s.size(); for(int i=0;i<si;i++){ if(n){ char x=s[i]; if(x=='('){ int num=1; while(num){ i++; if(s[i]=='(') num++; if(s[i]==')') num--; } }else if(n==1&&x=='|'||x==')'){ n=0; }else if(n==1&&x=='&'){ And++; }else if(n==2&&x=='|'){ Or++; } }else{ char x=s[i]; if(x=='1') val=1; if(x=='0') val=0; if(x=='&'&&val==0){ n=1; And++; } if(x=='|'&&val==1){ n=2; Or++; } } } cout<<val<<"\n"<<And<<" "<<Or<<"\n"; return 0; }
10-11
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值