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

一定要早点睡,我觉得这几天状态不好的一大原因就是没有睡好!任何事都不能与睡觉冲突!安排好时间!

猜你喜欢

转载自blog.csdn.net/weixin_42484877/article/details/81150611
今日推荐