[NOIP模拟测试31]题解
A.math
考场乱搞拿了95,2333。
考虑裴蜀定理:$ax+by=z$存在整数解,当且仅当$gcd(a,b)|z$。
那么如果某个数能够被拼出来,就必须满足所有$a_i$的$gcd$是它的因子。直接枚举倍数即可。
//代码来自DeepinC #include<cstdio> int n,k,g,x; int gcd(int a,int b){return b?gcd(b,a%b):a;} int main(){ scanf("%d%d",&n,&k);g=k; while(n--)scanf("%d",&x),g=gcd(g,x); printf("%d\n",k/g); for(int i=0;i<k;i+=g)printf("%d ",i); }
B.biology
首先将$a[i][j]$离散化,值相同的方格坐标都放到一起。然后很容易得到一个暴力的dp:$f[i][j]=max{f[i'][j']+|i-i'|+|j-j'|}$,$i',j'$位于a值比$i,j$小的上一层。
最劣复杂度会达到$O(n^2m^2)$,考虑优化。可以把绝对值用分类讨论拆开,四个树状数组分别维护$f[i][j]-i-j,f[i][j]-i+j,f[i][j]+i-j,f[i][j]+i+j$的最大值,转移时先分别查询以$i,j$为中心的四块区域的最优值,用最大的更新$f[i][j]$,之后再分别把$f[i][j]$上传到树状数组里。注意要用前缀最大值查询四块的最优值来保证log的复杂度,所以需要把坐标反转一下。
正解其实就是把树状数组去掉……我们用树状数组维护四部分的最大值是为了保证在合法的情况下转移最优,但本题的特殊性使不合法的情况一定没有合法情况优(如果你绝对值去的不对就会让坐标差变成负的),所以直接用四个变量维护四部分的最大值即可。我把坐标转换了一下,即$(i,j)->(i+j,i-j)$,其实不转换也是可以的。
//80分树状数组 #include<cstdio> #include<iostream> #include<cstring> #include<vector> #include<algorithm> #define pa pair<int,int> #define QWQ dp[x][y]=max(dp[x][y],maxx) using namespace std; typedef long long ll; int read() { int x=0,f=1;char ch=getchar(); while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();} while(isdigit(ch))x=x*10+ch-'0',ch=getchar(); return x*f; } const int N=2005,M=1e6+5; int n,m; int a[N][N],b[N][N]; struct bit { ll c[N][N]; int lb(int x){return x&-x;} void update(int x,int y,ll val) { for(int i=x;i<=n;i+=lb(i)) for(int j=y;j<=m;j+=lb(j)) c[i][j]=max(val,c[i][j]); } ll query(int x,int y) { ll res=0; for(int i=x;i;i-=lb(i)) for(int j=y;j;j-=lb(j)) res=max(res,c[i][j]); return res; } }c1,c2,c3,c4; int app[M],key[M],cnt,num[M]; vector<pa> fl[M]; ll dp[N][N]; int main() { n=read();m=read(); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { a[i][j]=read(); if(!app[a[i][j]]&&a[i][j]) { app[a[i][j]]=1; num[++cnt]=a[i][j]; } } for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) b[i][j]=read(); sort(num+1,num+cnt+1); for(int i=1;i<=cnt;i++) key[num[i]]=i; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { if(!a[i][j])continue; fl[key[a[i][j]]].push_back(make_pair(i,j)); } int sz=fl[1].size(); for(int i=0;i<sz;i++) { int x=fl[1][i].first,y=fl[1][i].second; c1.update(x,y,b[x][y]-x-y); c2.update(x,m-y+1,b[x][y]-x+y); c3.update(n-x+1,y,b[x][y]+x-y); c4.update(n-x+1,m-y+1,b[x][y]+x+y); } for(int i=2;i<=cnt;i++) { sz=fl[i].size(); for(int j=0;j<sz;j++) { int x=fl[i][j].first,y=fl[i][j].second; ll maxx=0; maxx=c1.query(x,y)+x+y+b[x][y];QWQ; maxx=c2.query(x,m-y+1)+x-y+b[x][y];QWQ; maxx=c3.query(n-x+1,y)-x+y+b[x][y];QWQ; maxx=c4.query(n-x+1,m-y+1)-x-y+b[x][y];QWQ; //cout<<x<<' '<<y<<' '<<dp[x][y]<<endl; } for(int j=0;j<sz;j++) { int x=fl[i][j].first,y=fl[i][j].second; c1.update(x,y,dp[x][y]-x-y); c2.update(x,m-y+1,dp[x][y]-x+y); c3.update(n-x+1,y,dp[x][y]+x-y); c4.update(n-x+1,m-y+1,dp[x][y]+x+y); //cout<<x<<' '<<y<<' '<<dp[x][y]<<endl; } } sz=fl[cnt].size(); ll ans=0; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) ans=max(ans,dp[i][j]); cout<<ans<<endl; return 0; }
//AC #include<cstdio> #include<iostream> #include<cstring> #include<vector> #include<algorithm> #define pa pair<int,int> using namespace std; typedef long long ll; int read() { int x=0,f=1;char ch=getchar(); while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();} while(isdigit(ch))x=x*10+ch-'0',ch=getchar(); return x*f; } const int N=2005,M=1e6+5; int n,m; int a[N][N],b[N][N]; int app[M],key[M],cnt,num[M]; vector<pa> fl[M]; ll dp[N][N]; int getx(int i,int j){return (i+j)/2;} int gety(int i,int j){return (i-j)/2;} int main() { n=read();m=read(); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { a[i][j]=read(); if(!app[a[i][j]]&&a[i][j]) { app[a[i][j]]=1; num[++cnt]=a[i][j]; } } for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) b[i][j]=read(); sort(num+1,num+cnt+1); for(int i=1;i<=cnt;i++) key[num[i]]=i; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { if(!a[i][j])continue; fl[key[a[i][j]]].push_back(make_pair(i+j,i-j)); } ll c1=-0x3f3f3f3f,c2=-0x3f3f3f3f,c3=-0x3f3f3f3f,c4=-0x3f3f3f3f; int sz=fl[1].size(); for(int i=0;i<sz;i++) { int x=fl[1][i].first,y=fl[1][i].second,ox=getx(x,y),oy=gety(x,y); c1=max(c1,1LL*b[ox][oy]-x); c2=max(c2,1LL*b[ox][oy]+x); c3=max(c3,1LL*b[ox][oy]-y); c4=max(c4,1LL*b[ox][oy]+y); } for(int i=2;i<=cnt;i++) { sz=fl[i].size(); for(int j=0;j<sz;j++) { int x=fl[i][j].first,y=fl[i][j].second,ox=getx(x,y),oy=gety(x,y); ll maxx=max(max(c1+ox+oy,c2-ox-oy),max(c3+ox-oy,c4-ox+oy)); dp[ox][oy]=maxx+b[ox][oy]; } for(int j=0;j<sz;j++) { int x=fl[i][j].first,y=fl[i][j].second,ox=getx(x,y),oy=gety(x,y); c1=max(c1,dp[ox][oy]-x); c2=max(c2,dp[ox][oy]+x); c3=max(c3,dp[ox][oy]-y); c4=max(c4,dp[ox][oy]+y); } } ll ans=0; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) ans=max(ans,dp[i][j]); cout<<ans<<endl; return 0; }
C.english
对于每一个$i\in n$,求出$a[i]$作为最大值的范围$[l,r]$,可以用单调栈实现。
维护一个前缀和,$f[x][i]$表示1~x的区间里第i位是1的数有多少个。
用启发式合并的思路,对于每一个区间x,看它的左右儿子区间$[l,x-1]$和$[x+1,r]$哪个长度更大,以右儿子长度大为例:
枚举左区间的每一个数$a[i]$,并枚举它的每一位,如果这一位是0,那么对ans1的贡献就是$2^i \times (f[r][i]-f[x-1]) \times a[x]$。如果是1同理。
对ans2的贡献就是右区间中有多少个数$xor\ a[i]$后$>a[x]$,再乘上$a[x]$。区间问题用可持久化Trie解决。
#include<cstdio> #include<iostream> #include<cstring> #include<stack> #include<algorithm> #include<map> //#define int long long using namespace std; int read() { int x=0,f=1;char ch=getchar(); while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();} while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();} return x*f; } typedef long long ll; const int N=1e5+5; const ll mod=1e9+7; int n,a[N],op; int le[N],re[N]; ll f[N][22]; stack<int> s; ll ans1,ans2; namespace Trie { int ch[N*22][2],size[N*22],type,root[N]; void ins(int pre,int &now,int i,ll x) { now=++type; size[now]=size[pre]; if(i<0){size[now]++;return ;} int d=(x>>i)&1; ch[now][d^1]=ch[pre][d^1]; ins(ch[pre][d],ch[now][d],i-1,x); size[now]=size[ch[now][0]]+size[ch[now][1]]; return ; } void ins(ll x,int pos){ins(root[pos-1],root[pos],20,x);} ll query(int x,int y,int pos) { int p=root[pos],res=0; for(int i=20;i>=0;i--) { int a=(x>>i)&1,b=(y>>i)&1; if(b==0) { res+=size[ch[p][a^1]]; p=ch[p][a]; } else p=ch[p][a^1]; if(!p)break; } return res; } } void cacl(int k,int l,int r) { if(k-l<r-k) { for(int j=l;j<=k;j++) { (ans1+=1LL*a[j]*a[k]%mod*(r-k+1)%mod)%=mod; for(int i=20;i>=0;i--) { int now=(a[j]>>i)&1; if(!now)(ans1+=1LL*(1<<i)*(f[r][i]-f[k-1][i])%mod*a[k]%mod)%=mod; else (ans1-=1LL*(1<<i)*(f[r][i]-f[k-1][i])%mod*a[k]%mod)%=mod,ans1=(ans1+mod)%mod; } ans2=(ans2+1LL*(Trie::query(a[j],a[k],r)-Trie::query(a[j],a[k],k-1)+mod)%mod*a[k]%mod)%mod; } } else { for(int j=k;j<=r;j++) { (ans1+=1LL*a[j]*a[k]%mod*(k-l+1)%mod)%=mod; for(int i=20;i>=0;i--) { int now=(a[j]>>i)&1; if(!now)(ans1+=1LL*(1<<i)*(f[k][i]-f[l-1][i])%mod*a[k]%mod)%=mod; else (ans1-=1LL*(1<<i)*(f[k][i]-f[l-1][i])%mod*a[k]%mod)%=mod,ans1=(ans1+mod)%mod; } ans2=(ans2+1LL*(Trie::query(a[j],a[k],k)-Trie::query(a[j],a[k],l-1)+mod)%mod*a[k]%mod)%mod; } } } signed main() { n=read();op=read(); for(int i=1;i<=n;i++) a[i]=read(),Trie::ins(a[i],i); for(int i=1;i<=n;i++) { for(int j=0;j<=20;j++) { f[i][j]+=f[i-1][j]; if((a[i]>>j)&1)f[i][j]++; } } for(int i=1;i<=n;i++) { while(!s.empty()&&a[i]>a[s.top()])s.pop(); le[i]=s.empty()?1:s.top()+1; s.push(i); } while(!s.empty())s.pop(); for(int i=n;i;i--) { while(!s.empty()&&a[i]>=a[s.top()])s.pop(); re[i]=s.empty()?n:s.top()-1; s.push(i); } for(int i=1;i<=n;i++) cacl(i,le[i],re[i]); if(op==1||op==3)cout<<ans1<<endl; if(op==2||op==3)cout<<ans2<<endl; /*for(int i=1;i<=n;i++) cout<<i<<' '<<le[i]<<' '<<re[i]<<endl;*/ return 0; } /* g++ -std=c++11 1.cpp -o 1 ./1 */