1. 程式人生 > 實用技巧 >2020 ccpc online

2020 ccpc online

A

B找下規律發現答案是[2,n]的和加上[2,n]的素數和, 直接min_25

C 隊友過的

D

E

根據SG定理可以得到$SG(n)=\mathop{mex} \{SG(\frac{n}{d})\oplus ...\oplus SG(\frac{n}{d}) \} $, 異或$d$次, $d$是$n$的因子

只有$d$為奇數時有貢獻, $SG(n)=\mathop{mex}\limits_{d|n,\text{$d$為奇}} \{ SG(\frac{n}{d}) \}$

找下規律就可以得到$SG(pn)=SG(n)+1, p>2$

$n$為偶時, $SG(2n)=SG(n)$, $n$為奇時, $SG(2n)=SG(n)+1$

篩一下素因子即可

#include <bits/stdc++.h>
#define ctz __builtin_ctzll
typedef long long ll;
using namespace std;

mt19937 rd(time(0));
const int p[12]={2,3,5,7,11,13,17,19,23,29,31,37};
const int p_cnt=12;

ll mmul(ll a,ll b,ll m) { //a*b%m
    ll d=((long double)a/m*b+1e-8);
    ll r=a*b-d*m;
    return
r<0?r+m:r; } ll FastPow(ll a,ll b,ll m) { ll ans=1; for (;b;b>>=1,a=mmul(a,a,m)) if (b&1) ans=mmul(ans,a,m); return ans; } ll gcd(ll a,ll b) { if (!a||!b) return a+b; int t=ctz(a|b); a>>=ctz(a); do { b>>=ctz(b); if (a>b) swap(a,b); b
-=a; } while (b); return a<<t; } int isprime(ll n) { if (n==1) return 0; if (n==2||n==3||n==5) return 1; if (!(n&1)||!(n%3)||!(n%5)) return 0; ll m=n-1; int k=0; while (!(m&1)) m>>=1,++k; for (int ip=0;ip<p_cnt&&p[ip]<n;++ip) { ll x=FastPow(p[ip],m,n),y=x; for (int i=0;i<k;++i) { x=mmul(x,x,n); if (x==1&&y!=1&&y!=n-1) return 0; y=x; } if (x!=1) return 0; } return 1; } ll f[110]; int cnt; ll g(ll x,ll n,ll a) { ll t=mmul(x,x,n)+a; return t<n?t:t-n; } const int M=(1<<7)-1; ll Pollard_Rho(ll n) { if (!(n&1)) return 2; if (!(n%3)) return 3; if (!(n%5)) return 5; ll x=0,y=x,t=1,q=1,a=(rd()%(n-1))+1; for (int k=2;;k<<=1,y=x,q=1) { for (int i=1;i<=k;++i) { x=g(x,n,a); q=mmul(q,abs(x-y),n); if (!(i&M)) { t=gcd(q,n); if (t>1) break; } } if (t>1||(t=gcd(q,n))>1) break; } if (t==n) { t=1; while (t==1) t=gcd(abs((x=g(x,n,a))-y),n); } return t; } void work(ll n) { if (n==1) return; if (isprime(n)) f[++cnt]=n; else { ll t=n; while (t==n) t=Pollard_Rho(n); work(t),work(n/t); } } const int N = 1e5+10; int n; ll a[N]; int main() { int t; scanf("%d", &t); while (t--) { scanf("%d", &n); int ans = 0; for(int i=1;i<=n;++i) { scanf("%lld", a+i); cnt = 0; work(a[i]); int sg = 0, ok = 0; for (int j=1; j<=cnt; ++j) { if (f[j]==2) ok = 1; else ++sg; } sg += ok; ans ^= sg; } puts(ans?"W":"L"); } }
View Code

F

G 隊友過的

H

設${dp}_{n,i}$表示從點$(i,n)$走到$(0,0)$的最小期望, 那麼有兩種方案

一種是一直往左走遍歷每扇門, 如果門開著就往下走, 如果左邊全關就再繞到右邊

另一種就是先右再左, 兩種方案取最小就是最優期望

預處理一下概率和距離, 然後暴力$dp$, $O(n^4)$可過

#include <bits/stdc++.h>
using namespace std;
const int N = 55;
int n,x0,k[N],pos[N][N];
bool vis[N][N];
double dp[N][N],C[N][N],dis[N][N][N],D[N][N][N];
double dfs(int n, int m) {
    double &ans = dp[n][m];
    if (vis[n][m]) return ans;
    vis[n][m] = 1;
    if (n==1) return ans = 1;
    double lx = 0, rx = 0;
    //先往左走到頭再往右走
    int cnt = 0, tot = 0;
    //cnt為檢視過的關上的門數, tot為當前走的距離
    auto gao = [&](int i) {
        //p為限定cnt扇門沒開, 當前門開的概率
        double p = D[n][cnt][k[n]], mi = 1e18;
        for (int j=1; j<n; ++j) mi = min(mi, dfs(n-1,j)+dis[n][i][j]);
        return p*(mi+tot);
    };
    for (int i=m; i>=1; --i) {
        if (cnt+k[n]>n) break;
        lx += gao(i);
        if (i!=1) tot += 2;
        ++cnt;
    }
    if (m!=n) {
        //從1移動到m+1
        tot += m*2;
        for (int i=m+1; i<=n; ++i) {
            if (cnt+k[n]>n) break;
            lx += gao(i);
            tot += 2, ++cnt;
        }
    }
    //先往右走到頭再往左走
    cnt = tot = 0;
    for (int i=m; i<=n; ++i) {
        if (cnt+k[n]>n) break;
        rx += gao(i);
        if (i!=n) tot+=2;
        ++cnt;
    }
    if (m!=1) {
        //從n走到m-1
        tot += (n-m+1)*2;
        for (int i=m-1; i>=1; --i) {
            if (cnt+k[n]>n) break;
            rx += gao(i);
            tot += 2, ++cnt;
        }
    }
    return ans=min(lx,rx);
}

void work() {
    scanf("%d%d", &n, &x0);
    for (int i=1; i<=n; ++i) scanf("%d", k+i);
    memset(vis,0,sizeof vis);
    double ans = 1e18;
    for (int i=1; i<=n; ++i) {
        double w = sqrt((x0-pos[n][i])*(x0-pos[n][i])+1);
        ans = min(ans, dfs(n,i)+w);
    }
    printf("%.20lf\n", ans);
}

int main() {
    for (int i=0; i<N; ++i) {
        C[i][0] = 1;
        for (int j=1; j<=i; ++j) {
            C[i][j] = C[i-1][j]+C[i-1][j-1];
            pos[i][j] = -i+2*j-1;
        }
    }
    for (int n=1; n<=50; ++n) {
        for (int i=1; i<=n; ++i) for (int j=1; j<n; ++j) {
            double x1 = pos[n][i], y1 = n;
            double x2 = pos[n-1][j], y2 = n-1;
            dis[n][i][j] = sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
        }
        for (int k=1; k<=n; ++k) for (int cnt=0; cnt+k<=n; ++cnt) {
            D[n][cnt][k] = (C[n-cnt][k]-C[n-cnt-1][k])/C[n][k];
        }
    }
    int t;
    scanf("%d", &t);
    while (t--) work();
}
View Code

I

J 隊友過的

K

若只有$K_{1,1}$非零, 直接輸出$A$, 否則輸出全零矩陣

L

cometoj原題

異或很容易處理, 絕對值可以拆開, 表示成$x-y+K\ge 0\wedge y-x+K\ge 0$

從二進位制高位到低位遍歷,如果當前位$x-y+K\le -2$, 那麼後面無論怎麼取都是負的,如果當前位$x-y+K\ge 2$, 那麼後面無論怎麼取都是正的

所以只要維護[-1,1]三種取值即可

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll dp[32][3][3][2][2][2];
int main() {
    int t;
    scanf("%d", &t);
    while (t--) {
        int a,b,k,w;
        scanf("%d%d%d%d", &a, &b, &k, &w);
        memset(dp,0,sizeof dp);
        dp[31][1][1][1][1][1] = 1;
        ll ans = 0;
        for (int i=30; i>=0; --i) {
            for (int u=-1; u<=1; ++u) for (int v=-1; v<=1; ++v) for (int lw=0; lw<2; ++lw) {
                for (int lx=0; lx<2; ++lx) for (int ly=0; ly<2; ++ly) {
                    ll &ret = dp[i+1][u+1][v+1][lw][lx][ly];
                    if (!ret) continue;
                    int mx = lx?a>>i&1:1, my = ly?b>>i&1:1;
                    for (int xx=0; xx<=mx; ++xx) for (int yy=0; yy<=my; ++yy) {
                        if (lw&&(xx^yy)>(w>>i&1)) continue;
                        int nu = u*2+xx-yy+(k>>i&1), nv = v*2+yy-xx+(k>>i&1);
                        if (nu<-1||nv<-1) continue;
                        nu = min(nu, 1), nv = min(nv, 1);
                        if (!i) {
                            if (nu>=0&&nv>=0) ans += ret;
                        }
                        else dp[i][nu+1][nv+1][lw&&(xx^yy)==(w>>i&1)][lx&&xx==mx][ly&&yy==my] += ret;
                    }
                }
            }
        }
        printf("%lld\n", ans);
    }
}
View Code

M