纪中爆零之行Day1

· · 个人记录

又来纪中了心态爆炸

上次来热的跟鬼一样,这次贼凉快,(因为有台风)换了个机房,成功被这个电脑搞成自闭,开机一段时间自动死机,下载东西自动死机,打开XCOJ自动死机,打开安装包自动死机,做个考试开关电脑五六次,(昨天晚上开关十来次)第一天考试,心情毫无波澜

T0 游戏

就是这么个题,第一眼就觉得是博弈论,虽然上次来的时候考过博弈论,但听纪中的dalao们讲完后还是不会,所以放弃思考,结果这题二维前缀和就能过。 定义bool型的二维数组f,f(x,y)表示从(1,1)到(x,y)的矩形先手是不是必赢,最后就只用判断f(n,n)的值就可以了

对于f数组的处理

根据定义我们可以得到如果f(x,y-1)=0,如果最后一列可以删,我们就赢了,同样的,如果f(x-1,y)=0,如果最后一排可以删,我们就赢了,如果两者都为1,则对手必胜

那么怎么判断当前排/列能不能删?

二维前缀和!

定义一个二维的a数组,a(x,y)表示从(1,1)到(x,y)所有元素值的和,处理前缀和的方法就不再赘述(因为连我都会的东西就没必要说了)上代码了

#include<bits/stdc++.h>
#define fa(a,b,c) for(int a=b;a<=c;a++)
using namespace std;
long long a[1010][1010];
bool f[1010][1010];
int n,t;
int main()
{
    scanf("%d",&t);
    fa(i,1,t){
        scanf("%d",&n);
        fa(i,1,n){
            fa(j,1,n){
                scanf("%lld",&a[i][j]);
                a[i][j]=a[i-1][j]+a[i][j-1]-a[i-1][j-1]+a[i][j];
            }
        }
        memset(f,0,sizeof(f));
        fa(i,1,n)
        fa(j,1,n){
            if(f[i-1][j]==0)
            if((a[i][j]-a[i-1][j])%2==0){
                f[i][j]=1;
                continue;
            }
            if(f[i][j-1]==0)
            if((a[i][j]-a[i][j-1])%2==0){
                f[i][j]=1;
                continue;
            }
        }
        if(f[n][n])
        printf("W\n");
        else
        printf("L\n");
    }
    return 0;
}

T1 六边形

JMY大佬(码王)肝了一上午的题,就是个大模拟,各种循环加花式判断处理出来一个数组然后输出对应的答案就好了,但是码量巨大(不过对JMY来说都是毛毛雨因为他是码王)按照限定条件做处理就好了,解释见代码注释吧

#include<bits/stdc++.h>
#define fa(a,b,c) for(int a=b;a<=c;a++)
#define fd(a,b,c) for(int a=b;a>=c;a--)//今天心血来潮的码风 
using namespace std;
int t,n;
int a[10010],num[110],sum[6]; 
/*
    a数组就是目标数组了,也就是按顺序存前10000个出现在图中的数字编号
    num数组表示每一圈最后一个数的编号(1~10000)
    sum数组来存每个数字在圈中出现的次数 
*/ 
int tot=2,f=7,k=0;
/*
    tot是当前圈数
    f是变化量,设所填数是第x个,则当前数不能等于第x-f个数(上一圈中与之相邻的数)
    f通过找规律可以确定
    k用来控制f的变化 
*/
int main()
{
    a[1]=1,a[2]=2,a[3]=3,a[4]=4,a[5]=5,a[6]=2,a[7]=3,a[8]=1,a[9]=4;
    sum[1]=2,sum[2]=2,sum[3]=2,sum[4]=2,sum[5]=1;
    num[1]=7;
    //把特殊情况赋好值
    fa(i,2,60)
    num[i]=num[i-1]+6*i;
    //预处理num数组
    fa(i,10,10000){//枚举 
        int minn=0x7fffffff;
        //选数时需要更新出现次数最小值,下皆同 
        if(i==num[tot]){
            f++;//变化 
            fd(j,5,1){//选数,为确保是最小值要从5开始递减到1 
                if(j==a[i-1])
                continue;
                if(j==a[num[tot-1]+1])
                continue;
                if(j==a[i-f])
                continue;
                if(sum[j]<=minn)
                minn=sum[j],a[i]=j;
            }
            sum[a[i]]++;
            continue;
        }
        if(i==num[tot]+1){//某圈的第一个数 
            fd(j,5,1){
                if(j==a[i-1])
                continue;
                if(j==a[i-f])
                continue;
                if(sum[j]<=minn)
                minn=sum[j],a[i]=j,k=1;
            }
            sum[a[i]]++;
            tot++;
            continue;
        }
        k++;
        if(k==tot){//k的变化与圈数有关系,第x圈需要x次变化 
            f++;
            k=0;
            fd(j,5,1){
                if(j==a[i-1])
                continue;
                if(j==a[i-f])
                continue;
                if(sum[j]<=minn)
                minn=sum[j],a[i]=j;
            }
            sum[a[i]]++;
            continue;
        }
        fd(j,5,1){//非特殊情况判断 
            if(j==a[i-1])
            continue;
            if(j==a[i-f])
            continue;
            if(j==a[i-f-1])
            continue;
            if(sum[j]<=minn)
            minn=sum[j],a[i]=j;
        }
        sum[a[i]]++;
    }
    scanf("%d",&t);
    while(t--){ 
        scanf("%d",&n);
        printf("%d\n",a[n]);//从已预处理过的数组中输出对应数 
    }
    return 0;
}

T2 数列

上午做的第一道题,就感觉题面没有什么花里胡哨的东西(而且可以暴力)就做了这道题,没错还是暴力别问我为什么因为我弱,所以只得了10分,正解是前缀和+数论,前段讲数论消化的不是很好所以有点难搞,在JMY大佬和ZX大佬一晚上的讲解后终于明白了,看我打个表先

这是样例的一个数据,通过找规律可以发现,前缀和数组里如果有两个数的差%k为0,那么这就是一个合法的区间,求两个数的差%k为0可以转化为求余数相等的两个前缀和,效果是一样的(大佬讲给我的我也没证明反正很对),那么就好办了,搞一个桶,统计下前缀和%k相等的数的个数,我们知道两个边界可以构成一个合法区间,根据排列组合的知识,方案总数ans计算公式应该为[(首项+末项)x项数]/2

需要特别注意的就是桶里面下标为0的数,如果一个数对k取模为0,那么它本身就是一个合法的子序列了,但通过排列组合计算出的只是有两个余数相等时的合法区间,没有计算取模为0的子区间的个数,所以最后的ans应该再加上s[0]。上代码

#include<bits/stdc++.h>
#define fa(a,b,c) for(int a=b;a<=c;a++)
using namespace std;
int a,tot;
int n,t,k;
long long ans;
int s[1000010];
int main()
{
    scanf("%d",&t);
    fa(i,1,t){
        ans=0,tot=0;
        memset(s,0,sizeof(s));
        scanf("%d%d",&k,&n);
        fa(i,1,n){
            scanf("%d",&a);
            tot=(tot+a)%k;
            s[tot]++;
        }
        for(int i=0;i<k;i++)
        if(s[i])
        ans+=(s[i]*(s[i]-1)/2);
        ans+=s[0];
        printf("%lld\n",ans);
    }
    return 0;
}