2021 四川省赛vp 题解

传送门

省流:没有 C F G

2021年四川省赛 vp

官方的信息:https://sua.ac/problems.html ,有题解、榜单、题面

五一集训第二天的模拟赛,这套题打起来真的挺爽,全程划水的时间比较少,基本都在想题,最后卡题的时候也是充满希望地在想题,不会说一点都想不出来那种

不过这场比赛打下来,我觉得我真的要好好地督促队友来机房训练了!

A. Chuanpai#

直接暴力就行,a 和 b 都很小,求 a+b=k 的方案数

#include <iostream>
using namespace std;

int main()
{
    int t;
    cin >> t;
    while(t--)
    {
        int n;
        cin >> n;
        int ans = 0;
        for(int i=1; i<=6; i++)
            if(n - i >= i && n - i >= 1 && n - i <= 6)
                ans++;
        cout << ans << endl;
    }
    return 0;
}

B. Hotpot#

这题一开始分喜欢一个菜的人数有奇数和偶数的情况讨论,发现如果是偶数的话,一轮之后会直接回到原地,如果是奇数的话两轮之后会直接回到原地

所以考虑直接模拟跑两轮作为一个周期,然后答案乘上周期数,剩下那一点余数直接暴力跑一边就行

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <algorithm>
#include <vector>
#include <queue>
#include <ctime>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 7;
const int inf = 2147483647;
int num[maxn], alp[maxn], ans[maxn];
int n, k, m;

void solve(int x)
{
    for(int i=0; i<=k; i++) alp[i] = 0;
    int tp = 0;
    while(x--)
    {
        if(alp[num[tp]])
        {
            alp[num[tp]] = 0;
            ans[tp]++;
        }
        else 
            alp[num[tp]]++;
        tp++;
        if(tp == n) tp = 0;
    }
}

signed main()
{
    int t;
    scanf("%d", &t);
    while(t--)
    {
        
        scanf("%d %d %d", &n, &k, &m);
        for(int i=0; i<n; i++) scanf("%d", &num[i]);
        for(int i=0; i<n; i++) ans[i] = 0;
        int x = min(n << 1, m);
        solve(x);
        x = m / (2 * n);
        x--;
        for(int i=0; i<n && x>=0; i++)
            ans[i] += ans[i] * x;
        if(m > n * 2)
            solve(m % (n * 2));
        for(int i=0; i<n; i++)
        {
            if(i) printf(" ");
            printf("%d", ans[i]);
        }
        printf("\n");
    }
    return 0;
}

C. Triangle Pendant

这题队友读完题都乐了,什么模拟物理引擎,反正我是没做

D. Rock Paper Scissors#

石头剪刀布,模拟题

因为后手可以看先手是什么牌,然后再出手,因此先手决策是不重要的

以后手最优,直接模拟就行

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <algorithm>
#include <vector>
#include <queue>
#include <ctime>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 7;
const int inf = 2147483647;
const double PI = acos(-1.0);
int num[maxn];
int a[3], b[3];

long long query(int x)
{
    long long ans = 0;
    int xa = (x + 1) % 3;
    int temp = min(a[x], b[xa]);
    a[x] -= temp;
    b[xa] -= temp;
    ans += temp;
    
    temp = min(a[x], b[x]);
    a[x] -= temp;
    b[x] -= temp;

    xa = (x + 2) % 3;
    temp = min(a[x], b[xa]);
    a[x] -= temp;
    b[xa] -= temp;
    return ans - temp;
}

signed main()
{
    int t;
    scanf("%d", &t);
    while(t--)
    {
        for(int i=0; i<3; i++) scanf("%d", &a[i]);
        for(int i=0; i<3; i++) scanf("%d", &b[i]);
        long long ans = 0;
        for(int i=0; i<3; i++)
            ans += query(i);
        printf("%lld\n", ans);
    }
    return 0;
}

E. Don't Really Like How The Story Ends#

在给出的一个图中加入最少的边,使得其 dfs 序是从 1 到 n

这题赛中队友说思路的时候一度没听懂,最后队友敲了出来,赛后才补题

直接 dfs 往下搜就行,要保证一下的决策:

  • 如果当前结点连接着下一个想要去的结点,则直接访问

  • 如果当前结点仍有连接着的结点未访问,而且并不与下一个想要去的结点相邻,则必须要添加一条边

  • 如果当前结点的相邻结点已经被访问,且不与下一个想要去的结点相邻,则返回上一个节点

如果每次去遍历那些点没有访问过,会非常消耗时间

直接存边的时候,仅让编号小的点指向编号大的点,然后对每个点发出的边,按指向的点编号从小到大排序,拿一个指针(标记)去指向下一个应该要访问的点即可

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <vector>
using namespace std;
const int maxn = 1e5 + 7;
vector<int>gra[maxn];
int pre[maxn], tp = 1, ans = 0;
int s[maxn];

void dfs(int now)
{
    if(now == tp)
        tp++;
    while(s[now] < gra[now].size() && gra[now][s[now]] < tp) s[now]++;
    while(s[now] < gra[now].size())
    {
        int nex = gra[now][s[now]];
        if(nex != tp) ans++;
        dfs(tp);
        while(s[now] < gra[now].size() && gra[now][s[now]] < tp) s[now]++;
    }
}

int main()
{
    int t;
    scanf("%d", &t);
    while(t--)
    {
        int n, m;
        scanf("%d%d", &n, &m);
        for(int i=0; i<m; i++)
        {
            int a, b;
            scanf("%d%d", &a, &b);
            int c = a + b;
            a = min(a, b);
            b = c - a;
            if(a == b) continue;
            gra[a].push_back(b);
        }
        for(int i=1; i<=n; i++) sort(gra[i].begin(), gra[i].end());
        ans = 0;
        tp = 1;
        while(tp <= n)
        {
            dfs(tp);
            ans++;
        }
        ans--;
        printf("%d\n", ans);
        for(int i=1; i<=n; i++)
        {
            gra[i].clear();
            s[i] = 0;
        }
    }
    return 0;
}

F. Direction Setting

是一个网络流的问题,我还没学完,咱队也没找到网络流的思路

这学期算法课有网络流,就趁机把它学懂

G. Hourly Coding Problem

听说是个二分

H. Nihongo wa Muzukashii Desu#

模拟题

队友写的,他说没有补题的必要,下面给出队友的代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <algorithm>
#include <vector>
#include <queue>
#include <ctime>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 7;
const int inf = 2147483647;


signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    int t;
    cin >> t;
    while(t--)
    {
        string s;
        cin >> s;
        string ans;
        if(s == "ikimasu") ans = "itte";
        else
        {
            string cur = s.substr(s.length() - 6);
            if(cur == "mimasu" || cur == "bimasu" || cur == "nimasu")
            {
                ans = s.substr(0, s.length() - 6) + "nde";
            }
            else if(cur == "kimasu")
            {
                ans = s.substr(0, s.length() - 6) + "ite";
            }
            else if(cur == "gimasu")
            {
                ans = s.substr(0, s.length() - 6) + "ide";
            }
            else if(cur == "rimasu")
            {
                ans = s.substr(0, s.length() - 6) + "tte";
            }
            else if(s.length() > 7)
            {
                cur = s.substr(s.length() - 7);
                if(cur == "chimasu")
                {
                    ans = s.substr(0, s.length() - 7) + "tte";
                }
                else if(cur == "shimasu")
                {
                    ans = s.substr(0, s.length() - 7) + "shite";
                }
                else
                {
                    ans = s.substr(0, s.length() - 5) + "tte";
                }
            }
            else
            {
                ans = s.substr(0, s.length() - 5) + "tte";
            }
        }
        cout << ans << endl;
    }
    return 0;
}

I. Monster Hunter#

二分答案,然后用模拟贪心的方法去判断是否可行

具体的贪心方案可以看看官方题解

#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const ll maxn = 1e5 + 10;
int n, m;
ll a[maxn], b[maxn];
ll num[10], c[maxn];

bool judge(ll x)
{
    ll t = x / n;
    for(int i=0; i<=3; i++) num[i] = 0;
    for(int i=0; i<n; i++) num[a[i]]++;
    for(int i=1; i<=3; i++) num[i] *= t;
    t = x % n;
    for(int i=0; i<t; i++) num[a[i]]++;
    for(int i=0; i<m; i++) c[i] = b[i];
    for(int i=0; i<m && num[3]; i++)
    {
        if(c[i] < 3) continue;
        if(c[i] % 2 == 1) c[i] -= 3, num[3]--;
    }
    for(int i=0; i<m && num[3]; i++)
    {
        t = min(c[i] / 6, num[3] / 2);
        c[i] -= t * 6ll;
        num[3] -= t * 2ll;
    }
    ll maxx = 0, way = 0;
    for(int i=0; i<m; i++) if(maxx < c[i]) {maxx = c[i]; way = i;}
    if(maxx > 4 && num[3]) {c[way] -= 3; num[3]--;}
    for(int i=0; i<m && num[3]; i++) if(c[i] == 4) c[i] -= 3, num[3]--;
    for(int i=0; i<m && num[3]; i++) if(c[i] == 2) c[i] = 0, num[3]--;
    for(int i=0; i<m && num[3]; i++) if(c[i] == 1) c[i] = 0, num[3]--;
    for(int i=0; i<m && num[2]; i++)
    {
        t = min(c[i] / 2, num[2]);
        c[i] -= t * 2ll;
        num[2] -= t;
    }
    for(int i=0; i<m && num[1]; i++)
    {
        t = min(c[i], num[1]);
        c[i] -= t;
        num[1] -= t;
    }
    for(int i=0; i<m && num[2]; i++)
    {
        t = min((c[i] + 1) / 2, num[2]);
        num[2] -= t;
        c[i] -= t * 2ll;
    }
    for(int i=0; i<m; i++) if(c[i] > 0) return false;
    return true;
}

int main()
{
    int t;
    scanf("%d", &t);
    while(t--)
    {
        ll l = 0, r = 0;
        scanf("%d", &n);
        for(int i=0; i<n; i++) scanf("%lld", &a[i]);
        scanf("%d", &m);
        for(int i=0; i<m; i++)
        {
            scanf("%lld", &b[i]);
            r += b[i];
        }
        while(l < r)
        {
            ll mid = l + r >> 1;
            if(judge(mid))
                r = mid;
            else
                l = mid + 1;
        }
        printf("%lld\n", l);
    }
    return 0;
}

J. Ants#

一个思维题 + 一点模拟

我们需要明确的是,蚂蚁之间的碰撞是不重要的,我们在乎的只是蚂蚁与边界的碰撞,蚂蚁本身碰撞后,本该出现蚂蚁的位置仍有蚂蚁,只是哪一只蚂蚁不同了而已

所以对于夸张的数据范围,我们只需要考虑最后一点点的时间

对于一开始,n 只蚂蚁在走完 2L 的距离后,都会对两边碰撞一次,所以在 2L 的时间内,两边边界各减少 n 的耐久

到最后再来一次 2L 的时间,边界就要破碎的时候我们就可以自己进行模拟,用两个队列来对蚂蚁的行为进行模拟

按照往左边走和往右边走 来分队列,然后队列内也是按照碰到边界的先后来排列,队头的先碰边界,如果碰到边界会返回,则加入到另外一个队列中去,否则直接就出去,最后计算一个最大值就行了

写在最后,这个想法是我和队友一起讨论出来的,然后队友去敲了,敲了快1个小时都没有弄出来,然后我一看,很多逻辑都有问题,最后我把这题抢过来了

还好只是模拟赛,如果是正式赛,我想我都没办法放心把键盘交出去(五一的集训看下来,我好像每场比赛都是主代码手)

真的要好好好好督促队友刷题,感觉我队友已经很久没有训练了

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <algorithm>
#include <vector>
#include <queue>
#include <ctime>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 7;
const int inf = 2147483647;
const double PI = acos(-1.0);
ll arr[maxn], brr[maxn];
ll len = 1e9 + 1;
queue<ll> q2; // 右
queue<ll> q1; // 左

signed main()
{
    int n, a, b;
    scanf("%d %d %d", &n, &a, &b);
    for (int i = 1; i <= n; i++)
        scanf("%lld", &arr[i]);
    for (int i = 1; i <= n; i++)
        scanf("%lld", &brr[i]);
    ll pre = 0, tr = min(a, b) / n, ans = 0;
    pre = tr * len * 2;
    tr *= n;
    a -= tr, b -= tr;
    for(int i=1; i<=n; i++)
        if(brr[i] == 0)
            q1.push(arr[i]);
    for(int i=n; i>=1; i--)
        if(brr[i])
            q2.push(len - arr[i]);
    
    while(q1.size() || q2.size())
    {
        while(q1.size())
        {
            ll now = q1.front();
            q1.pop();
            if(a)
            {
                a--;
                q2.push(now + len);
            }
            else
                ans = max(ans, pre + now);
        }
        while(q2.size())
        {
            ll now = q2.front();
            q2.pop();
            if(b)
            {
                b--;
                q1.push(now + len);
            }
            else
                ans = max(ans, pre + now);
        }
    }

    printf("%lld\n", ans);
    return 0;

}

K. K-skip Permutation#

这题就直接贪心的,按模k同余分组,然后一个个放上去就行了

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <algorithm>
#include <vector>
#include <queue>
#include <ctime>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 7;
const int inf = 2147483647;
const double PI = acos(-1.0);
int num[maxn];

signed main()
{
    int n, k, tp = 0;
    cin >> n >> k;
    for(int i=1; i<=k; i++)
    {
        for(int j=i; j<=n; j+=k)
        {
            num[++tp] = j;
        }
    }
    for(int i=1; i<=n; i++)
    {
        if(i != 1) cout << " ";
        cout << num[i];
    }
    cout << endl;
    return 0;
}

L. Spicy Restaurant#

这题直接按照权值分类,跑 100 次bfs就行

将题目给出的终点作为起点,将所有其他所有点作为终点,多起点的bfs,复杂度还是没有变化,都是 O(n)

最后的答案就按题目的要求,在符合条件的权值下找到最小值就行了

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <algorithm>
#include <vector>
#include <queue>
#include <ctime>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 7;
const int inf = 2147483647;
const double PI = acos(-1.0);
#define pii pair<int, int>
vector<int>gra[maxn];
vector<int>val[110];
int ans[110][maxn];
int vis[maxn];

void bfs(int n)
{
    queue<pair<int, int>>q;
    for(int i=0; i<val[n].size(); i++)
    {
        vis[val[n][i]] = 1;
        q.push(make_pair(val[n][i], 0));
    }
    while(q.size())
    {
        pii x = q.front();
        q.pop();
        int now = x.first;
        ans[n][now] = x.second;
        for(int i=0; i<gra[now].size(); i++)
        {
            int nex = gra[now][i];
            if(vis[nex]) continue;
            vis[nex] = 1;
            q.push(make_pair(nex, x.second + 1));
        }
    }
}

signed main()
{
    int n, m, q, w = 0;
    scanf("%d%d%d", &n, &m, &q);
    for(int i=1; i<=n; i++)
    {
        int a;
        scanf("%d", &a);
        w = a > w ? a : w;
        val[a].push_back(i);
    }
    for(int i=0; i<m; i++)
    {
        int a, b;
        scanf("%d%d", &a, &b);
        gra[a].push_back(b);
        gra[b].push_back(a);
    }
    for(int i=0; i<=w; i++) for(int j=0; j<=n; j++) ans[i][j] = n;
    for(int i=1; i<=w; i++)
    {
        for(int j=0; j<=n; j++) vis[j] = 0;
        bfs(i);
    }

    while(q--)
    {
        int a, b;
        scanf("%d%d", &a, &b);
        int x = n;
        for(int i=1; i<=b; i++) x = ans[i][a] < x ? ans[i][a] : x;
        printf("%d\n", x == n ? -1 : x);
    }
    return 0;
}

M. True Story#

题目给出了很多对数据范围的限制,所以就变成了一个纯纯思维题

直接找延迟时间里面的最大时间,然后判断够不够乘客到机场就行

如果怕 double 类型会导致精度问题的话,记得开longlong

#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;
const int maxn = 1e5 + 7;
long long num[maxn];
int l[maxn], r[maxn];

int main()
{
    int n, m, s, p;
    scanf("%d%d%d%d", &n, &m, &s, &p);
    for(int i=0; i<n; i++) scanf("%lld", &num[i]);
    for(int i=0; i<m; i++) scanf("%d", &l[i]);
    for(int i=0; i<m; i++) scanf("%d", &r[i]);
    for(int i=0; i<m; i++) p = max(p, r[i] - l[i]);
    int ans = 0;
    for(int i=0; i<n; i++)
        if(num[i] * p >= s) ans++;
    printf("%d\n", ans);
    return 0;
}
posted @   dgsvygd  阅读(740)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示
主题色彩