省选模拟(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;
}