2024牛客暑期多校训练营1

A、B、C、H、I

总结

成绩:SOLVE:3,Penalty:4

C

签到题

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
void solve()
{
    int n,k;
    cin>>n>>k;
    t-=n;
    a[t]=k*t;
    t++;
    a[t-1]=(a[t-2]+a[t-1])%mod;
    cout<<a[t-1]<<'\n';
}

H

签到题

显然,最优答案一定是参加第一场或第二场

对于每一场,只要有比自己排名高的能放到另一场的就都放到另一场

模拟取最小值

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
void SINGLE_TEST()
{
    int n,m;
    cin>>n;
    map<string,PII> a,b;
    int ans=INF;
    vector<string> s,w;
    vector<int> s1,s2,w1,w2;
    int t1,t2;
    int z1,z2;
    map<string,bool> vis1,vis2;
    for(int i=1;i<=n;i++){
        string ss;cin>>ss;
        int ss1,ss2;
        cin>>ss1>>ss2;
        s.push_back(ss);
        s1.push_back(ss1);
        s2.push_back(ss2);
        if(ss=="lzr010506"){
            t1=ss1;
            t2=ss2;
        }
        vis1[ss]=true;
    }
    cin>>m;
    for(int i=1;i<=m;i++){
        string ss;cin>>ss;
        int ss1,ss2;
        cin>>ss1>>ss2;
        w.push_back(ss);
        w1.push_back(ss1);
        w2.push_back(ss2);
        if(ss=="lzr010506"){
            z1=ss1;
            z2=ss2;
        }
        vis2[ss]=true;
    }
    int p1=0,p2=0;
    for(int i=0;i<n;i++){
        if(s[i]=="lzr010506") continue;
        if(s1[i]>t1 || s1[i]==t1 && s2[i]<t2){
            p1++;
            if(vis2[s[i]]){
                p1--;
            }
        }
        
    }
    for(int i=0;i<m;i++){
        if(w[i]=="lzr010506") continue;
        if(w1[i]>z1 || w1[i]==z1 && w2[i]<z2){
            p2++;
            if(vis1[w[i]]){
                p2--;
            }
        }
        
    }
    cout<<min(p1,p2)+1<<"\n";
}

A

我们可以将每个数字拆位考虑

拆位

考虑枚举$i$个数,这$i$数的$AND$为1

那么对于这$i$数,固定第1位为1,剩下的m-1位,至少有一个数在这一位上为0,依次进行分配

对于每一组,方案数是$(2^i-1)^{m-1}$

对于不选的$n-i$个数,每一个数随意选择即可,每一个数的方案数为$2^{m-1}$

因此答案为$\sum_{i=1}^{n}C_n^i\cdot (2^i-1)^{m-1}\cdot {(2^{m-1})}^{n-i}$

值得注意,这里的模数$q$不一定为质数,无法使用逆元,考虑到范围较小,可以预处理组合数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 计算组合数,取模
void calculate_combinations() {
    for (int n = 0; n <= MAXN; ++n) {
        for (int k = 0; k <= n; ++k) {
            if (k == 0 || k == n) {
                comb[n][k] = 1; // C(n, 0) = C(n, n) = 1
            } else {
                comb[n][k] = (comb[n - 1][k - 1] + comb[n - 1][k]) % q; // 递推关系式,取模
            }
        }
    }
}
void SINGLE_TEST()
{
    int n,m;
    cin>>n>>m>>q;
    calculate_combinations();
    ll ans=0;
    for(int i=1;i<=n;i++){
        ll res=get_combination(n, i) *power(power(2,i,q)-1,m-1,q);
        res%=q;
        res*=power(power(2,m-1,q),n-i,q);
        (ans+=res)%=q;
    }
    cout<<ans<<"\n";
}

I

图论

对于每一个点的每一个方向进行预处理,在环上的点的答案是环的大小,一般的点的答案是走到边界的距离或到环的距离加上环的大小

对于每一个查询即可$O(1)$访问

难在代码处理

参考代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
void dfs(int x,int y,int d){
    if(x>n||x<=0||y>m||y<=0)return;
    sx[++hd]=x,sy[hd]=y,sd[hd]=d;
    if(p[x][y][d]){
        rg=1;
        return;
    }
    p[x][y][d]=1;
    int nd=dt[d][mir[x][y]];
    dfs(x+dx[nd],y+dy[nd],nd);
}
int Dfs(int x,int y,int d){
    if(x>n||x<=0||y>m||y<=0)return 0;
//  cerr<<x<<' '<<y<<' '<<d<<endl;
    sx[++hd]=x,sy[hd]=y,sd[hd]=d;
    dp[x][y][d]=0;
    assert(!p[x][y][d]);
    p[x][y][d]=1;
    int nd=dt[d][mir[x][y]];
    dp[x][y][d]+=Dfs(x+dx[nd],y+dy[nd],nd);
    if(!vm[x][y]&&ok(d,mir[x][y])){
        vm[x][y]=1,++dp[x][y][d];
    }
    return dp[x][y][d];
}
void work(){
    cin>>n>>m;
    memset(dp,-1,sizeof dp);
    FOR(i,1,n){
        cin>>s;
        FOR(j,1,m){
            if(s[j-1]=='-')mir[i][j]=0;
            if(s[j-1]=='|')mir[i][j]=1;
            if(s[j-1]=='/')mir[i][j]=2;
            if(s[j-1]=='\\')mir[i][j]=3;
        }
    }
    cin>>q;
    while(q--){
        int x,y,d;
        cin>>x>>y>>s;
        if(s=="above")d=0;
        if(s=="below")d=1;
        if(s=="left")d=2;
        if(s=="right")d=3;
        x+=dx[d],y+=dy[d];
        if(x>n||x<=0||y>m||y<=0){
            puts("0");
            continue;
        }
        if(get(x,y,d)!=-1){
            printf("%d\n",get(x,y,d));
            continue;
        }
        dfs(x,y,d);
        if(rg){
            FOR(i,1,hd){
                int X=sx[i],Y=sy[i];
                if(!vm[X][Y]&&ok(sd[i],mir[X][Y])){
                    vm[X][Y]=1,++ans;
                }
            }
            FOR(i,1,hd)dp[sx[i]][sy[i]][sd[i]]=ans;
            clr();
            printf("%d\n",get(x,y,d));
            continue;
        }
        int X=sx[hd],Y=sy[hd],D=dt[sd[hd]][mir[X][Y]]^1;
        clr();
        Dfs(X,Y,D);
        X=sx[hd],Y=sy[hd],D=dt[sd[hd]][mir[X][Y]]^1;
        clr();
        Dfs(X,Y,D);
        clr();
        printf("%d\n",get(x,y,d));
    }
}

B

不难发现,本题的答案$=$A题的答案$-$只存在1个子序列$AND$为$1$的方案数

对于1个子序列$AND$为$1$的情况,可以反向理解为从这个序列中删掉任意一个数都无法为$1$,

也就是每一个数至少在一个位上必须只有自己才有$0$,这样才能确保自己不能被去掉(即对应官方题解中的“特殊位”)

因此,我们可以先为每一个数分配$0$,然后再将多出来的$0$进行任意分配,这样就可以得到在这一组下的方案数

Licensed under CC BY-NC-SA 4.0
最后更新于 Jul 17, 2024 00:00 UTC
本博客已稳定运行
使用 Hugo 构建
主题 StackJimmy 设计