1. 程式人生 > >正睿NOIP贈送附加賽1

正睿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;
int 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; }
View Code

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