大一暑假集训最终考试(一)(2019.8.31)

D题 nefu 1866 这是一道难题

思路:树状数组模板题。还有就是树状数组的单点修改只支持加减,不支持直接修改(修改时先减去原来的数,再加上修改后的数即可)。

坑的地方:没说查询时一定满足x<y!

AC代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+10,mod=1e9+7;
ll tr[N];
int n,m,a[N],x,y,opt;
void add(int i,int k)
{
    while(i<=n)
    {
        tr[i]+=k;
        i+=(i&-i);
    }
}
ll sum(int i)
{
    ll s=0;
    while(i)
    {
        s+=tr[i]%mod;
        i-=(i&-i);
    }
    return s%mod;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        add(i,a[i]);
    }
    while(m--)
    {
        scanf("%d%d%d",&opt,&x,&y);
        if(opt==0)
        {
            add(x,-a[x]);//先减去原来的数
            add(x,y);//再加上现在的数
            a[x]=y;//修改原数组
        }
        else
        {
            if(x>y)swap(x,y);//巨坑!!!这个地方wa了4次!!!
            printf("%lld\n",sum(y)-sum(x-1));
        }
    }
    return 0;
}

B题 nefu 1878 Alice和Bob的分组游戏(一)

思路:sg函数打表。

AC代码:

#include <bits/stdc++.h>
using namespace std;
const int N=1e4+10;
typedef long long ll;
bool vis[N];
int n,m,x,t;
ll sg[N],sum;
void get_sg()
{
    memset(sg,0,sizeof(sg));
    for(int i=2;i<=N;i++)//有i个糖果
    {
        memset(vis,0,sizeof(vis));
        for(int j=2;j<=min(i,m);j++)//分成j堆
        {
            if(i%j==0)
            {
                t=i/j;//每堆t个
                if(j&1)vis[sg[t]]=1;//奇数堆,后继状态的sg值为sg[t]
                else vis[0]=1;//偶数堆,后继状态的sg值为0
            }
        }
        for(int j=0;;j++)
            if(vis[j]==0){sg[i]=j;break;}
    }
}
int main()
{
    while(scanf("%d%d",&n,&m)!=-1)//没有多组输入wa了一发,气死我了!
    {
        get_sg();sum=0;
        for(int i=1;i<=n;i++)
        {
            cin>>x;
            sum^=sg[x];
        }
        if(sum)printf("Alice\n");
        else printf("Bob\n");
    }
    return 0;
}

E题 nefu 1870 这是一道签到题

思路:找规律,异或值显然与[x,y]区间长度的奇偶有关。查询时用线段树维护。

设d=y-x+1,当d为奇数时,说明x、y均为奇数或者x、y均为偶数,此时F(x,y)=a[x]^a[x+2]^…^a[y-2]^a[y](比如区间[1,5],F(1,5)=a[1]^a[3]^a[5])
当d为偶数时,F(x,y)=0恒成立。

对于d是奇数的情况,我们可以用两个线段树分别维护x、y均为奇数以及x、y均为偶数时的区间异或值。

还有,这根本就不是签到题难度啊啊啊啊!

AC代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+10;
ll n,d,m,x,y,opt,cnt1,cnt2,a1[N],a2[N],tre1[4*N],tre2[4*N];
void pushup(ll tr[],ll i)
{tr[i]=tr[2*i]^tr[2*i+1];}
void build(ll tr[],ll a[],ll i,ll l,ll r)
{
    if(l==r){tr[i]=a[l];return;}
    ll mid=l+r>>1;
    build(tr,a,2*i,l,mid);
    build(tr,a,2*i+1,mid+1,r);
    pushup(tr,i);
}
void update(ll tr[],ll i,ll l,ll r,ll x,ll y)
{
    if(x>r||x<l)return;
    if(l==r&&l==x){tr[i]=y;return;}
    ll mid=l+r>>1;
    update(tr,2*i,l,mid,x,y);
    update(tr,2*i+1,mid+1,r,x,y);
    pushup(tr,i);
}
ll query(ll tr[],ll i,ll l,ll r,ll x,ll y)
{
    if(l>y||r<x)return 0;
    if(l>=x&&r<=y)return tr[i];
    int mid=l+r>>1;
    return query(tr,2*i,l,mid,x,y)^query(tr,2*i+1,mid+1,r,x,y);
}
int main()
{
    ios::sync_with_stdio(false);
    while(cin>>n>>m)
    {
        cnt1=cnt2=0;
        for(ll i=1;i<=n;i++)
        {
            cin>>x;
            if(i&1)a1[++cnt1]=x;//a1保存下标为奇数的值(方便之后查询[x,y],x、y均为奇数的情况)
            else a2[++cnt2]=x;//a2保存下标为偶数的值(方便之后查询[x,y],x、y均为偶数的情况)
        }
        build(tre1,a1,1,1,cnt1);//tre1保存a1数组的区间异或值
        build(tre2,a2,1,1,cnt2);//tre2保存a2数组的区间异或值
        for(ll i=1;i<=m;i++)
        {
            cin>>opt>>x>>y;
            if(opt==0)
            {
                if(x&1)update(tre1,1,1,cnt1,(x+1)/2,y);
                else update(tre2,1,1,cnt2,x/2,y);
            }
            else
            {
                if(x>y)swap(x,y);
                d=y-x+1;
                if(d%2==0)printf("0\n");
                else
                {
                    if(x&1)printf("%lld\n",query(tre1,1,1,cnt1,(x+1)/2,(y+1)/2));
                    else printf("%lld\n",query(tre2,1,1,cnt2,x/2,y/2));
                }
            }
        }
    }
    return 0;
}

C题 nefu 1867 why的考号

思路:根据递推方程构造矩阵,再用矩阵快速幂。

递推方程(n>=3):f[n]=2*f[n-2]+f[n-1]+n3

设矩阵相乘等式为A*B=C,难点就是怎么去构造A矩阵。

首先,不能像我这样构造:在这里插入图片描述
我们要想办法把(n+1)3用B矩阵的元素表示出来,但是这样A矩阵有一个元素[(n+1)/n]3显然是不行的(因为A矩阵中有元素[(n+1)/n]3,再快速幂误差很大)

正确做法是把(n+1)3拆开,(n+1)3=n3+3*n2+3*n+1,要想(n+1)3用B矩阵的元素表示出来,需要增添B矩阵的元素!在B矩阵下方再加三个数:n2、n、1。

那么A*B=C就变成了这样:
9

最后再处理一下:
在这里插入图片描述
OK,数学公式推导到此结束。把上面的那个最终公式推出来,代码就很好写了。

AC代码:

#include <bits/stdc++.h>
using namespace std;
const int mod=123456789;
typedef long long ll;
ll n,t,cas,ans;
struct node
{
    ll m[6][6];
};
node s,
A={
0,2,0,0,0,0,
1,1,1,0,0,0,
0,0,1,3,3,1,
0,0,0,1,2,1,
0,0,0,0,1,1,
0,0,0,0,0,1};
node mul(node x,node y)//两矩阵x、y相乘
{
    node s;
    memset(s.m,0,sizeof(s.m));
    for(int i=0;i<6;i++)
        for(int j=0;j<6;j++)
            for(int k=0;k<6;k++)
                s.m[i][j]+=x.m[i][k]*y.m[k][j]%mod;
    return s;
}
node quickpow(node a,ll b)//矩阵快速幂,求矩阵a的n次方
{
    memset(s.m,0,sizeof(s.m));
    for(int i=0;i<6;i++)
        s.m[i][i]=1;//s初始化为单位矩阵
    while(b)
    {
        if(b&1){b--;s=mul(s,a);}
        a=mul(a,a);b=b/2;
    }
    return s;
}
int main()
{
    ios::sync_with_stdio(false);
    cin>>t;
    while(t--)
    {
        cin>>n;
        printf("Case %d: ",++cas);
        if(n==1){printf("000000001\n");continue;}
        s=quickpow(A,n-2);//n>=2
        ans=s.m[1][0]*2%mod+s.m[1][1]*2%mod+s.m[1][2]*27%mod+s.m[1][3]*9%mod+s.m[1][4]*3%mod+s.m[1][5]%mod;
        printf("%09lld\n",ans%mod);
    }
    return 0;
}

【未完待续。。。】

发布了72 篇原创文章 · 获赞 91 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/ljw_study_in_CSDN/article/details/100180434