A

题目描述
牛可乐发明了一种n面骰子(点数分别从1到n,掷出每面的概率为1/n)
去给牛牛玩,因为牛牛是个欧皇,所以他想测试一下牛牛的人品,他告诉牛牛,让牛牛投m{}m次骰子,牛牛如果全部投出点数为{}nn的面就算牛牛赢,牛牛很相信自己的人品,就和牛可乐赌一包辣条,说自己肯定可以全部投出点数为{}nn点面,但是牛牛又有点害怕自己打赌输了,想让你提前帮他计算一下他输概率有多少?
思路:暴力算,逆元。
代码:

#include <iostream>
#include <bits/stdc++.h>
using namespace std;
const int mod=1e9+7;
inline int read()
{
    int X=0; bool flag=1; char ch=getchar();
    while(ch<'0'||ch>'9') {if(ch=='-') flag=0; ch=getchar();}
    while(ch>='0'&&ch<='9') {X=(X<<1)+(X<<3)+ch-'0'; ch=getchar();}
    if(flag) return X;
    return ~(X-1);
}

long long quickpow(long long a, long long b) {
    if (b < 0) return 0;
    long long ret = 1;
    a %= mod;
    while(b) {
        if (b & 1) ret = (ret * a) % mod;
        b >>= 1;
        a = (a * a) % mod;
    }
    return ret;
}
long long inv(long long a) {
    return quickpow(a, mod - 2);
}
int main()
{
    int t;
    t=read();
    while(t--){
        long long n,m;
        n=read(),m=read();
        long long q=quickpow(n,m);
        printf("%lld\n",(q-1)*inv(q)%mod);
    }
    return 0;
}

B

题目描述
牛牛感觉在上一次赌约中,情况对于自己非常不利,所以决定再赌一场。
这时候,牛蜓队长出现了:第一,绝对不意气用事;第二,绝对不漏判任何一件坏事;第三,绝对裁判的公正漂亮。
牛蜓队长带他们来到了一个棋盘游戏,棋盘左上角是(0,0)(0,0),这个棋盘在(x,y)(x,y)的位置有一个棋子,牛牛和牛可乐轮流移动这个棋子,这个棋子可以左移也可以上移,可以移动一格或者两格,直到不能再移动(即到(0,0)(0,0))的那个人算输。
如果原本在(x,y)(x,y),左移一格即为(x,y -1)(x,y−1),上移一格即为(x-1,y)(x−1,y)
这个时候,牛牛为了弥补上一局的不公平,决定要自己先手,如果两个人都用最优的策略,最后牛牛是否能获胜。
思路:打表发现有规律。打表就看看当前点能否到达一个先手必输点,可以的话该点先手就必胜。
代码:

#include <bits/stdc++.h>
using namespace std;
int a[3][3]={{0,1,1},{1,0,1},{1,1,0}};
int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        int x,y;
        scanf("%d%d",&x,&y);
        if(a[x%3][y%3])printf("yyds\n");
        else printf("awsl\n");
    }
    return 0;
}

C:模拟,不想写了。
D:
题目描述
a + b的值为x,a&b的值为y,首先需要判断能否有一组a,b满足当前的情况,如果有,那么求出a xor b,否则输出-1
(其中a,b{}>0a,b>0)
思路:基本位运算。
代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        long long x,y;
        scanf("%lld%lld",&x,&y);
        long long a=y,b=y;
        x-=2*y;
        for(int i=62;i>=0;i--){
            if(y&(1ll<<i))continue;
            if(x>=(1ll<<i))x-=(1ll<<i),a+=(1ll<<i);
        }
        if(x==0)printf("%lld\n",a^b);
        else printf("-1\n");
    }
    return 0;
}

G

题目描述
牛牛每天都要做的事就是读书,从书里找自己喜欢的句子,他每天都会去读一本书,如果牛牛今天读的书的某连续{}kk个字符刚好是牛牛喜欢句子的某个前缀,那么牛牛将得到{}kk点兴奋感,但他每天只能注意到一次自己喜欢的句子(也就是每天只能增加一次兴奋感),也就是说他会尽量去找那个让自己兴奋度增加最多的句子,那么,{}nn天之后牛牛总共最多能有多少兴奋感?
思路:kmp模板,找份模板,稍加修改就行了。
代码

#include <iostream>
#include <bits/stdc++.h>
using namespace std;
int nextt[100005];
void getnext(char *p)
{
    int lenp=strlen(p);
    nextt[0]=-1;
    int k=-1;
    int j=0;
    while(j<lenp-1)
    {
        //p[k]表示前缀,p[j]表示后缀(并没有“真”!)
        if(k==-1||p[j]==p[k])//j在这
        {
            j++;//j+1在这
            k++;//k=k+1
            //---->若p[k]=p[j],则next[j+1]=next[j]+1;
            //下一个位置的next
            if(p[j]!=p[k])//当p[k]!=p[j]时,与主串不匹配时可以返回到这
            {
                nextt[j]=k;
            }
            else//当p[j]=p[k]时与主串匹配时你在j位置不匹配匹配串返回这里当前
                //还是 不能与主串匹配,然后还是要返回next[]即next[next[k]]
                nextt[j]=nextt[k];//优化了
        }
        else
        {
            k=nextt[k];
        }
    }
    return;
}
int KMP(char *s,char *p)
{
    int i=0,temp=0;
    int j=0;
    int lens=strlen(s);
    int lenp=strlen(p);
    while(i<lens&&j<lenp)
    {
        //如果j=-1(表示第一次开始或者重新开始匹配),即失配于首字符
        if(j==-1||s[i]==p[j])
        {
            j++;
            i++;
            temp=max(temp,j);
        }
        else
        {
            //如果j!+-1,或者当前匹配失败 ,则
            j=nextt[j]; // i不变,j=next[j]
        }
    }
    return temp;
}

int main()
{
    int t;
    char a[100005],b[100005];
    scanf("%s",a);
    getnext(a);
    scanf("%d",&t);
    int ans=0;
    while(t--)
    {
        scanf("%s",b);
        ans+=KMP(b,a);
    }
    printf("%d\n",ans);
    return 0;
}

I
题目描述
有一个n×m的网格地图,每个点有个值a ij
,现在牛牛要从(1,1)走到(n,m),他可以往右边或者往下走,每次到一个点会获得当前的点权值,并将权值和mod 1e4+7,当牛牛从不同方式走到(n,m)的时候能获得多少种权值和?
思路:
判断当前点有那些权值,暴力硬算。
代码;

#include <bits/stdc++.h>
using namespace std;
bool dp[101][101][10007];
int a[101][101];
const  int mod=1e4+7;
int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            scanf("%d",&a[i][j]);
            a[i][j]%=mod;
        }
    }
    dp[1][1][a[1][1]]=1;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            for(int k=0;k<mod;k++){
                dp[i][j][k]|=dp[i-1][j][(k-a[i][j]+mod)%mod];
                dp[i][j][k]|=dp[i][j-1][(k-a[i][j]+mod)%mod];
            }
        }
    }
    int ans=0;
    for(int i=0;i<mod;i++)if(dp[n][m][i])ans++;
    printf("%d\n",ans);

    return 0;
}

J
题目描述
牛牛苦练武功绝学——轻功水上漂,最终没有练成,但是他学会了在树上行走的本领。
这天,牛牛落入了敌人的陷阱,身后有巨石追击,面前有n个点,n-1条边连成一张连通图(一棵树),现在牛牛必须立马选择进入这张图中,但是牛牛发现,这张图有两种不同的点,一旦进入一个点,所有与该点不同类型的点都会消失(相连的边也会消失),牛牛只能走到有边相连的点,牛牛想要自己尽量有更多的点可以活动,那么他可以进入哪些点?
思路:可以按照相同点建边,然后找最大的那几个联通图,或者建边搜相同点,或者并查集找最大的块。
代码:

#include <iostream>
#include <bits/stdc++.h>
using namespace std;
vector<int>edge[200005];
int type[200005],vis[200005];
int dfs(int u,int t)
{
    vis[u]=1;
    int num=0;
    for(int i=0;i<edge[u].size();i++){
        int v=edge[u][i];
        if(vis[v]||type[v]!=t)continue;
        num+=dfs(v,t);
    }
    return num+1;
}
vector<int>ans,ansn;
void dfs1(int u,int t)
{
    vis[u]=1;
    ans.push_back(u);
    for(int i=0;i<edge[u].size();i++){
        int v=edge[u][i];
        if(vis[v]||type[v]!=t)continue;
        dfs1(v,t);
    }
}
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&type[i]);
    }
    for(int i=0;i<n-1;i++){
        int u,v;
        scanf("%d%d",&u,&v);
        edge[u].push_back(v);
        edge[v].push_back(u);
    }
    int num=0;
    for(int i=1;i<=n;i++){
        if(vis[i])continue;
        int temp=dfs(i,type[i]);
        if(temp>num){
            ansn.clear();
            ansn.push_back(i);
            num=temp;
        }
        else if(temp==num)ansn.push_back(i);
    }
    memset(vis,0,sizeof(vis));
    for(int i=0;i<ansn.size();i++){
        //printf("%d\n",ansn[i]);
        dfs1(ansn[i],type[ansn[i]]);
    }
    sort(ans.begin(),ans.end());
    printf("%d\n",ans.size());
    for(int i=0;i<ans.size();i++){
        printf("%d ",ans[i]);
    }
    printf("\n");
    return 0;
}