Codeforces Round #964

A Splits(思维+数学)

传送门:A


Description

我们定义正整数 \(n\) 的分裂为一个由正整数组成的不上升序列,且序列数字和为 \(n\)

举个栗子:下列这些序列都是 \(8\) 的分裂:\([4,4],[3,3,2],[2,2,1,1,1],[5,2,1]\)

下列这些序列不是 \(8\) 的分裂:\([1,7],[5,4],[11,-3],[1,1,4,1,1]\)

一个分裂的权是序列第一个数出现的次数,举个例子:\([1,1,1,1,1]\) 的权是 \(5\)\([5,5,3,3,3]\) 的权是 \(2\)\([9]\) 的权是 \(1\)

现在给出 \(n\),求 \(n\) 的分裂有多少个不同的权

\(n\leq 10^9\)


Solution

分情况讨论:

  1. 如果权重等于 \(n\),那么所有都赋 \(1\) 就可以啦,方案数为 \(1\)
  2. 否则,最大值超过 \(1\),我们设最大值为 \(2\) 来进行简化(更大的值产生的权的数量一定不会比这个更大,而且全部被包含)。这样我们只要枚举有多少个 \(2\) 就可以啦,其余全部填 \(1\),方案数为 \(\frac{n}{2}\)

总的方案数为 \(\frac{n}{2}+1\)

#include<bits/stdc++.h>
using namespace std;
int n;
signed main()
{
    scanf("%d",&n);
    printf("%d\n",n / 2 + 1);
    return 0;
}

B Messages(贪心+模拟)

传送门:B


Description

Vasya 有 \(n\) 封信。第 \(i\) 封信将在 \(t_i\) 秒之后接收。 每封信都开始都值 \(a\) 元,但收到消息后,消息的成本每分钟减少 \(B\) 元(\(B\) 可能变为负数)。Vasya 可以在任意时刻接收到任何消息,也可在任意时刻读取它。读完消息后,Vasya 的银行账户会收到该消息的当前成本。最初,Vasya 的银行账户为 \(0\)。 同时,每分钟 Vasya 的银行账户会收到 \(C\times k\) 元,\(k\) 是收到了但未读信的数量。 Vasya 非常贪婪(又一个葛朗台),正因为如此,他想让所有的信息在 \(T\) 分钟后被阅读,使其利益最大化。


Solution

一个很容易想到的贪心思路是,如果 \(C > B\),那么我们在 \(T\) 时刻读取所有信件,否则我们选择在收到这封信的同时直接读取信件。剩余的按照题意模拟即可。

#include<bits/stdc++.h>
using namespace std;
int n,a,b,c,T,res,awa;
int t[1010];
signed main()
{
    scanf("%d%d%d%d%d",&n,&a,&b,&c,&T);
    for(int i = 1;i <= n;i++)
    {
        scanf("%d",&t[i]);
        res += T - t[i];
    }
    awa = n * a;
    if(b < c) awa += res * (c - b);
    printf("%d\n",awa);
    return 0;
}

C Alternating Sum(思维+数学)

传送门:C


Description

给你两个整数 \(a\)\(b\) 。再给你一个序列 \(s_0, s_1, \cdots, s_n\),其中 \(s_i\)​ 要么为 \(1\) ,要么为 \(-1\)

已知这个序列以 \(k\) 为周期并且 \(k\) 整除 \(n + 1\) ,换句话说,对于所有满足 \(k \leq i \leq n\)\(i\) 都有 \(s_i = s_{i - k}\)

求出下式在模 \(10^9 + 9\) 意义下的值:

\(\sum_{i = 0}^{n} s_i a^{n - i}b^i\)


Solution

我们发现,这个序列显然是由周期为 \(k\) 的子序列进行循环的,对应项 \(s_i\) 相等,且后面的值对应项形成一个公差为 \((\frac{b}{a})^k\) 的等比数列,那么直接使用等比数列求和公式就可以啦,需要处理逆元,正向扫一遍即可。时间复杂度 \(O(klogn)\)。(注意:需要特判公差为 \(1\) 的情况,等比数列公式会炸,直接都相等即可)

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod = 1000000009;
const int N = 100000 + 10;
int n,a,b,k,ans;
int ksm(int x,int y)
{
    int res = 1;
    for(;y;y >>= 1,x = x * x % mod) if(y bitand 1) res = res * x % mod;
    return res;
}
char sig[N];
int s[N];
signed main()
{
    scanf("%lld%lld%lld%lld",&n,&a,&b,&k);
    scanf("%s",sig);
    int ox = (n + 1) / k;
    for(int i = 0;i < k;i++)
        s[i] = (sig[i] == '+') ? 1 : (mod - 1);
    int q = ksm(b,k) * ksm(ksm(a,k),mod - 2) % mod;
    for(int i = 0;i < k;i++)
    {
        int cur = ksm(a,n-i) * ksm(b,i) % mod;
        int nox = (ksm(b,n + 1) * ksm(ksm(a,n + 1),mod - 2) % mod - 1 + mod) % mod;
        int pox = (ksm(b,k) * ksm(ksm(a,k),mod - 2) % mod - 1 + mod) % mod;
        pox = ksm(pox,mod - 2);
        pox = pox * nox % mod * cur % mod;
        if(q == 1) ans = (ans + s[i] * cur % mod * ox % mod) % mod;
        else ans = (ans + s[i] * pox % mod) % mod;
    }
    printf("%lld\n",ans);
    return 0;
}

D Destruction of a Tree(思维+树上 DFS)

传送门:D


Description

给你一棵树,如果树上的节点有偶数条边与它相连,则这个节点是可删除的,删除这个节点后所有与之相连的边也将删除。判断一棵树是否可以依次删除所有节点。
输入: 第一行:一个整数 \(n\)
第二行:\(n\) 个整数 \(P_i\),表示编号为 \(i\) 的点与编号为 \(P_i\) 的点相连,保证是一棵树
输出: 可以删除输出 YES,并输出依次删除的点的编号;不可以则输出 NO


Solution

水紫。考虑直接暴力删边。先自顶向下搜,遇到度数为偶数的就删除与之相连的所有边,并加入答案序列。这时剩下的点再自顶向下搜一遍,如果度数为偶数那么就删边更新答案序列,如果是奇数那么无论如何也删不掉(因为他上面和下面的偶数已经被删完了),答案不存在。最后输出答案序列即可。由于每条边只访问了一次,所以整体时间复杂度上限为 \(O(n+m)\)

#include<bits/stdc++.h>
using namespace std;
const int N = 200000 + 10;
int n,root,len,tot,flag;
int cnt[N],seq[N],vis[N],head[N],ver[2*N],Next[2*N];
void add(int x,int y)
{
    ver[++tot] = y,Next[tot] = head[x],head[x] = tot;
}
void cancel(int x)
{
    seq[++len] = x;
    vis[x] = 1;
    for(int i = head[x];i;i = Next[i])
    {
        int v = ver[i];
        cnt[v]--;
    }
}
void dfs(int u,int fa)
{
    for(int i = head[u];i;i = Next[i])
    {
        int v = ver[i];
        if(v == fa) continue;
        dfs(v,u);
    }
    if(cnt[u] % 2 == 0) cancel(u);
}
void gfs(int u,int fa)
{
    if(not vis[u] and cnt[u] % 2 == 0) cancel(u);
    else if(not vis[u] and cnt[u] & 1) flag = 1;
    for(int i = head[u];i;i = Next[i])
    {
        int v = ver[i];
        if(v == fa) continue;
        gfs(v,u);
    }
}
signed main()
{
    tot = 1;
    scanf("%d",&n);
    for(int i = 1;i <= n;i++)
    {
        int sop;
        scanf("%d",&sop);
        if(not sop) root = i;
        else
        {
            add(i,sop);
            add(sop,i);
            cnt[sop]++;
            cnt[i]++;
        }
    }
    dfs(root,0);
    gfs(root,0);
    if(flag)
    {
        printf("NO\n");
        return 0;
    }
    printf("YES\n");
    for(int i = 1;i <= len;i++) printf("%d\n",seq[i]);
    return 0;
}

E Cutting Rectangle

传送门:E
还没错,本雪就是菜 qaq

posted @ 2021-12-16 13:05  一程山雪  阅读(119)  评论(0)    收藏  举报