省选模拟(20220313)

第一题,看了两个小时之后发现,有单调性!!!然后就切掉了

第二题,用欧拉判别,是否含有k次剩余,这个是结论,我不会呜呜呜

第三题,提取有用的键值,无用的不需要维护!!!

T1 跑步

首先暴力就是直接每次dp一遍,把所有的dp值加起来就好了

如果有一个小优化就是每次从更改的地方开始dp,这样应该会快一些的

然而我们发现每一行改掉的左端点和右端点都是单调右移的

以为dp不能向左和向上转移,于是就单调了!!

可以用树状数组维护每一个点的dp值,动态的维护答案就好了

还有这个题可以快速修改的一个原因是,每次更改只会变化1哦

如果每次变化的多一些,单调性还是有的,但是每一个点的变化量就不一定了!!

AC_code
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
int read(){
    int s=0,t=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')t=-1;ch=getchar();}
    while(isdigit(ch)){s=(s<<1)+(s<<3)+(ch^48);ch=getchar();}
    return s*t;
}
const int N=2005;
int n,a[N][N];
ll dp[N][N],ans;
struct BIT{
    ll tr1[N];
    void insert(int x,int v){
        for(int i=x;i<=n;i+=(i&-i))tr1[i]+=v;
    }
    void ins(int l,int r,int v){
        insert(l,v);insert(r+1,-v);
    }
    ll query(int x){
        ll ret=0;
        for(int i=x;i;i-=(i&-i))ret+=tr1[i];
        return ret;
    }
    int qry(int x){
        return query(x);
    }
}bit[N];
bool jud(int x,int y){
    return (x<=n&&x>0&&y<=n&&y>0);
}
signed main(){
    freopen("run.in","r",stdin);
    freopen("run.out","w",stdout);
    // cerr<<(sizeof(bit)+sizeof(dp)+sizeof(a)>>20)<<endl;
    n=read();
    fo(i,1,n)fo(j,1,n)a[i][j]=read();
    ans=dp[1][1]=a[1][1];
    bit[1].ins(1,1,dp[1][1]);
    fo(i,1,n)fo(j,1,n){
        if(i==1&&j==1)continue;
        if(jud(i-1,j))dp[i][j]=max(dp[i][j],dp[i-1][j]+a[i][j]);
        if(jud(i,j-1))dp[i][j]=max(dp[i][j],dp[i][j-1]+a[i][j]);
        ans+=dp[i][j];
        bit[i].ins(j,j,dp[i][j]);
    }
    printf("%lld\n",ans);
    fo(nn,1,n){
        char tp[5];scanf("%s",tp+1);
        int x=read(),y=read(),l=y,r=y+1;
        if(tp[1]=='U'){a[x][y]++;
            fo(i,x,n){
                while(bit[i-1].qry(r)<bit[i].qry(r-1)+1&&r<=n)r++;
                if(i!=x)while(bit[i-1].qry(l)<=bit[i].qry(l-1)&&l<=n)l++;
                if(l>=r)break;
                ans+=(r-l);bit[i].ins(l,r-1,1);
            }
        }
        else {a[x][y]--;
            fo(i,x,n){
                while(bit[i-1].qry(r)<=bit[i].qry(r-1)-1&&r<=n)r++;
                if(i!=x)while(bit[i-1].qry(l)<bit[i].qry(l-1)&&l<=n)l++;
                if(l>=r)break;
                ans-=(r-l);bit[i].ins(l,r-1,-1);
            }
        }
        printf("%lld\n",ans);
    }
    return 0;
}

T2 算术

这样的话,我们不可能手动开方吧??!要是可以的话,您自己试试看吧,我撤了

那么我们只能考虑取模意义下了,那就是k次剩余呗!

我们多选几个质数,形式是p=ak+1这样的,a任意

那么我们把n对这个p取模,\(n^{\frac{p-1}{k}} \equiv 1\)

这个可以证明的,首先我们的p是个质数,设x的k次方同余于n,那么\(x^{p-1} \equiv 1\)

我们还有\(x^k \equiv n\),于是可以带入一下,就是\(x^{ak} \equiv 1\),就是\(n^{\frac{p-1}{k}} \equiv 1\)

于是我们只要找几个质数判断一下就好了,对了n不能是这个数的倍数,要不然就全是0了

AC_code
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
int read(){
    int s=0,t=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')t=-1;ch=getchar();}
    while(isdigit(ch)){s=(s<<1)+(s<<3)+(ch^48);ch=getchar();}
    return s*t;
}
const int N=1e6+5;
int T,k,n,len;char s[N];
int ksm(int x,int y,int mod){
    int ret=1;
    while(y){
        if(y&1)ret=ret*x%mod;
        x=x*x%mod;y>>=1;
    }return ret;
}
bool isp(int x){
    if(ksm(2,x-1,x)!=1)return false;
    if(ksm(3,x-1,x)!=1)return false;
    if(ksm(5,x-1,x)!=1)return false;
    if(ksm(7,x-1,x)!=1)return false;
    return true;
}
signed main(){
    freopen("math.in","r",stdin);
    freopen("math.out","w",stdout);
    T=read();
    fo(t,1,T){
        scanf("%s",s+1);len=strlen(s+1);k=read();
        bool fl=true;//cerr<<"ZZ"<<endl;
        for(int i=k+1,c=0;c<9;i+=k){
            // cerr<<i<<" "<<isp(i)<<endl;
            if(isp(i)){
                c++;n=0;
                fo(j,1,len)n=(n*10+s[j]-'0')%i;
                if(n==0){c--;continue;}
                // cerr<<k<<endl;
                if(ksm(n,(i-1)/k,i)!=1){fl=false;printf("N\n");break;}
            }
        }
        if(fl)printf("Y\n");
    }
    return 0;
}

T3 求和

我们可以把答案集中到最大值身上,也就是说如果这个数两侧的值都比它小,那么我们就在这里统计答案,修改的时候也只修改这几个位置就好了

用堆,每次修改当前值,左边最大值的值,右边最大值的值,能修改就改了

AC_code
#include<bits/stdc++.h>
using namespace std;
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
#define getchar getchar_unlocked
int read(){
    int s=0,t=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')t=-1;ch=getchar();}
    while(isdigit(ch)){s=(s<<1)+(s<<3)+(ch^48);ch=getchar();}
    return s*t;
}
const int N=1e6+5;
int n,k,Q,op,a[N],ans;
struct XDS{
    #define ls x<<1
    #define rs x<<1|1
    int mx[N*4],ps[N*4];
    inline void pushup(int x){
        mx[x]=max(mx[ls],mx[rs]);
        if(mx[x]==mx[ls])ps[x]=ps[ls];
        else ps[x]=ps[rs];
    }
    void build(int x,int l,int r){
        if(l==r)return mx[x]=a[l],ps[x]=l,void();
        int mid=l+r>>1;
        build(ls,l,mid);build(rs,mid+1,r);
        pushup(x);return ;
    }
    void ins(int x,int l,int r,int pos,int v){
        if(l==r)return mx[x]=v,void();
        int mid=l+r>>1;
        if(pos<=mid)ins(ls,l,mid,pos,v);
        else ins(rs,mid+1,r,pos,v);
        pushup(x);return ;
    }
    int query(int x,int l,int r,int ql,int qr){
        if(ql>n||qr<=0||ql>qr)return 0;
        if(ql<=l&&r<=qr)return ps[x];
        int mid=l+r>>1,ret,res;
        if(ql>mid)return query(rs,mid+1,r,ql,qr);
        if(qr<=mid)return query(ls,l,mid,ql,qr);
        ret=query(ls,l,mid,ql,mid);
        res=query(rs,mid+1,r,mid+1,qr);
        return a[ret]>=a[res]?ret:res;
    }
    #undef ls
    #undef rs
}xds;
priority_queue<int> q1,q2;
int jl[N];
void ins(int x){
    if(!x)return ;
    int le=xds.query(1,1,n,max(x-k,1),x-1);
    int ri=xds.query(1,1,n,x+1,min(x+k,n));
    if(a[le]<a[x]&&a[ri]<=a[x])jl[x]=max(a[le],a[ri])+a[x],q1.push(jl[x]);
}
void ers(int x){
    if(!jl[x])return ;q2.push(jl[x]);jl[x]=0;
}
signed main(){
    freopen("sum.in","r",stdin);
    freopen("sum.out","w",stdout);
    n=read();k=read();Q=read();op=read();
    fo(i,1,n)a[i]=read();
    xds.build(1,1,n);fo(i,1,n)ins(i);
    ans=q1.top();printf("%d\n",ans);
    while(Q--){
        int x=(read()^(ans*op)),y=(read()^(ans*op));
        int l=xds.query(1,1,n,max(x-k,1),x-1);
        int r=xds.query(1,1,n,x+1,min(x+k,n));
        ers(l);ers(r);ers(x);
        a[x]=y;xds.ins(1,1,n,x,y);
        ins(l);ins(r);ins(x);
        while(!q1.empty()&&!q2.empty()&&q1.top()==q2.top())q1.pop(),q2.pop();
        ans=q1.top();printf("%d\n",ans);
    }
    return 0;
}

虽然正解很妙,但是有一个Max_QAQ的乱搞对我的启发很大

首先我们对修改分类,如果改的是当前答案的数,如果改大了,直接给答案加上,如果改小了那么从新查一遍

如果改的不是答案的数,那他是有可能成为答案的,查一下两侧的最大值,和答案比较一下就行了

整体查答案的时候,我们仍然是在最大值处统计,枚举最大值,然后更新答案,删掉最大值,枚举下一个最大值

这样直到最大值小于答案的一半之后就不用查了,直接停就行了,最后把删掉的都插回来

可以在数据非常水的情况下切掉,要hack的话,就可以一个1e9,k个1,然后1个6e8和k个1,1个6e8和k个1,1个6e8和k个1

每次让那个1e9减少一个,这样每次都要重新查一遍,每次都会查满,就被卡死了......

乱搞
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
int read(){
    int s=0,t=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')t=-1;ch=getchar();}
    while(isdigit(ch)){s=(s<<1)+(s<<3)+(ch^48);ch=getchar();}
    return s*t;
}
const int N=1e6+5;
const int inf=0x3f3f3f3f3f3f3f3f;
int n,k,Q,op,a[N],ans,ps1,ps2;
struct XDS{
    #define ls x<<1
    #define rs x<<1|1
    int mx[N*4],ps[N*4];
    void pushup(int x){
        mx[x]=max(mx[ls],mx[rs]);
        if(mx[x]==mx[ls])ps[x]=ps[ls];
        else ps[x]=ps[rs];
        return ;
    }
    void build(int x,int l,int r){
        if(l==r)return mx[x]=a[l],ps[x]=l,void();
        int mid=l+r>>1;
        build(ls,l,mid);build(rs,mid+1,r);
        pushup(x);return ;
    }
    void ins(int x,int l,int r,int pos,int v){
        if(l==r)return mx[x]=v,void();
        int mid=l+r>>1;
        if(pos<=mid)ins(ls,l,mid,pos,v);
        else ins(rs,mid+1,r,pos,v);
        pushup(x);
    }
    int query(int x,int l,int r,int ql,int qr){
        if(qr<=0||ql>n)return 0;
        if(ql>qr)return 0;
        if(ql<=l&&r<=qr)return ps[x];
        int mid=l+r>>1,ret=0,res;
        if(ql<=mid){
            res=query(ls,l,mid,ql,qr);
            if(a[ret]<a[res])ret=res;
        }
        if(qr>mid){
            res=query(rs,mid+1,r,ql,qr);
            if(a[ret]<a[res])ret=res;
        }
        return ret;
    }
    int qry(int x){
        int ps1=query(1,1,n,max(x-k,1ll),x-1);
        int ps2=query(1,1,n,x+1,min(x+k,n));
        if(a[ps1]<a[ps2])return ps2;
        else return ps1;
    }
    #undef ls
    #undef rs
}xds;
struct node{
    int val,pos;
    node(){}node(int a,int b){val=a;pos=b;}
    bool operator < (node a)const{
        if(val!=a.val)return val>a.val;
        return pos<a.pos;
    }
};
int sta[N],top;
void query(){ans=0;
    fo(i,1,n){
        int pos=xds.query(1,1,n,1,n);if(a[pos]*2<=ans)break;
        sta[++top]=pos;xds.ins(1,1,n,pos,-inf);
        int pes=xds.qry(pos);
        if(ans<a[pos]+a[pes])ans=a[pos]+a[pes],ps1=pos,ps2=pes;
    }
    while(top)xds.ins(1,1,n,sta[top],a[sta[top]]),top--;
}
signed main(){
    freopen("sum.in","r",stdin);
    freopen("sum.out","w",stdout);
    n=read();k=read();Q=read();op=read();
    fo(i,1,n){
        a[i]=read();
    }xds.build(1,1,n);
    query();
    printf("%lld\n",ans);
    fo(q,1,Q){
        int x=(read()^(ans*op)),y=(read()^(ans*op));
        if(x==ps1||x==ps2){
            if(a[x]<y){
                ans+=y-a[x];
                a[x]=y;xds.ins(1,1,n,x,y);
            }
            else {
                a[x]=y;xds.ins(1,1,n,x,y);
                query();
            }
        }
        else {
            if(a[x]<y){
                int pos=xds.qry(x);
                if(ans<y+a[pos])ans=y+a[pos],ps1=x,ps2=pos;
            }
            a[x]=y;xds.ins(1,1,n,x,y);
        }
        printf("%lld\n",ans);
    }
    return 0;
}
posted @ 2022-03-13 21:30  fengwu2005  阅读(42)  评论(0编辑  收藏  举报