【原文链接】

A.智乃与瞩目狸猫、幸运水母、月宫龙虾

1签

题目大意

题目分享了一个冷知识: Ubuntu 发行版的每个代号都包含一个形容词和一个动物。例如:瞩目狸猫(Focal Fossa)、幸运水母(Jammy Jellyfish)、月宫龙虾(Lunar Lobster),每个代号的两个单词首字母相同。 现在给定两个字符串,问首字母是否相同(忽略大小写)。

解题思路

直接比较首字母即可。

参考程序

void solve()
{
    string s1,s2;cin >> s1 >> s2;
    if(s1[0]==s2[0]||s1[0]-32==s2[0]||s1[0]+32==s2[0]) cout << YES;
    else cout << NO;
}

B.智乃的数字手串

博弈、猜结论

题目大意

有一串首尾相连的数字,两个玩家轮流操作。 当且仅当相邻个数字之和为偶数时,可以消除其中一个, 然后可以交换剩下的数字中任意两个数字的位置(也可以不交换)。 特别的,如果只有个数字,可以直接消除。 最先无法操作的玩家输。 问对于给定的数字串,qcjj(先手)和zn(后手)谁会赢。

解题思路

考虑游戏结束时的状态:手串上的数字排布为奇偶奇偶...奇偶,特点是数量为偶数。 数量为奇数时,一定存在两个相邻的数,奇偶校验相同(和为偶数)。

判断原始数字串长度的奇偶性即可。

参考程序

void solve()
{
    ll n;cin >> n;
    create_vec(v,n);
    if(n&1) cout << "qcjj" << endl;
    else cout << "zn" << endl;
}

D.chino's bubble sort and maximum subarray sum(easy version)

最大子段和

题目大意

给定一个长度为的数组,求恰好执行“交换任意相邻元素”操作次后,数组的最大非空子段和。

解题思路

由于easy version的范围极小(),暴力枚举所有情况求最大非空子段和即可。

对于最大非空字段和的求法,可以用贪心方法: 记为:包含当前位置元素的最大子段和。 从开始遍历数组,记当前元素为,则当前最大子段和有种情况:

  1. 将当前位置的元素加入当前最大子段和,值为
  2. 以当前位置的元素为起点,重新开始计算最大子段和,值为

每个位置的取这种情况的较大值,每个位置的最大值即为最大非空子段和。

参考程序

void solve()
{
    ll n,k;cin >> n >> k;
    create_vec(v,n);
    ll ans=-INF,cur;
    if(k==1){
        vector<ll> tv;
        FORLL(i,0,n-2){
            tv=v;
            swap(tv[i],tv[i+1]);
            ll tans=tv[0];
            cur=tv[0];
            FORLL(j,1,n-1){
                cur=max(tv[j],cur+tv[j]);
                tans=max(tans,cur);
            }ans=max(ans,tans);
        }
    }else{//求原数组的最大子段和
        ans=v[0];
        cur=v[0];
        FORLL(i,1,n-1){
            cur=max(v[i],cur+v[i]);
            ans=max(ans,cur);
        }
    }
    cout << ans << endl;
}

GH.智乃的比较函数(easy+normal)

偏序

题目大意

给定个数字的对关系x y z表示表示。 问这对关系是否存在逻辑矛盾。

解题思路

由于只有个数字,可以直接暴力赋值,验证是否有情况可以满足所有关系。 时间复杂度:

赛时本人直接对给定的关系集进行判断,会很麻烦,需要注意很多细节; 还有大佬用Floyd算法求偏序关系//%%%

参考程序

vector<array<ll,3>> cmp;
int pending(array<ll,4> a){
    for(auto [x,y,o]:cmp){
        if(o==1) { if(a[x]>=a[y]) return 0; }
        else { if(a[x]<a[y]) return 0; }
    }return 1;
}//判断是否满足所有关系
void solve()
{
    ll n,x,y,o;
    cin >> n;
    cmp.clear();
    FORLL(i,1,n){
        cin >> x >> y >> o;
        cmp.pb({x,y,o});
    }
    int ans=0;
    FORLL(i,1,3) FORLL(j,1,3) FORLL(k,1,3) ans|=pending({0,i,j,k});
    //存在满足所有关系的情况即可
    cout << (ans?YES:NO);
}

J.智乃的相亲活动

建图、概率论

题目大意

名男嘉宾和名女嘉宾,异性之间存在对双向的好感关系。 每位男嘉宾和女嘉宾都会从ta喜欢的异性中均匀随机的选择一个, 被至少选中一次的嘉宾称为“心动嘉宾”。 求本次相亲活动中,心动男嘉宾和女嘉宾的期望数量。

解题思路

建图:对每对好感关系,建立一条从男嘉宾到女嘉宾的无向边。 对于任一嘉宾: ta喜欢的每位嘉宾被ta选中的概率为; ta成为心动嘉宾的概率为,其中为ta喜欢的嘉宾集合(因为好感关系为双向)。

分别求出每位嘉宾成为心动嘉宾的概率,再对男女嘉宾分别求和即可。

参考程序

void solve()
{
    ll n,m,k;cin >> n >> m >> k;
    vector<vector<ll>> G(n+m+1);
    vector<ll> deg(n+m+1);
    ll u,v;
    FORLL(i,1,k)
    {
        cin >> u >> v;  
        G[u].pb(n+v);
        G[n+v].pb(u);
        deg[u]++;
        deg[n+v]++;
    }
    ll ansn,ansm,t;
    ansn=ansm=0;
    FORLL(i,1,n){
        t=1;
        for(auto &x:G[i]){
            multo(t,mdiv(deg[x]-1,deg[x]));
        }
        addto(ansn,sub(1,t));
    }
    FORLL(i,n+1,n+m){
        t=1;
        for(auto &x:G[i]){
            multo(t,mdiv(deg[x]-1,deg[x]));
        }
        addto(ansm,sub(1,t));
    }
    cout << "modint" << endl;
    cout << ansn << ' ' << ansm << endl;
}

K.智乃的“黑红树”

数据结构

题目大意

定义“黑红树”满足以下条件:

  1. 一棵以黑色节点为根的二叉树
  2. 每个节点只能有个子节点。
  3. 黑色节点的子节点只能是红色;红色节点的子节点只能是黑色。

给定黑红节点的数量,构造一棵黑红树。

解题思路

根据定义,黑色节点的个数必须为奇数,红色节点的个数必须为偶数,因此必须为奇数,必须为偶数。 根据定义,节点颜色将由节点的深度决定。

在满二叉树的情况下:

  1. 如果最后一层是黑色节点,则节点数满足
  2. 如果最后一层是红色节点,则节点数满足

利用这两个关系,可以再排除不合法的情况,剩余的情况都可以构造。

本人选用的构造方法为:按照层序遍历顺序铺设节点,直到节点数达到要求。

参考程序

void solve()
{
    ll a,b;cin >> a >> b;
    ll ub=a+b;
    if((a%2==0)||b%2){
        cout << "No" << endl;
        return ;
    }//奇偶性不符合条件
    if(b>2*a||a-1>2*b){
        cout << "No" << endl;
        return ;
    }//节点数超出临界值
    cout << "Yes" << endl;

    ll i=2,st=1,ed=1;a--;
    //[st,ed]为上一层的节点范围,i为当前节点编号
    vector<pll> son(ub+5,pll(-1,-1));
    while(a||b){
        FORLL(j,st,ed){//铺设红色节点
            if(b==0) break;
            son[j]=pll(i,i+1);
            i+=2;b-=2;
        }
        st=ed+1;ed=i-1;//更新本层的节点范围
        FORLL(j,st,ed){//铺设黑色节点
            if(a==0) break;
            if(son[j].first==-1){
                son[j]=pll(i,i+1);
                i+=2;a-=2;
            }
        }
        st=ed+1;ed=i-1;
    }
    FORLL(i,1,ub) cout << son[i].first << ' ' << son[i].second << endl;
}

LM.智乃的36倍数(easy+normal)

模数

题目大意

给定范围内的非负整数。 求在其中取不同的个元素,拼接起来后能被整除的方案数。

解题思路

(由于Latex版本不同,一直调整都没调好qwq///不能显示的部分请点击最上方原文链接查看)

假设拼接,拼接后的数记为,其中的位数。 可以发现:

因此,预处理的值,记录每个数对的余数及其位数。 计算时,枚举每个数作为右半部分,此时也被确定为的位数, 只需枚举左半部分的余数,对满足条件的部分求和即可。

参考程序

vector<ll> DM;
void pre(){//预处理10^k%36
    ll t=1;
    DM.pb(1);
    FORLL(i,1,19){
        t*=10;
        DM.pb(t%36);
    }
}
int dig(ll x){//求位数
    int ret=0;
    while(x){ x/=10; ret++; }
    return ret;
}
void solve()
{
    pre();
    ll n;cin >> n;
    ll t;pll tp;
    map<ll,int> mp;
    vector<pll> v;
    FORLL(i,1,n){
        cin >> t;
        tp=make_pair(t%36,dig(t));
        v.pb(tp);
        mp[t%36]++;
    }
    ll ans=0;
    for(auto &x:v){
        t=x.second;
        FORLL(i,0,35){
            if((i*DM[t]+x.first)%36==0){
                ans+=mp[i];
                if(i==x.first) ans--;
            }
        }
    }cout << ans << endl;
}