bzoj5月月赛订正
已完成2/9(要准备中考啊QwQ)
T1
考虑对所有数分解质因数,其中因子>sqrt(100000)的因子最多有一个,于是我们可以暴力维护<sqrt(100000)的因子个数的前缀和。
剩下的就是判区间里一个数出现的次数。我写了主席树。。。
code
#include <bits/stdc++.h> using namespace std; int tot,i,j,k,n,m,x,y,t,cas,prime1[70],prime2[100001],b[100001],num1[67],num2[67],tt,c[100001],s[100001][67]; inline int read(){ int x=0,f=1; char ch=getchar(); while (ch<'0'||ch>'9'){f=ch=='-'?-f:f;ch=getchar();} while (ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();} return x*f; } inline void pre(){ tot=0; for (register int i=2;i<=100000;i++) if (!b[i]){ b[i]=1;prime2[i]=++tot; if (i<317)prime1[tot]=i,tt=tot; for (register int j=i;j<=100000;j+=i)b[j]=1; } } int rt[200001],l[3600001],r[3600001],size[3600001],num; inline void Build(int &rt,int L,int R){if (!rt)rt=++num;if (L==R)return;Build(l[rt],L,L+R>>1);Build(r[rt],(L+R>>1)+1,R);} inline void Insert(int &rt,int la,int L,int R,int v){ if (!rt)rt=++num; if (L==R){size[rt]=size[la]+1;return;} l[rt]=l[la];r[rt]=r[la]; int mid=L+R>>1; if (v<=mid){l[rt]=0;Insert(l[rt],l[la],L,mid,v);}else {r[rt]=0;Insert(r[rt],r[la],mid+1,R,v);} } inline int calc(int rt,int L,int R,int x){ if (!rt)return 0; if (L==R)return size[rt];int mid=L+R>>1; if (x<=mid)return calc(l[rt],L,mid,x);else return calc(r[rt],mid+1,R,x); } int V[200001]; int main(){ cas=read();pre(); while (cas--){ n=read();m=read(); for (register int i=1;i<=n;i++){ x=read();y=x;V[i]=x; for (register int j=1;j<=tt;j++)s[i][j]=s[i-1][j]; for (register int j=1;j<=tt;j++){while (x%prime1[j]==0)s[i][j]++,x/=prime1[j];if (x==1)break;} c[i]=x; } memset(rt,0,sizeof rt); memset(l,0,sizeof l); memset(r,0,sizeof r); memset(size,0,sizeof size); num=0;Build(rt[0],1,tot); for (register int i=1;i<=n;i++)Insert(rt[i],rt[i-1],1,tot,prime2[c[i]]); while (m--){ int L=read(),R=read();x=read(); memset(num1,0,sizeof num1); memset(num2,0,sizeof num2); for (register int j=1;j<=tt;j++){while (x%prime1[j]==0)x/=prime1[j],num2[j]++;if (x==1)break;} for (register int j=1;j<=tt;j++)num1[j]=s[R][j]-s[L-1][j]; bool bo=1; for (register int j=1;j<=tt;j++)if (num1[j]<num2[j]){puts("No");bo=0;break;} if (!bo)continue; if (x==1){puts("Yes");continue;} if (calc(rt[R],1,tot,prime2[x])-calc(rt[L-1],1,tot,prime2[x])<1)puts("No");else puts("Yes"); } } return 0; }
T2
考虑DP,f[i][j][x][y]表示走到i,j,并且路径上有x个没选,并在前i-1行以及第i行前j-1个里选了y个的最优值。ans显然等于max(f[n][m][i][i])0<=i<=t
然后转移。
f[i][j][x][y]可以直接转移到f[i][j+1][x][y]以及f[i][j+1][x+1][y](i,j+1不选)
再考虑往下转移,(i+1,j)也可以选或不选,然后,再在(i,j+1)~(i,m)以及(i+1,1)~(i+1,j-1)中选最大的k个,转移给f[i+1][j][x+(1 or 0)][y+k]
code
#pragma GCC optimize(2) #include <bits/stdc++.h> #define RI register int using namespace std; typedef long long ll; int i,j,k,n,m,x,y,t,T,b[51][51][51]; ll f[51][51][21][21],a[51][51]; int read(){ int x=0,f=1; char ch=getchar(); while (ch<'0'||ch>'9'){f=ch=='-'?-f:f;ch=getchar();} while (ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();} return x*f; } inline int max(int x,int y){return x>y?x:y;} int main(){ T=read(); while (T--){ n=read();m=read();x=read(); for (RI i=1;i<=n;i++)for (RI j=1;j<=m;j++)scanf("%lld",&a[i][j]);; memset(b,0,sizeof b); for (RI i=1;i<n;i++) for (RI j=1;j<=m;j++){ for (RI k=j+1;k<=m;k++)b[i][j][++b[i][j][0]]=a[i][k]; for (RI k=1;k<j;k++)b[i][j][++b[i][j][0]]=a[i+1][k]; sort(b[i][j]+1,b[i][j]+1+b[i][j][0]); } memset(f,-1,sizeof f); f[1][1][0][0]=a[1][1];f[1][1][1][0]=0; for (RI i=1;i<=n;i++) for (RI j=1;j<=m;j++) for (RI k=0;k<=x;k++) for (RI t=0;t<=x;t++) if (f[i][j][k][t]>-1){ if (j<=m){ f[i][j+1][k][t]=max(f[i][j+1][k][t],f[i][j][k][t]+a[i][j+1]); if (k<x)f[i][j+1][k+1][t]=max(f[i][j+1][k+1][t],f[i][j][k][t]); } if (i<n){ ll p=0; for (RI h1=0;h1+t<=x;h1++){ p+=b[i][j][b[i][j][0]-h1+1]; f[i+1][j][k][t+h1]=max(f[i+1][j][k][t+h1],f[i][j][k][t]+p+a[i+1][j]); if (k<x){f[i+1][j][k+1][t+h1]=max(f[i+1][j][k+1][t+h1],f[i][j][k][t]+p);} } } } ll ans=0;for (RI i=0;i<=x;i++)ans=max(ans,f[n][m][i][i]); printf("%lld\n",ans); } return 0; }