正睿NOIP贈送附加賽1
T1:math
題目連結:
http://zhengruioi.com/contest/156/problem/471
題解:
先講講我的亂搞做法。對於前面70%,我跑了揹包。因為揹包有後效性...我做了兩次,也就是迭代了一下
剩下的30%隨機化了一波。就是先把每個數的20以內的倍數暴力的算出來對k取模然後丟到一個大小為k的桶裡面去。因為題目就是讓你給每個數一個係數,於是我就每次隨機兩個位置相加判斷在模k的意義下是否出現過,如果沒有出現過就加入答案中,咳咳重複1e7次即可A掉本題
下面說說題解做法:
$ax+by=z$存在整數解,當且僅當$gcd(a, b)∣z$。
那麼,若z可以被湊出,即 $\sum_{i=1}^{n} x_ia_i = z$,當且僅當 $gcd(a_1, a_2,⋯, a_n)∣z$。
因此,答案只能是gcd的整數倍。
但是,這樣考慮x有可能是負數,但是在mod k的條件下,我們可以把x調為非負整數。
時間複雜度$O((n + k)logv)$。
亂搞程式碼
#include<algorithm> #include<cstring> #include<cstdio> #include<iostream> #include<vector> #include<time.h> using namespace std; const int N=1e6+15;View Codeint n,k; int a[N],f[N]; inline int read(){ char ch=getchar();int s=0,f=1; while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();} while (ch>='0'&&ch<='9') {s=(s<<3)+(s<<1)+ch-'0';ch=getchar();} return s*f; } namespace task1 { void main() {for (int i=1;i<=n;i++) for (int j=0;!f[1ll*j*a[i]%k];j++) f[1ll*j*a[i]%k]=1; for (int i=1;i<=n;i++) { for (int j=0;j<=k;j++) f[j]|=f[((j-a[i])%k+k)%k]; for (int j=0;j<=k;j++) f[j]|=f[((j-a[i])%k+k)%k]; } int s=0; for (int j=0;j<k;j++) s+=f[j]; printf("%d\n",s); for (int j=0;j<k;j++) if (f[j]) printf("%d ",j); } } int gcd(int a,int b) {if (!b) return a;else return gcd(b,a%b);} namespace task2 { void main() { printf("%d\n",k); for (int i=0;i<k;i++) printf("%d ",i); } } int main() { n=read();k=read(); for (int i=1;i<=n;i++) a[i]=read()%k; for (int i=1;i<=n;i++) if (a[i]==1||gcd(a[i],k)==1) {task2::main();return 0;} if (n<=1000) {task1::main();return 0;} srand(time(0)); vector <int> p; for (int i=1;i<=n;i++) { for (int j=0;j<=20;j++) { int q=1ll*j*a[i]%k; if (!f[q]) { p.push_back(q); f[q]=1; } } } for (int i=1;i<=1e7;i++) { int si=p.size(); int l=rand()%si,r=rand()%si; if (!f[(p[l]+p[r])%k]) { p.push_back((p[l]+p[r])%k); f[(p[l]+p[r])%k]=1; } } int s=0; for (int j=0;j<k;j++) s+=f[j]; printf("%d\n",s); for (int j=0;j<k;j++) if (f[j]) printf("%d ",j); return 0; }
T2:biology
題目連結:
http://zhengruioi.com/contest/156/problem/472
題解:
我們顯然可以把元素按$a$排序,然後寬搜轉移。
$f_{x,y}$ 表示當前路徑的結尾在$(x,y)$位置的最大吸引度之和。 $f_{x,y} = b_{x,y}+max (f_{z,k} + ∣x −z ∣ + ∣y −k∣, a_{x,y} >a_{z,k} )$
暴力轉移時間複雜度最差為$O(n ^2 m^2)$,考慮優化。
座標轉化$(x,y)->(x+y,x-y)$
這樣原來的曼哈頓距離$|x-z|+|y-k|$就變成了切比雪夫距離$max(|x-z|,|y-k|)=max(x-z,z-x,y-k,k-y)$
這樣座標是最大值,轉移也是最大值,因此可以用4個變數分別記錄最大的$f_{z,k}-z,f_{z,k}+z,f_{z,k}-k,f_{z,k}+k$
時間複雜度為排序複雜度$O(nmlognm)$
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> #include<queue> #include<cmath> using namespace std; typedef long long ll; const int N=2e3+15; const ll inf=1e9; int n,m,tot; ll A,B,C,D; ll b[N][N],dp[N][N]; inline ll read(){ char ch=getchar();ll s=0,f=1; while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();} while (ch>='0'&&ch<='9') {s=(s<<3)+(s<<1)+ch-'0';ch=getchar();} return s*f; } struct node{ int x,y; int d; }s[N*N]; bool operator < (node x,node y) {return x.d<y.d;} void chkmx(ll &a,ll b) {if (b>a) a=b;} void calc() { int l=1;ll mx=0; A=-inf;B=-inf;C=-inf;D=-inf; while (l<=tot) { int r=l; while (r<tot&&s[r].d==s[r+1].d) ++r; for (int k=l;k<=r;k++) { int x=s[k].x,y=s[k].y; int i=x+y,j=x-y; dp[x][y]=b[x][y]; chkmx(dp[x][y],b[x][y]+A+i); chkmx(dp[x][y],b[x][y]+B-i); chkmx(dp[x][y],b[x][y]+C+j); chkmx(dp[x][y],b[x][y]+D-j); chkmx(mx,dp[x][y]); } for (int k=l;k<=r;k++) { int x=s[k].x,y=s[k].y; int i=x+y,j=x-y; chkmx(A,dp[x][y]-i); chkmx(B,dp[x][y]+i); chkmx(C,dp[x][y]-j); chkmx(D,dp[x][y]+j); } l=r+1; } printf("%lld\n",mx); } int main() { n=read();m=read(); for (int i=1;i<=n;i++) for (int j=1,x;j<=m;j++) { x=read(); if (x) s[++tot]=(node){i,j,x}; } for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) b[i][j]=read(); sort(s+1,s+1+tot); calc(); return 0; }View Code