2024暑假集训测试9

前言

image

一点部分分没打感觉要被 D 了。

赛时题面和数据好像有出了点问题,不过我 T3 没做换数据不关我的事,但是 T1 还特殊考虑了 \(a_i<0\) 的情况,实则不用。

T2 理解错题了硬控 \(2\) 个多小时,发现之后改改就过了,但 T3、T4 没时间看了。

但是这次终于不挂分了。

T1 简单的序列

签到题,贪心倒退即可,若出现 \(a_i=0\) 了还不合法即无解。

虽然不存在但还是想说,若 \(a_i<0\) 正推即可,若 \(a_i=-1\) 了还不合法即无解,只可能前面一串负后面全是正,否则无解。

点击查看代码
#include<bits/stdc++.h>
#define ll long long 
#define endl '\n'
#define sort stable_sort
using namespace std;
const int N=2e4+10;
template<typename Tp> inline void read(Tp&x)
{
    x=0;register bool z=true;
    register char c=getchar();
    for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0;
    for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
    x=(z?x:~x+1);
}
template<typename Tp> inline void wt(Tp x)
{if(x>9)wt(x/10);putchar((x%10)+'0');}
template<typename Tp> inline void write(Tp x)
{if(x<0)putchar('-'),x=~x+1;wt(x);}
ll T,n,a[N];
signed main()
{
    read(T);
    while(T--)
    {
        read(n);
        ll len=0,ans=0;
        bool flag=0;
        for(int i=1;i<=n;i++) read(a[i]);
        for(int i=1;i<=n;i++)
        {
            if(a[i]<0) len++;
            else break;
        }
        for(int i=len+1;i<=n;i++)
            if(a[i]<0)
            {
                puts("-1");
                flag=1;
                break;
            }
        if(!flag)
        {
            for(int i=2;i<=len;i++)
            {
                while(a[i]<=a[i-1]&&a[i]!=-1&&a[i-1]!=-1) 
                {
                    a[i]=floor((double)a[i]/2.0);
                    ans++;
                }
                if(a[i]<=a[i-1])
                {
                    puts("-1");
                    flag=1;
                    break;
                }
            }
        }
        if(!flag)
        {
            for(int i=n-1;i>=len+1;i--)
            {
                while(a[i]>=a[i+1]&&a[i]!=0&&a[i+1]!=0)
                {
                    a[i]=floor((double)a[i]/2.0);
                    ans++;
                }
                if(a[i]>=a[i+1])
                {
                    puts("-1");
                    flag=1;
                    break;
                }
            }
        }
        if(!flag) write(ans),puts("");
    }
}

T2 简单的字符串

先把题面读明白,即每次从尾部删一个,同时 \(a_i\) 可向左传染,等价于在末尾加一个万能传染字符,只考虑传染。

发现对于字符 \(x\) 上次和本次出现的位置 \(l,r\),那么其需要的次数就是 \(r-l-1\),故此处理出其每个 \(l_i,r_i\),取最大的 \(r_i-l_i-1\) 即可。

特殊的,第一个 \(l=0\),最后一个 \(r=n+1\)

枚举字符即可,预处理可达到 \(O(n)\)

点击查看代码
#include<bits/stdc++.h>
#define ll long long 
#define endl '\n'
#define sort stable_sort
using namespace std;
const int N=1e4+10;
template<typename Tp> inline void read(Tp&x)
{
    x=0;register bool z=true;
    register char c=getchar();
    for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0;
    for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
    x=(z?x:~x+1);
}
template<typename Tp> inline void wt(Tp x)
{if(x>9)wt(x/10);putchar((x%10)+'0');}
template<typename Tp> inline void write(Tp x)
{if(x<0)putchar('-'),x=~x+1;wt(x);}
char s[N];
int n,ans=0x3f3f3f3f;
bool vis[N];
vector<int>pos,e[N];
signed main()
{
    cin>>(s+1);
    n=strlen(s+1);
    for(int i=1;i<=n;i++)
    {
        if(!vis[s[i]-'a'])
        {
            vis[s[i]-'a']=1;
            pos.push_back(s[i]-'a');
        }
        e[s[i]-'a'].push_back(i);
    }
    for(int x:pos)
    {
        int l=1,maxx=0,vr;
        for(int r:e[x])
        {
            maxx=max(maxx,r-l);
            l=r+1;
            vr=r;
        }
        maxx=max(maxx,n-vr);
        ans=min(ans,maxx);
    }
    write(ans);
}

T3 简单的博弈

机房绝大多数人没有学过 \(SG\) 函数,所以只好赛后粗略学习一下。

对于每个节点 \(x\) 存在子节点 \(y\),考虑以 \(y\) 为根的子树对其贡献,因为该子树上所有节点包括 \((x,y)\) 这条边都可能被删除,故该子树上所有节点都连向一个终止节点,结果就是 \(SG(y)+1\),于是有 \(SG(x)=⊕_{y\in son_x}(SG(y)+1)\)

最后若 \(SG(1)=0\) 则后手赢,反之先手赢。

点击查看代码
#include<bits/stdc++.h>
#define ll long long 
#define endl '\n'
#define sort stable_sort
using namespace std;
const int N=2e5+10;
template<typename Tp> inline void read(Tp&x)
{
    x=0;register bool z=true;
    register char c=getchar();
    for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0;
    for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
    x=(z?x:~x+1);
}
template<typename Tp> inline void wt(Tp x)
{if(x>9)wt(x/10);putchar((x%10)+'0');}
template<typename Tp> inline void write(Tp x)
{if(x<0)putchar('-'),x=~x+1;wt(x);}
ll T,n;
vector<ll>e[N];
ll dfs(int x,int fa)
{
    ll ans=0;
    for(int y:e[x]) if(y!=fa) ans^=(dfs(y,x)+1);
    return ans;
}
signed main()
{
    read(T);
    while(T--)
    {
        read(n);
        for(int i=1;i<=n;i++) e[i].clear();
        for(int i=1,x,y;i<=n-1;i++)
        {
            read(x),read(y);
            e[x].push_back(y);
            e[y].push_back(x);
        }
        puts(dfs(1,0)?"gtm1514":"joke3579");
    }
}

T4 困难的图论

没学过最短路 \(dag\),不想调了,好像还要手打 \(bitset\)

挂个官方题解吧

首先把图建出来。这题是最短路能看出来吧。暴力建图可以过第一个点。对每个标签建一个虚点,每个点进虚点的边权为 \(0\),出的边权为 \(1\),再跑可以过三个点。

我们考虑继续发掘一下这些个虚点能干些什么。对于 \(m\le 3000\) 的图,我们可以发现单靠这 \(m\) 条边基本是不连通的。那么我们就完全不需要管没被 \(m\) 条边覆盖的点,他们的答案可以直接通过虚点计算。那么只需要对虚点和 \(O(m)\) 个点建图跑最短路。

考虑将这个思路拓展一下。两个点之间最短路有两种情况,一种是过虚点,一种不过。以起点 \(p\) 为例,他要么过他所连虚点要么不过。如果过,这个点答案就是虚点的答案。如果不过,就要减 \(1\),相当于把第一步走虚点再走回来去掉。

那么什么时候减呢?可以从每个虚点开始跑最短路,建立最短路 dag。如果要减这个 \(1\),那么终点是起点的后继。可以用 bitset 从后往前扫一遍维护每个点前驱有哪些节点来统计前驱后继。

总结一下,我们每次计算一个虚点里边所有点的答案。建立最短路 dag 以后统计虚点内的点到其他点的最短路。枚举所有终点 \(x\),对于自己的所有前驱,答案是 \(dis_x-1\),否则是 \(dis_x\)。这样就可以方便地开桶计算个数了。

但是直接开 bitset 会浪费很多长度 t 掉,需要手写 bitset。就是用 ull 压,一次压 64 个点处理。

总结

好好读题,最好是手摸一遍样例来确定是否读对题。

posted @ 2024-07-23 10:12  卡布叻_周深  阅读(19)  评论(0编辑  收藏  举报