2018.7.21日記&總結

今天又只過了一題,連崩三天了!!!說明我的知識還有很多漏洞,各方面能力都還有很大欠缺!這三天的考試都暴露了很大的問題。
1。對算法瞭解不深入,不全面。好多算法只是聽說過,或者略懂一點,但沒有真正深入掌握。比如今天的二分圖博弈,又忘了結論是什麼。
2。寫代碼錯誤太多。這些錯誤在代碼寫得很熟練的時候往往很少犯,所以一定要多練習,慢慢提升自己的代碼能力。如今天F題,dfs前忘了標記vis,導致路徑上有重點,一直沒調出來。
並且,一定要訓練自己用眼調試代碼的能力,邊讀邊想每句代碼的正確性,減少對對拍的依賴,因爲考場上沒時間對拍!
3。思維不夠深入。好多時候模型轉化還不夠,只能想出一個時間複雜度較爲接近的算法,但離正解還有一步,如果去寫通常過不了反而浪費時間。比如今天G題本來是很簡單的結論題,(看錯題)想複雜去分治NTT。還有昨天nlogn求&最大值,只想到sqrt(n)分塊,怎麼優化常數都過不了。這種情況應該更深入的去想正解,不能心存僥倖,去寫時間複雜度明顯錯誤的算法!
4。讀題不夠仔細。英文比賽打得很少,所以對ACM題面的分析能力不夠強。經常看掉題目特殊限制,從而想不出題,想錯題,浪費很多時間。還有ACM中細節比OI中明顯多很多,比如多組數據清空數組,特殊情況判定(特別是ACM必須AC,而OI少特判可能掉10分而已)。所以必須更加細緻,思維更加全面,特別是把做法想出來的題快速寫對!
5。套路不夠熟練。可能是因爲脫離OI太久的緣故但這絕不是藉口!!任何和你一起訓練的人都和你站在同一起跑線上,ACM是新的起點!所以多做題,認真補題,特別是多總結,多思考,多複習,靜心努力,提高自己的水平,讓自己能熟悉的轉化出各種模型!

題解:
A:簽到題。換個順序枚舉即可,思路很常見。應該10分鐘寫完的!
B:數位DP,求迴文數個數(後四位爲日期)。所以枚舉後4爲,對前面的數位DP,記錄是否頂上界,不頂上界直接乘10^len即可
其實很好寫啊!

#include<bits/stdc++.h>
using namespace std;

#define rep(i,l,r) for(register int i = l ; i <= r ; i++)
#define repd(i,r,l) for(register int i = r ; i >= l ; i--)

typedef long long ll;
ll power[20];
ll L,R,d1,d2;
ll ans;
int T,mx[20],a[20],rev[10020],tot;

inline void read(ll &x,ll &y){
    int num[20],cnt = 0;
    register char ch = getchar();
    while ( ch > '9' || ch < '0' ) ch = getchar();
    while ( ch <= '9' && ch >= '0' ) num[++cnt] = ch - '0' , ch = getchar();
    rep(i,cnt - 3,cnt) y = y * 10 + num[i];
    rep(i,1,cnt - 4) x = x * 10 + num[i];   
}
void init(){
    power[0] = 1;
    rep(i,1,18) power[i] = power[i - 1] * 10ll;
    rep(i,1,12) mx[i] = 31;
    mx[2] = 28 , mx[4] = mx[6] = mx[9] = mx[11] = 30;
    rep(i,1,2000){
        int cnt = 0,x = i;
        rep(j,1,4) a[++cnt] = x % 10 , x /= 10;
        rep(j,1,4) rev[i] = rev[i] * 10 + a[j];
    }
    rep(i,1,12) rep(j,1,mx[i]) if ( j % 10 ) tot++;
}
ll DP(int i,int j,int t2){
    if ( i < j ){
        if ( t2 ) return 0;
        return 1;
    }
//  if ( !t1 ) return power[(i - j + 2) / 2];
    ll res = 0;
    //頂上界繼續枚舉,注意超過下界主要看當前位
    res += DP(i - 1,j + 1,(a[i] > a[j]) || (t2 && (a[i] == a[j])));
    //不頂上界直接統計答案,所以不用記錄是否頂上界
    res += power[(i - j) / 2] * a[i];
    return res;
}
inline ll Calc(ll x,ll y){
    int cnt = 0,fir = 0,c = 0; ll res = 0;
    memset(a,0,sizeof(a));
    while ( x ) a[++cnt] = x % 10 , x /= 10;
    repd(i,cnt,cnt - 3) fir = fir * 10 + a[i];
    rep(i,1,12){
        rep(j,1,mx[i]){
            c = i * 100 + j;
            if ( (j % 10) == 0 || rev[c] > fir ) continue;
            if ( rev[c] == fir ) res += DP(cnt - 4,1,c > y);
            else res += power[(cnt - 3) / 2];
        }
    //  cout<<res<<endl;
    }
    rep(i,0,cnt - 5) res += power[(i + 1) / 2] * tot;
    return res;
}
int main(){
    freopen("input.txt","r",stdin);
    init();
    scanf("%d",&T);
    while ( T-- ){
        L = d1 = R = d2 = 0;
        read(L,d1) , read(R,d2);
        ans = Calc(R,d2) - Calc(L,d1 - 1);
        printf("%lld\n",ans);
    }
    return 0;
}


C:博弈,每次選一個數,是last+a[x]爲質數,將它刪去。
因爲last+a[x]爲奇數,所以一定一奇一偶,則轉化爲二分圖(奇偶分治)然後就是二分圖博弈。
然而怎麼判斷一個點是否一定在所有最大匹配上。在殘量網絡上bfs,如果S能到x,則x不一定在,否則一定在。注意判2時要刪除0號節點

#include<bits/stdc++.h>
using namespace std;
#define maxn 1020
#define rep(i,l,r) for(register int i = l ; i <= r ; i++)
#define inf 1e8

typedef long long ll;
struct node{
    int next,to,f;
}e[maxn * maxn * 2];
int head[maxn],cnt = 1,cur[maxn],S,T,dis[maxn],q[maxn],hh,tt;
int a[maxn],n,t,id,cnte,cnto,odd[maxn],even[maxn],maxflow;

void clear(){
    rep(i,1,T) head[i] = 0;
    cnte = cnto = 0 , cnt = 1 , id = maxflow = 0;
}
inline ll power(ll x,ll y,ll p){
    ll res = 1;
    while ( y ) {
        if ( y & 1 ) res = res * x % p;
        x = x * x % p;
        y >>= 1;
    }
    return res;
}
inline bool check(ll a,int x,int d,ll p){
    a = power(a,x,p);
    if ( a == 1 || a == p - 1 ) return 1;
    rep(i,1,d){
        a = a * a % p;
        if ( a== p - 1) return 1;
    }
    return 0;
}
inline bool isprime(int p){
    if ( p <= 2 ) return 0;
    //if ( p == 2 ) return 1;
    int x = p - 1,cnt = 0;
    while ( !(x & 1) ) x >>= 1 , cnt++;
    rep(i,1,10){
        int cur = rand() % (p - 1) + 1;
        if ( !check(cur,x,cnt,p) ){ return 0; }
    }
    return 1;
}
inline void adde(int x,int y,int c){
    e[++cnt].to = y;
    e[cnt].next = head[x];
    e[cnt].f = c;
    head[x] = cnt;
    e[++cnt].to = x;
    e[cnt].next = head[y];
    e[cnt].f = 0;
    head[y] = cnt;
}
bool bfs(){
    rep(i,1,T) dis[i] = 0;
    tt = hh = 0 , q[tt++] = S , dis[S] = 1;
    while ( hh < tt ){
        int x = q[hh++];
        for (int i = head[x] ; i ; i = e[i].next){
            if ( e[i].f && !dis[e[i].to] ){
                dis[e[i].to] = dis[x] + 1;
                q[tt++] = e[i].to;
            }
        }
    }
    return dis[T];
}
int dfs(int x,int delta){
    if ( x == T || !delta ) return delta;
    int res = 0;
    for (int &i = cur[x] ; i ; i = e[i].next){
        if ( e[i].f && dis[e[i].to] == dis[x] + 1 ){
            int d = dfs(e[i].to,min(delta,e[i].f));
            res += d , delta -= d;
            e[i].f -= d, e[i ^ 1].f += d;
            if ( !delta ) return res;
        }
    }
    if ( delta ) dis[x]= -1;
    return res;
}
//直接刪掉x重新跑,看流量是否相等也可以判斷。注意判2的時候0也要刪除
bool check(int x){ //一個點是否在所有最大匹配上
    if ( !x ) return 1;
    for(int i = 2 ; i <= cnt ; i += 2){
        if ( e[i].to == x || e[i ^ 1].to == x || e[i].to == cnte || e[i ^ 1].to == cnte ){
            e[i].f = e[i ^ 1].f = 0;
        }
        else{
            e[i].f = 1 , e[i ^ 1].f = 0;
        }
    }
    int c = 0;
    while ( bfs() ){
        rep(i,1,T) cur[i] = head[i];
        c += dfs(S,inf);
    }
    if ( c == maxflow ) return 0;
    return 1;
}

bool bfs2(int x,int y){
    tt = hh = 0;
    rep(i,1,T) dis[i] = 0;
    q[tt++] = S, dis[S] = 1;
    while ( hh < tt ){
        int x = q[hh++];
        for (int i = head[x] ; i ; i = e[i].next){
            if ( e[i].f && !dis[e[i].to] && e[i].to != y ){
                dis[e[i].to] = dis[x] + 1;
                q[tt++] = e[i].to;
            }
        }
    }
    return dis[x];
}
//直接在殘量網絡上從S bfs,如果能到x,說明x不一定在最大匹配上,否則一定在
bool check2(){
    if ( !bfs2(cnte,0) ) return 0;
    for(int i = 2 ; i <= cnt ; i += 2){
        if ( e[i].to == cnte || e[i ^ 1].to == cnte ){
            e[i].f = e[i ^ 1].f = 0;
        }
        else{
            e[i].f = 1 , e[i ^ 1].f = 0;
        }
    }
    while ( bfs() ){
        rep(i,1,T) cur[i] = head[i];
        dfs(S,inf);
    }
    if ( id && bfs2(id,cnte) ) return 0;
    return 1;
}
int main(){
    freopen("input.txt","r",stdin);
    scanf("%d",&t);
    while ( t-- ){
        clear();
        scanf("%d",&n);
        int tag = 0;
        rep(i,1,n){
            scanf("%d",&a[i]); 
            if ( isprime(a[i]) ) tag = 1;
            if ( a[i] & 1 ) odd[++cnto] = a[i];
            else even[++cnte] = a[i];
            if ( a[i] == 2 ) id = cnte;
        }
        if ( !tag && !id ){  printf("Totodile\n"); continue; }
        even[++cnte] = 0 , S = n + 2, T = n + 3;
        rep(i,1,cnto){
            rep(j,1,cnte)
                if ( isprime(odd[i] + even[j]) ){
                    adde(j,i + cnte,1);
                }
        }
        rep(i,1,cnto) adde(i + cnte,T,1);
        rep(i,1,cnte) adde(S,i,1);
        while ( bfs() ){
            rep(i,1,T) cur[i] = head[i];
            maxflow += dfs(S,inf);
        }
        //if ( !check(cnte) && check(id) ) 
            if ( check2() ) printf("Totodile\n");
        else printf("Bulbasaur\n");
    }
    return 0;
}

`
D:給n個數填入m個位置,是sigma(|a[i] - b[j]|)最小。
用線段樹維護DP。DP時把匹配看成向左或向右的一段。
好題!但是寫和調花了太長時間!應該快速理清楚思路然後寫!注意細節,其實很好寫,但要推清楚,查錯很難!注意有相同數時只能把a[i]和b[i]的關係定爲一種。

#include<bits/stdc++.h>
using namespace std;
#define maxn 100020
#define rep(i,l,r) for(register int i = l ; i <= r ; i++)
#define repd(i,r,l) for(register int i = r ; i >= l ; i--)
#define inf 1e18

typedef long long ll;
int a[maxn],b[maxn],c[maxn * 2],n,m,tot;
int T,root[maxn],cnt,ls[maxn << 5],rs[maxn << 5];
ll fl[maxn],mn[maxn << 5],lw[maxn],rw[maxn],suma[maxn],sumb[maxn];
int stack_[maxn],tops,lb[maxn],la[maxn],rb[maxn],ra[maxn],pa[maxn];
struct node{
    int id,num,t;
/*  bool operator < (node a)const{
        if ( id == a.id ) return t > a.t;
        return id < a.id;
    }*/
}dt[maxn * 2];
    inline bool cmp1 (node a,node b){
        if ( a.id == b.id ){
            if ( a.t == b.t ) return a.num < b.num;
            return a.t > b.t;
        }
        return a.id < b.id;
    }
inline bool cmp2(node a,node b){
    if ( a.id == b.id ){
        if ( a.t == b.t ) return a.num < b.num;  
        return a.t > b.t; 
    }
    return a.id < b.id;
}
void clear(){
    mn[0] = inf;
    memset(fl,0x3f,sizeof(fl)) , fl[0] = 0;
    rep(i,1,n) root[i] = 0;
    rep(i,1,tot) ls[i] = rs[i] = 0;
    tot = 0;
    rep(i,1,m) lb[i] = la[i] = rb[i] = ra[i] = 0 , pa[i] = n + 1;
}
void pre(){
    sort(c + 1,c + tot + 1);
    rep(i,1,n) a[i] = lower_bound(c + 1,c + tot + 1,a[i]) - c;
    rep(i,1,m) b[i] = lower_bound(c + 1,c + tot + 1,b[i]) - c;
    sort(a + 1,a + n + 1) , sort(b + 1,b + m + 1);
    int j = 1;
    rep(i,1,m){
        while ( j <= n && a[j] < b[i] ) j++;
        if ( j > n ) break;
        pa[i] = j;
    }
    rep(i,1,m) sumb[i] = sumb[i - 1] + c[b[i]];
    rep(i,1,n) suma[i] = suma[i - 1] + c[a[i]];
}
void init(){
    int mx = 0,mn = tot + 2;
    //b向右匹配
    tot = tops = 0;
    rep(i,1,n) dt[++tot] = (node){a[i],i,-1};
    rep(i,1,m) dt[++tot] = (node){b[i],i,1};
    sort(dt + 1,dt + tot + 1,cmp1);
    rep(i,1,tot){
        int t = dt[i].t,cur = 0;
        if ( t == 1 ) stack_[++tops] = dt[i].num , mx = dt[i].num;
        else{
            if ( !tops ) continue;
            cur = stack_[tops--];
            ra[cur] = dt[i].num , rb[cur] = mx , rw[cur] = suma[dt[i].num] - suma[pa[cur] - 1] - (sumb[mx] - sumb[cur - 1]);
        }
    }

    //b向左匹配
    tot = tops = 0;
    rep(i,1,n) dt[++tot] = (node){a[i],i,-1};
    rep(i,1,m) dt[++tot] = (node){b[i],i,1};
    sort(dt + 1,dt + tot + 1,cmp2);
    repd(i,tot,1){
        int t = dt[i].t,cur = 0;
        if ( t == 1 ) stack_[++tops] = dt[i].num , mn = dt[i].num;
        else{
            if ( !tops ) continue;
            cur = stack_[tops--];
            la[cur] = dt[i].num , lb[cur] = mn;
            lw[cur] = -(suma[pa[cur] - 1] - suma[dt[i].num - 1]) + sumb[cur] - sumb[mn - 1];
        }
    }
    //rep(i,1,n) cout<<a[i]<<" ";
    //rep(i,1,m) cout<<b[i]<<" ";
}
ll query(int x,int l,int r,int L,int R){
    if ( !x ) return inf;
    if ( L <= l && R >= r ) return mn[x];
    ll res = inf; int mid = (l + r) >> 1;
    if ( L <= mid ) res = query(ls[x],l,mid,L,R);
    if ( R > mid ) res = min(res,query(rs[x],mid + 1,r,L,R));
    return res;
}
inline void update(int x){
    mn[x] = min(mn[ls[x]],mn[rs[x]]);
}
void modify(int &x,int l,int r,int id,ll d){
    if ( !x  ) x = ++tot;
    if ( l == r ){ mn[x] = d; return; }
    int mid = (l + r) >> 1;
    if ( id <= mid ) modify(ls[x],l,mid,id,d);
    else modify(rs[x],mid + 1,r,id,d);
    update(x);
}
void solve(){
    tot = 0;
    rep(i,1,m){
        if ( lb[i] ) fl[i] = min(fl[lb[i] - 1],query(root[lb[i] - 1],1,n,1,la[i] - 1)) + lw[i];
        if ( rb[i] ) {
            ll cur = min(fl[i - 1],query(root[i - 1],1,n,1,pa[i] - 1)) + rw[i];
            modify(root[rb[i]],1,n,ra[i],cur);  
        }
    }
    ll ans = min(fl[m],mn[root[m]]);
    printf("%lld\n",ans);
}
int main(){
    freopen("input.txt","r",stdin);
    scanf("%d",&T);
    while ( T-- ){
        scanf("%d %d",&n,&m);
        clear();
        rep(i,1,n) scanf("%d",&a[i]) , c[++tot] = a[i];
        rep(i,1,m) scanf("%d",&b[i]) , c[++tot] = b[i];

        pre();
        init();
        solve();
    }
    return 0;
}

E:數學題。

#include<bits/stdc++.h>
using namespace std;
#define maxn 100020
#define rep(i,l,r) for (register int i = l ; i <= r ; i++)

typedef long long ll;
const ll p = 1e9 + 7;
int prime[maxn],cnt,tag[maxn],mul[1020];
int A,B,D,T,a[maxn],tot;
ll ans = 0,inv6;

ll power(ll x,ll y){
    ll res = 1;
    while ( y ){
        if ( y & 1 ) res = res * x % p;
        x = x * x % p;
        y >>= 1;
    }
    return res;
}
void init(){
    rep(i,2,100000){
        if ( !tag[i] ) prime[++cnt] = i;
        for(register int j = 1 ; j <= cnt && (i * prime[j]) <= 100000 ; j++){
            tag[i * prime[j]] = 1;
            if ( (i % prime[j]) == 0 ) break;
        }   
    }
    inv6 = power(6,p - 2);
}
inline ll Calc(ll n){
//  if ( n & 1 ) n++;
    return n * (n + 1)  % p * (2 * n + 1) % p * inv6 % p;
}
void solve(){
    ans = 0;
    rep(i,0,(1 << tot) - 1){
        int cur = 1,cnt = 0;
        rep(j,0,tot - 1){
            if ( i & (1 << j) ) cur *= a[j] , cnt++;
        }

            if ( cnt & 1 ) ans = (ans - Calc((D + 1)/cur) * cur % p * cur % p + p) % p;
            else  ans = (ans + Calc((D + 1)/cur) * cur % p * cur) % p;

    }
    printf("%lld\n",ans * ((ll)A * A % p + (ll)B * B % p) % p);
}
int main(){
    freopen("input.txt","r",stdin);
    init();
    scanf("%d",&T);
    while ( T-- ){
        scanf("%d %d %d",&A,&B,&D);
        if ( D & 1 ){ printf("0\n"); continue; }
        else{
            tot = 0; int x = D + 2;
            //質數要篩到大於sqrt(n)
            for (register int i = 1 ; prime[i] * prime[i] <= x ; i++){
                if ( (x % prime[i]) == 0 ){
                    a[tot++] = prime[i];
                    while ( (x % prime[i]) == 0 ) x /= prime[i];
                }
            }
            if ( x > 1 ) a[tot++] = x;
            solve();
        }
    }
    return 0;
}


F:找出一個圖的K染色方案或輸出長度爲K的路徑。
直接構建dfs樹,每層一種顏色(因爲只有返祖邊,同層無邊)
如果無法染色則深度>K
學習了一波圖的K染色方案數(注意不是最大團,不能直接從不能染色的點dfs,明天問一下?)
這裏寫鏈接內容
G:序列只有1和2,問能構成的區間和抑或值。
結論:如果一個序列的一端爲1,則1-sum都可以被表示
挺顯然的,但當時沒有往這方面想,光想着這麼統計和的方案數。開始看成會被抑或掉那種,要求出現次數,所以就想到分治FFT(其實根本不用分治,前綴和卷一下就好,所以FFT技巧也不夠熟練)
然後就找最左或最右的1,v=max(sum[r],sum[n - l + 1])即找出最長可被連續表示的數。然後數爲v +2,v +4,v + 2k

一定要早點睡,我覺得這幾天狀態不好的一大原因就是沒有睡好!任何事都不能與睡覺衝突!安排好時間!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章