总之就是 | ZROI CSP连测 Day2

「0.0」序言

虽然这个题解发的比 NOIP Day2 的晚,但是实际考试时间是比那个早的。

虽然第一题挂了 \(30\) pts,但是总分还算可以。也是这几次模拟赛唯一一次涨 rating。

题目还是 根 据 实 际 情 况 写个题目。

缺省源可以去这篇博客拿,当然底下有些用了什么 unordered_mapbitset 之类的头文件自己加上就好了w

「1.0」签到挂分好耶

一个合法的 IP 地址是由四个不含前导 \(0\) 且不大于 \(255\) 的四个数字和三个 . 组成的。

现在给出一个字符串,判断它是不是合法的地址,并将修改后的合法地址输出。(保证字符串中存在四个分开的自然数)

「1.1」思路简述

众所周知我们有快读,于是直接对快读改一改即可,但是快读最后实际上会多读一个字符,所以还要判断一下最后那个多读的字符。

「1.2」Code

bool flg;int cntdot;

template<typename J>
I void fr(J &x)
{
    short f(1);x=0;char c(getchar());
    
    while(c<'0' or c>'9')
    {
        if(x!=0)
        {
            flg=1;
            break;
        }

        c=getchar();

        if(c=='.') ++cntdot;
        else if(c!='.' and c>='0' and c<='9') flg=1;
    }
   
    while (c>='0' and c<='9') 
    {
        if(x==0 and c=='0') flg=1;

        x=(x<<3)+(x<<1)+(c^=48);

        if(x>=255) {x=255;flg=1;}
       
        c=getchar();

        if(c=='.') ++cntdot;
        else if(c!='.' and c>='0' and c<='9') flg=1;
    }

    x*=f;
}

template<typename J>
I void fw(J x,bool k)
{
    if(x<0) x=-x,putchar('-');
   
    static short stak[35];short top(0);
   
    do
    {
        stak[top++]=x%10;
        x/=10;
    }
    while(x);
   
    while(top) putchar(stak[--top]+'0');
   
    k?puts(""):putchar('.');
}

I void Files()
{
    freopen("RNMTQ.in","r",stdin);
    freopen("RNMTQ.out","w",stdout);    
}

S main()
{
    LL x,y,j,k;
    fr(x),fr(y),fr(j),fr(k);

    if(cntdot>=3) flg=1;

    if(flg) puts("NO");
    else puts("YES");

    fw(x,0),fw(y,0),fw(j,0),fw(k,1);
    
    Heriko Deltana;
}

「2.0」贪心正解好耶

给出一个长度为 \(n\) 且只有 \(A\)\(P\) 的字符串。每次操作你能将所有形如 \(AP\)\(PP\) 的组合删掉,问若干次操作之后字符串最短是多少。

「2.1」思路简述

这个题我最一开始想的就是贪心,而且感觉贪心很对。但是隔壁都在考虑区间 DP,于是我就非常不解,于是我就看着他们自己构造数据把自己写的 DP 卡掉了但是卡不掉贪心。

然后大家就都交的贪心,但是贪心确实是正解w

发现整个字符串中只有 \(P\) 能造成操作,所以我们在读入的时候判断一下 \(P\)。又因为字符串中的 \(A\) 只能又 \(AP\) 这个组合删去,所以我们贪心的先删掉 \(AP\)

「2.2」Code

CI MXX=10005;

char c;

int n,cnta,cntp;

S main()
{
    ON;

    while(cin>>c)
    {
        if(c=='A') ++cnta;
        else if(c=='P')
        {
            if(cnta) --cnta;
            else ++cntp;
        }
    }

    cout<<cnta+(cntp&1);

    Heriko Deltana; 
}

「3.0」祖孙限定好耶

现在有 \(n\) 条指令,且每个语句都要声明一个类,这里类的定义和 C++ 中的类相似。

每个类的声明语句格式为:\(\tt ClassName : A_1,A_2,A_3,A_4,\cdots,A_k ;\)

这个语句的意思是 \(\tt{ClassName}\) 继承\(\tt A_1,A_2,\cdots,A_k\)。当然,一个类也可以不继承自其它类。

如果 \(\tt C_1\) 继承自 \(\tt C_2\)\(\tt C_3\) 继承自 \(\tt C_3\),……,\(\tt C_{k-1}\) 继承自 \(\tt C_k\),那么就称 \(\tt C_1,\cdots,C_{k-1}\)派生\(\tt C_k\)

在声明时不能出现以下的情况:

  1. 继承的类未被声明。

  2. 本次声明的类已经被声明过。

  3. 声明的类之间出现了菱形关系。

菱形关系如下图,其中:

  • \(X\)\(Y\) 派生自 \(A\)

  • \(B\) 派生自 \(X\)\(Y\)

  • \(X\)\(Y\) 之间没有派生关系。

若出现以上情况,本次声明的类会被抛弃。

现在要求判断每条语句是否合法。

「3.1」思路简述

显然不合法的前两种情况用一个 map 就能解决掉。

现在来考虑第三种。实际上菱形关系就是指从多个近亲派生,即禁止近亲〇,除非是祖孙关系(也就是说禁止从多个旁系派生)。

如果当前语句合法,那么它所继承的类的旁系亲属都是它的旁系亲属,于是就可以用 bitset 去维护。(bool 数组维护的时间复杂度是 \(O(n^3)\),而 bitset\(O(\dfrac{n^3}{w})\),而且我们正好需要位运算。)

「3.2」Code

CI MXX(1005);

unordered_map<string,int> mp;

bitset<1005> f[MXX],b[MXX];

int n,cnt,tot,q[MXX];

S main()
{
    ON;cin>>n;

    while(n--)
    {
        bool wa(0),flg(0);tot=0;string s,c;
        cin>>s>>c>>c;

        while(c!=";")
        {
            if(!mp[c]) wa|=1;

            q[++tot]=mp[c];cin>>c;
        }

        bitset<1005> temp;

        for(int i(1);i<=tot;++i) temp[q[i]]=1;

        for(int i(1);i<=tot;++i)
            if((temp&b[q[i]]).any()) 
                flg=1;

        if(flg) wa|=1;

        if(wa or mp[s]) {cout<<"greska\n";continue;}
        
        cout<<"ok\n";mp[s]=++cnt;f[cnt][cnt]=1;

        for(int i(1);i<=tot;++i) f[cnt]|=f[q[i]];

        for(int i(1);i<=cnt;++i)
            if(!f[cnt][i] and (f[i]&f[cnt]).any())
                b[cnt][i]=1;
    }
    Heriko Deltana;
}

「4.0」读不懂题好耶

本题 Code 我是真的不想写了,我承受不住。题面基本和原题面一致(因为实在不会简化了w

某人的研究兴趣是在图 \(G=(V(G),E(G))\) 中找到最好的 k-degree 子图。

子图 \(S\)\(G\) 的 k-degree 子图需要满足以下要求:

每个顶点 \(v(v \in S)\)\(S\) 中至少有 \(k\) 度;

  • \(S\) 是连通的 ;

  • \(S\) 是极大的,即 \(S\) 的任何超图都不是 k-degree 子图,除了 \(S\) 本身。

如下图:

然后那人就定义子图 \(S\) 的分数。在定义分数之前,他首先定义:

  • \(n(S)\):子图 S 中的顶点数,即 \(n(S)=|V(S)|\)

  • \(m(S)\):子图 S 的边数,即 \(m(S)=|E(S)|\)

  • \(b(S)\):子图 S 中的边界边数,\(b(S)=|(u,v)|(u,v)∈E(G),u∈V(S),v∉V(S),v∈V(G)|\);

他定义一个子图的分数为 \(score(S)=M⋅m(S)−N⋅n(S)+B⋅b(S)\),其中 \(M,N,B\) 是给定的常数。

子图的分数越高,认为它越好。你需要在图 \(G\) 中找到最好的 k-degree 子图。如果有许多 k-degree 子图的分数相同,则应最大化 \(k\)。输出最大的分数及对应的 \(k\).

「4.1」思路简述

这题目真是给我整异或了,我在场上是真的看不懂,于是暴力也没打。

为了下面描述方便,这里先做一个定义;

  • \(G(k)\):代表图 \(G\) 的 k=degree 子图。

不考虑联通性的情况下,\(G(k+1)\) 显然是 \(G(k)\) 的子图,那么我们给不在 \(G(k+1)\) 中,但是在 \(G(k)\) 中的点 \(X\) 求一个标记。

显然这个过程不断迭代就能求得 \(G(k)\) 的值。

下面再定义三个集合:

  • \(E(u,>)\):代表 \(u\) 的边中另一个节点的标记比 \(u\) 大的集合。

  • \(E(u,=)\):代表 \(u\) 的边中另一个节点的标记和 \(u\) 相等但是节点编号大于 \(u\) 的集合。

  • \(E(u,<)\):代表 \(u\) 的边中另一个节点的标记比 \(u\) 小的集合。

那么新加入的点 \(new\)\(n,m,b\) 的贡献就是:

  • \(\Delta n = 1\)

  • \(\Delta m = |E(u,>)| + |E(u,=)|\)

  • \(\Delta b = |E(u,<)| - |E(u,>)|\)

合并过程用并查集去维护,每个连通块的代表元素应当是编号最小的那一个,以及对于当前连通块的 \(X\) 点。

这样的话总体的复杂度(并查集使用路径压缩)是 \(O(m \log n + n)\),如果再加上按秩合并,总体复杂度即为 \(O(m+n)\)

「5.0」尾声

希望下次不挂分。

posted @ 2021-09-08 15:32  HerikoDeltana  阅读(66)  评论(1编辑  收藏  举报