[游记]来自学长的馈赠4-2022.7.24

额,早上睡过头了没吃饭……饿

A. 活动投票

B. 大佬

C. Dp搬运工3

D. Beautiful

赛时得分:$220/400$

赛时排行:$\mathbf{Rank9}$

Rating 变化:+154 $\to$ 394 ,$\mathbf{Rank8}$

 

 

 

 

 

A. 活动投票

注意到了非同寻常的时空限制,显然 $\operatorname{sort}$ 和开桶都绝对不可行

注意到一共至少有 $\lceil \frac{n}{2} \rceil$ 个人投给了同一个人,也就是说这个人的得票数大于所有其他人的得票数之和

这里涉及到了摩尔投票法

本来在 Biology 那道题里涉及了但是有点懒就没学 =_=

补一下补一下

我们已知一个数组中有一个元素的数量超过了其他元素数量的总和(大于一半),但是不知道是哪一个元素。

那么我们可以这样想,把数组看成一个投票箱,下标 $i$ 即为 $num[i]$ 投出了支持票

我们不关心每个参与竞选的候选人的得票数,我们只需要知道是哪一个候选人的得票数超过了一半

我们可以假定候选人 $i=1$ 支持的 $nums[1]$ 是候选人,那么他需要满足他的得票比其他任何人都多

我们可以这样操作,我们统计他的得票 $count$ 。

我们顺序扫描每个人投出的票,如果是支持他的,则将 $count$ 加 1,否则就将 $count$ 减一

当 $count$ 数量降为 $0$ 时,就意味着没人支持他了,我们就将候选人换成当前投票者 $i$ 支持的候选人 $nums[i]$,

由于已知有一个支持者的得票数超过了一半,那么无论剩下的人支持谁,都不能撼动他到最后的地位。

想象一下最恶劣的情况,假设这个候选人出现在数组的头部,那么他的最终得票数会最少,因为反对者都在他后面

即使是这样,他的支持者仍然能确保成为最后仅存的候选人,因为支持者足够多

那么无论这个候选人出现在什么位置,依照上述规则投票,最后的候选人仍然是他,不可能会是其他候选人,

因为其他候选人都会被这个最后的候选人的支持者反对出去。

 

 

 

 

 

#include<cstdio>
#include<cstring>
#include<string>
#define WR WinterRain
using namespace std;
const int WR=101000;
int n,s,cnt;
int len;
int read(){
    int s=0,w=1;
    char ch=getchar();
    while(ch>'9'||ch<'0'){
        if(ch=='-') w=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        s=s*10+ch-'0';
        ch=getchar();
    }
    return s*w;
}
signed main(){
    n=read();
    for(int i=1;i<=n;i++){
        int tmp=read();
        if(!cnt){
            s=tmp;
            cnt=1;
        }
        if(tmp==s) cnt++;
        else cnt--;
    }
    printf("%lld",s);
    return 0;
}
View Code

 

B. 大佬

首先我觉得题面里比较迷惑的一个点是难度好像是可以改变的

但是第一组样例很好地告诉了我们这一点所以倒不用在这方面花太大的精力

然后我们考虑如何求解

首先显然地,分子等于 $\sum\limits_{i=1}^{m}$ $\text{以 i 为最大值的方案数}$ $\times w[i]$

分母就是总的方案数

发现以 $i$ 为最大值的方案数不太好求,考虑转化为 前缀和差分

设 $cnt[i]$ 表示 以 i 为最大值的方案数

那么不妨设 $f(i)=\sum\limits_{j=1}^{i} cnt[j]$ ,也就是 最大值不大于 $i$ 的方案数

此时对于 $k$ 个位置,每个位置都有 $i$ 种选择,可以遍历 $1$ 到 $i$ 的所谓完系

那么惊讶地发现 $f(i)=i^k$ ,预处理一下或者选择直接快速幂可以很快地求出 $cnt[i]$

又注意到对于每一个 $cnt[i]$ ,会有 $n-k+1$ 个起点可供选择

这样分子即可求得,为 $(n-k+1)\sum\limits_{i=1}^{m}w[i](i^k-(i-1)^k)$

发现分母就是我们求的 $f(m)$

所以可以得出所求为

$$\dfrac{(n-k+1)\sum\limits_{i=1}^{m}w[i](i^k-(i-1)^k)}{m^k}$$

为啥数据范围这么小 $\cdots\cdots$

 

 

 

 

#include<cstdio>
#include<cstring>
#include<string>
#define int long long
#define WR WinterRain
using namespace std;
const int WR=1001000,mod=1000000007;
int n,m,k;
int w[WR],f[WR];
int ans;
int read(){
    int s=0,w=1;
    char ch=getchar();
    while(ch>'9'||ch<'0'){
        if(ch=='-') w=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        s=s*10+ch-'0';
        ch=getchar();
    }
    return s*w;
}
int quick_pow(int a,int b){
    int base=a,res=1;
    while(b){
        if(b&1) res=res*base%mod;
        base=base*base%mod;
        b>>=1;
    }
    return res;
}
signed main(){
    n=read(),m=read(),k=read();
    for(int i=1;i<=m;i++) w[i]=read();
    for(int i=1;i<=m;i++) f[i]=quick_pow(i,k);
    int dn=quick_pow(m,k);
    dn=quick_pow(dn,mod-2);
    for(int i=1;i<=m;i++){
        ans=(ans+w[i]*(f[i]-f[i-1])%mod*dn%mod);
    }
    ans=ans*(n-k+1)%mod;
    printf("%lld",(ans+mod)%mod);
    return 0;
}
View Code

 

C. DP搬运工3

又是一个 $\operatorname{DP}\cdots$

我其实有点胜之不武 $\cdots$ 因为 Jijidawang 酱昨天晚上发了一条犇犇安利了一下这道题,我就看了一眼

然后觉得太阴间不会考(

此时,题变难了,我也困得快睡着了,正是祸不单行的时候

看着诡异的题面,又想起昨天优先级搞错爆的 $0$ ,不禁簌簌地流下眼泪

我对自己说,事已至此,不必难过,好在我还有打的 $\operatorname{next}$_$\operatorname{permutation}$ 的暴力!(

开玩笑的(

下面的内容引自 这篇博客

首先可以固定一个数组为从 $1$ 到 $n$ 的原始排列,考虑第二个数组

考虑设 $dp[i][j][k]$ 表示已经填进了 $i$ 之前的数字(包括 $i$),现在有 $j$ 个数的下标大于 $i$ ,$k$ 为所有 $\max{a[p],b[p]}\leqslant i$ 中 $\max{a[p],b[p]}$ 的和 的方案数

那么可以分类讨论:

  1. 将 $i$ 恰好填到了下标 $i$ 那么 $k+=i$
  2. 将 $i$ 填到了下标小于 $i$ 的位置,下标为 $i$ 的位置填进的数小于 $i$ ,那么 $k+=2i$ ,$j--$
  3. 将 $i$ 填到了下标小于 $i$ 的位置,下标为 $i$ 的位置填进的数大于 $i$ ,那么 $k+=i$
  4. 将 $i$ 填到了下标大于 $i$ 的位置,下标为 $i$ 的位置填进的数小于 $i$ ,那么 $k+=i$
  5. 将 $i$ 填到了下标大于 $i$ 的位置,下标为 $i$ 的位置填进的数大于 $i$ ,那么 $j++$

答案是 $\sum\limits_{i=K}^{+∞}dp[n][0][i]$ ,可以在更新时把大于 $K$ 的数和 $K$ 取最小值,这样只用记 $dp[n][0][K]$ 即可

最后要乘 $n!$ 计算第一个数组的错排方法

 

#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#define int long long
#define WR WinterRain
using namespace std;
const int WR=1001000,mod=998244353;
int n,K,ans;
int dp[55][55][2555];
int read(){
    int s=0,w=1;
    char ch=getchar();
    while(ch>'9'||ch<'0'){
        if(ch=='-') w=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        s=s*10+ch-'0';
        ch=getchar();
    }
    return s*w;
}
signed main(){
    n=read(),K=read();
    if(K>n*n) K=n*n;
    dp[0][0][0]=1;
    for(int i=1;i<=n;i++){
        for(int j=0;j<=i;j++){
            for(int k=0;k<=K;k++){
                if(j) dp[i][j-1][min(k+(i<<1),K)]=(dp[i][j-1][min(k+(i<<1),K)]+
                                                   j*j%mod*dp[i-1][j][k]%mod)%mod;
                dp[i][j+1][k]=(dp[i][j+1][k]+dp[i-1][j][k])%mod;
                dp[i][j][min(k+i,K)]=(dp[i][j][min(k+i,K)]+dp[i-1][j][k]*(j<<1|1)%mod)%mod;
            }
        }
    }
    ans=dp[n][0][K];
    for(int i=1;i<=n;i++) ans=ans*i%mod;
    printf("%lld",ans);
    return 0;
}
View Code

 

posted @ 2022-07-24 14:36  冬天丶的雨  阅读(112)  评论(4编辑  收藏  举报
Live2D