绍兴一中模拟赛7.6

T1:人类杀

题目描述
 因为吃完晚饭之后只有半个小时不够打狼人杀,某个天才设计了一个新游戏叫做人类杀。
  有若干个狼人和一个人类,在晚上每个狼人依次醒来指票一个人,第二天白天吃票最多的人死亡。假如存在平票情况则无人死亡。狼人的游戏目标是找出那个人类然后杀掉。白天每个人都发一次言之后进入天黑。
  小A正在旁观这个游戏,在一天白天每个人都发了言解释了夜晚时自己的票给了谁,假设所有狼人都说了实话,那么现在小A想要知道,多少人有可能是人类。
输入格式
第一行两个正整数 N , M N 表示总人数, M 表示吃票最多的人是谁
接下来一行 N 个整数,第 i 个整数 a i 表示第 i 个人说自己在夜晚时上票给了谁
保证数据合法,注意吃票最多的人也有可能是人类
输出格式
输出一行表示有多少人可能是人类
样例输入
5 1
1 1 2 2 3
样例输出
2
样例解释
3号和4号有可能是人类,一定没有其他情况
数据规模与约定
30%: N 1000 N 1000
另30%: a i ∈< 1 , 2 > a i ∈< 1 , 2 >
对于所有数据 N 1000000 , 1 a i N

题解:

并没有什么什么好说的。
根据有多少个人同时吃票最多和m是否为0可以分成四种情况讨论。
细节也并不是很难写。

标程:

#include<bits/stdc++.h>
using namespace std;
const int N=1000002;
int n,m,i,a[N],s[N],res,ans,ss[N];
int read(){
    int x=0,f=1;char c;
    do{c=getchar();if (c=='-') f=-1;}while (c<'0'||c>'9');
    while ('0'<=c&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
    return x*f;
}
bool check(int x){
    if (!m){
        if (ss[ans]==1) return s[x]==ans;
        if (s[x]<ans) return 1;
        return ss[ans]>2;
    }else{
        if (ss[ans]==2) return s[x]==ans && x!=m;
        if (ss[ans-1]) return x!=m;
        return 1;
    }
}
int main(){
    n=read();m=read();
    for (i=1;i<=n;i++) a[i]=read(),s[a[i]]++;
    for (i=1;i<=n;i++) ss[s[i]]++,ans=max(ans,s[i]);
    for (i=1;i<=n;i++) res+=check(a[i]);
    printf("%d",res);
}

T2:染色

题目描述
小A正在帮助小M刷她家的墙壁
小M家的墙可以分为 n 块,每段需要被刷成黑色或者白色。你可以认为每一块墙最开始时既不是黑色也不是白色。
小M有一把神奇的刷子,每次可以刷连续的一段墙,但先刷的颜色会被后刷的颜色覆盖。这把刷子只能够用 k
现在小A想要知道,最多能正确地刷对多少块墙的颜色。
输入格式
第一行两个正整数 n , k
接下来 n 个整数,每个整数都是0或者1表示这块墙需要被刷成的颜色。
输出格式
共一行一个整数,表示最多能刷对几块墙的颜色。
样例输入
5 2
0 1 0 1 0
样例输出
4
样例解释
先刷1到5,再刷2到4
数据规模与约定
对于30%的数据, n 3000
对于另30%的数据, k 3
对于100%的数据, n 100000 , k 50

题解:

不难证明最后一定会是2*k-1段黑白相间的段。
然后dp,有两种做法

1.标程的做法

d p [ i ] [ j ] [ p ] 表示前 i 格,分成 j 段,第 i 格染成 p 的最大能刷对的颜色数
d p [ i ] [ j ] [ p ] = m a x ( d p [ i 1 ] [ j 1 ] [ p 1 ] , d p [ i 1 ] [ j ] [ p ] ) + ( p == c o l [ i ] )

#include<cstdio>
using namespace std;
int n,m;
int dp[100005][100][2];
inline int max(int x,int y){return x>y?x:y;}
inline int read(){
    int ret=0;char c=getchar();
    while((c>'9')||(c<'0'))c=getchar();
    while((c>='0')&&(c<='9'))ret=(ret<<1)+(ret<<3)+c-'0',c=getchar();
    return ret;
}
int main(){
    n=read();m=read();
    for(int i=1;i<=n;i++){
        int x=read();
        for(int j=1;j<=2*m-1;j++)
            for(int p=0;p<2;p++)dp[i][j][p]=max(dp[i-1][j-1][p^1],dp[i-1][j][p])+(p==x);
    }
    printf("%d",max(dp[n][2*m-1][0],dp[n][2*m-1][1]));
}
2.我的做法

感觉只是把标程做法变复杂了点而已,可以忽略
f [ i ] [ j ] [ t ] 表示前 i 格,分成 j 段,最后一段最大能刷对的颜色数
f [ i ] [ j ] [ t ] = m a x ( f [ i ] [ j ] [ t ] , f [ p ] [ j 1 ] [ t 1 ] + s u m [ t ] [ i ] s u m [ t ] [ p ] )
可用单调队列优化,因为内存只有128M,所以用了vector,卡内存

#include<bits/stdc++.h>
using namespace std;
#define now st[j][t][st[j][t].size()-1]
const int N=100001;
int n,k,i,j,t,f[N][100][2],sum[2][N],x,pre;
vector<int>st[100][2];
inline char gc(){
    static char buf[100000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
#define gc getchar
inline int read(){
    int x=0,fl=1;char ch=gc();
    for (;ch<48||ch>57;ch=gc())if(ch=='-')fl=-1;
    for (;48<=ch&&ch<=57;ch=gc())x=(x<<3)+(x<<1)+(ch^48);
    return x*fl;
}
int main(){
    n=read();k=read();
    for (i=1;i<=n;i++) x=read(),sum[0][i]=sum[0][i-1]+(x==0),sum[1][i]=sum[1][i-1]+(x==1);
    k=2*k-1;
    for (i=1;i<=n;i++)
        for (j=1;j<=k && j<=i;j++)
            for (t=0;t<2;t++){
                if (st[j-1][t^1].size()) pre=st[j-1][t^1][0];
                else pre=0;
                f[i][j][t]=f[pre][j-1][t^1]+sum[t][i]-sum[t][pre];
                while (st[j][t].size() && f[i][j][t]-sum[t^1][i]>=f[now][j][t]-sum[t^1][now]) st[j][t].pop_back();
                st[j][t].push_back(i);
            }
    printf("%d",max(f[n][k][0],f[n][k][1]));
}

T3:神经衰弱

题目描述
天然少女小雪非常喜欢玩一个叫做神经衰弱的游戏。
游戏规则是,有若干种牌,每种牌有若干对,开始时全都正面朝下放置。
然后每次同时翻开两张牌,假如这两张牌是同一种类,则拿走这两张牌,否则再次翻回背面。
小雪虽然看上去傻乎乎的但是玩这个游戏非常厉害,所以可以认为她是绝对聪明的,即会采取最优决策和有着完美的记忆力
现在小雪想要知道,对于某一副牌局,她期望拿多少次可以拿走所有牌。
Hint:固定策略为先把所有未知的牌拿一遍,再拿已知的成对的牌
输入格式
第一行一个整数 n 表示牌的种类数
第二行 n 个整数 a i 表示第 i 种牌有多少对
输出格式
一个整数,表示期望mod998244353
即假如答案是 a / b ,你需要输出的是某个数 x ,使 x b = a ( m o d 998244353 ) x b = a ( m o d 998244353 )
保证a≠0,b≠0
样例输入
2
1 1
样例输出
332748121
样例解释
期望是 10 3
数据规模与约定
30%: n 5 , a i 2
60%: n 3000
对于所有数据 n 10 6 , a i 10 9

题解:

不难发现,当发现两张相同的牌时直接拿掉和最后拿掉并没有区别。
所以我们可以认为先把所有牌翻一遍再考虑消去一定最优。
那么我们只要计算在第一遍翻牌时恰好翻出的两张是相同的牌的概率。
因为每次翻牌操作是等价的,所以可以直接把每次翻牌都当做第一次计算期望
m = Σ 1 n a [ i ]
则最少要取 m 次,最多要取 2 m 次,因为每次取到两张相同的就可以比最多的少取一次,不管什么时候取,拿到相同牌的概率都相等(这句话很难理解要仔细体会,文末还会再稍微解释一下),所以
a n s = 2 m Σ 1 n C a [ i ] 2 2 C 2 m 2 m

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll M=998244353;
int n,i;
ll x,ans,m;
inline char gc(){
    static char buf[100000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
#define gc getchar
inline int read(){
    int x=0,fl=1;char ch=gc();
    for (;ch<48||ch>57;ch=gc())if(ch=='-')fl=-1;
    for (;48<=ch&&ch<=57;ch=gc())x=(x<<3)+(x<<1)+(ch^48);
    return x*fl;
}
ll pow(ll x,ll y){
    ll z=1;
    for (;y;y>>=1,x=x*x%M)
        if (y&1) z=z*x%M;
    return z;
}
int main(){
    n=read();
    for (i=1;i<=n;i++) x=read(),m=(m+x)%M,ans=(ans+x*2*(x*2-1))%M;
    ans=ans*pow(m*2*(m*2-1)%M,M-2)%M;
    ans=((m*2-ans*m)%M+M)%M;
    printf("%lld",ans);
}

这里解释一下为什么前后拿到相同牌的概率都相等,这里说两个真命题,自己融合一下就能懂了。
1.举个例子,一个盒子里有n个球,每次拿出来一个,不能放回去,那么,拿到每个球的概率都是相等的。
摸到第一个球的概率: 1 n
摸到第二个球的概率: ( 1 1 n ) 1 n 1 = 1 n
………………
2.因为每次不管翻到的两张牌相不相同,以后都不会再选,所以这和摸球其实是一样的

猜你喜欢

转载自blog.csdn.net/xumingyang0/article/details/80945543
今日推荐