【學習筆記】算法競賽進階指南第一章


前言

這是一篇流水賬式的真·隨筆

大概是第n次被教做人過后,感受到了“菜是原罪”這句話的痛啊..於是決心補救一下,從啃書開始吧。

覺得比較重要,是挑着着看的部分,會另開一篇總結的

不得不說這本書真的挺有意思的!!!

正文

8.26

看完了第一章,感覺懂了80%吧,應該寫寫題,看得還算認真,想了n遍都理解不到的地方基本都有勘誤..

8.27

改完了考試的錯,寫寫例題

P4 POJ1995

快速冪模板題

#include<cstdio> #include<iostream> #include<cstring> #include<cmath> #include<algorithm>
using namespace std; #define ll long long ll a,b,t,m,n,ans; ll ksm(ll a,ll b,ll mod) { ll ret=1; while(b) { if(b&1)ret=ret*a%mod; a=a*a%mod; b>>=1; } return ret; } int main() { scanf("%lld",&t); while(t--) { ans=0; scanf("%lld%lld",&m,&n); for(ll i=1;i<=n;i++) { scanf("%lld%lld",&a,&b); ans=(ans+ksm(a,b,m))%m; } printf("%lld\n",ans); } }

 P7

狀壓太困難了,就寫書上的部分,以后再填坑吧【按照自己寫狀壓的習慣搞的..】

/*
本質是狀壓dp
f[i][j]表示走到i點,狀態為j(哪些點走過),w[i][j]表示i到j的路徑長度
則有f[i][j]=min{f[k][j^(1<<i)]+w[k][i]}
*/ 
int cal(int n)
{
    memset(f,0x3f,sizeof(f));
    f[0][1]=0;
    mx=(1<<n)-1;//最大狀態 
    for(int i=1;i<n;i++)
        for(int j=0;j<=mx;j++)
            if(1&(j>>i))//走過i 
                for(int k=0;k<n;k++)
                    if(1&(j>>k))//走過 k
                        f[i][j]=min(f[i][j],f[k][j^(1<<i)]+w[k][i]);
                        //要從k-->i必須滿足i這個點在上一層狀態里未被訪問 
    return f[mx][n-1];
}

 

P9 TYVJ1266

這題真的適合放枚舉嗎??我覺得挺坑的

書上的思路看了幾遍,沒啥問題,但是真的不太好實現,也不知道怎么去一排一排推??

8012年了,冥思無果當然是搜題解啊,被輸入坑了半天,但是覺得這個題還挺有意思

這個思路是用bfs,從全亮狀態往回推,直到點了6次結束【其實bfs似乎才是最直接能想到的】,利用二進制存狀態

然而這個時空消耗都好大,嚴格地來說,狀態上限有225    

總之這個方法雖然思路簡單,不需要數學證明也不需要深入思考,但的確不如書上的優..

確實不知道怎么實現我也沒辦法啊

一些解釋在代碼里

#include<bits/stdc++.h>
using namespace std;
int o,t,mx,mp;
int ans[1<<25];
queue<int>q;
/*
            k=0 
            ↓
            0 0 0 0 0 <--k=4
k%5 == 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 <--k=19,k%5=4 0 0 0 0 0
*/ int read() { char ch=getchar(); while (ch<'0'||ch>'1') ch=getchar(); return ch-48; } inline int change(int x,int k) { x^=(1<<k);//當前位置 if(k>=5)x^=(1<<k-5);// if(k<20)x^=(1<<k+5);// if(k%5) x^=(1<<k-1);// if(k%5<4)x^=(1<<k+1);// return x; } void bfs() { memset(ans,-1,sizeof(ans)); mx=(1<<25)-1; q.push(mx);ans[mx]=0;//全亮狀態不需要操作 while(!q.empty()) { int x=q.front();q.pop(); if(ans[x]==6)return; for(int i=0;i<25;i++) { int y=change(x,i); if(ans[y]<0)q.push(y),ans[y]=ans[x]+1; } } } int main() { scanf("%d",&t); bfs();//逆推 ,預處理所有情況需要操作次數 while(t--) { mp=0; for(int i=1;i<=5;i++) for(int j=1;j<=5;j++) { mp<<=1; mp+=read();//處理二進制地圖,讀入不要當做整數讀,當字符串讀! } printf("%d\n",ans[mp]); } return 0; }

 P10

水水的HANOI

#include<cstdio>
#include<cstring>
#include<string>
#include<iostream>
#include <queue>
using namespace std;
#define N 20
int d[N],f[N];
int main()
{
    memset(f,0x3f,sizeof(f));
    d[1]=1;f[1]=1;
    for(int i=1;i<=12;i++)
        d[i]=2*d[i-1]+1;
    for(int i=1;i<=12;i++)
        for(int j=1;j<i;j++)
            f[i]=min(f[i],2*f[j]+d[i-j]);
    for(int i=1;i<=12;i++)
        cout<<f[i]<<endl;
} 

8.28 

下午填完了兩天考試的題解的坑,晚上才開始看書..

P11 BZOJ1218

二維前綴和,下標要從1開始..

#include<bits/stdc++.h>
using namespace std;
#define N 5010
int e[N][N],s[N][N];
int n,x,y,v,r,mx,my,ans;
int main()
{
    scanf("%d%d",&n,&r);
    mx=r,my=r;
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d%d",&x,&y,&v);
        x++;y++;
        e[x][y]=v;
        mx=max(x,mx);my=max(y,my);
    }
    for(int i=1;i<=mx;i++)
        for(int j=1;j<=my;j++)
            s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+e[i][j];
    for(int i=r;i<=mx;i++)
        for(int j=r;j<=my;j++)
            ans=max(ans,s[i][j]-s[i-r][j]-s[i][j-r]+s[i-r][j-r]);
    printf("%d",ans);
    return 0;
}

 P12 POJ3263

這個題雖然水,但是思想很重要,寫法也很有意思

額外的總結:傳送門

#include<iostream>
#include<cstdio>
#include<map>
#include<cstring>
using namespace std;
int n,p,h,m;
#define N 10100
map<pair<int,int>,bool>flag;
int a[N],c[N],s[N];
int main()
{
    scanf("%d%d%d%d",&n,&p,&h,&m);
    for(int i=1;i<=m;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        if(x>y)swap(x,y);
        if(flag[make_pair(x,y)])continue;
        c[x+1]--;c[y]++;
        flag[make_pair(x,y)]=1;
    }
    for(int i=1;i<=n;i++)
    {
        s[i]=s[i-1]+c[i];
        printf("%d\n",s[i]+h);
    }
    return 0;
}

 8.29

臨近開學,略慌。

測試前看了一遍質數這一章。

早上測試不算慘,於是先調了昨晚上留的坑

P17 POJ1845

數學題就是玄,本來想寫分治,被安利了一種新的求逆元的方法,抱着嘗試的心情去寫,結果素數掛了,快速冪又掛了,開方也掛,總之慘不忍睹。各種奇怪的錯誤....

不過這種求逆元方法確實也值得學習

a/b%m=d

a/b=mx+d

a=bmx+bd

a %bm=bd

(a%bm)/b=d

所以a除以b對m的取模,等效於a對b*m取模后除以b,很明顯,非常適用於這種模數不大的情況。有幾個坑點,比如開方向下取整,比如-1過后要再加上模數防止不是正數...

#include<bits/stdc++.h>
using namespace std;
#define N 10100
#define mod 9901
#define ll long long
ll prime[N],num,v[N];
ll a,b,n,pos,cnt,mb,limit,ans=1;

ll ksm(ll a,ll b,ll mb)
{
    ll ret=1;
    a%=mb;
    while(b)
    {
        if(b&1)ret=ret*a%mb;
        a=a*a%mb;
        b>>=1;
    }
    return ret;
}

void primes()
{
    for(ll i=2;i<N;i++)
    {
        if(!v[i])
        {
            v[i]=i;
            prime[++cnt]=i;
        }
        for(ll j=1;j<=cnt;j++)
        {
            if(prime[j]>v[i]||prime[j]*i>N)break;
            v[prime[j]*i]=prime[j];
        }
    }
}

int main()
{
    scanf("%lld%lld",&a,&b);
     limit=sqrt(a)+1;n=a;primes();
    for(ll i=1;prime[i] * prime[i]<=n;i++)
    {
        if(n%prime[i]==0)
        {
            num=0;
            while(n%prime[i]==0)
            {
                n/=prime[i];
                num++;
            }
            mb=mod*(prime[i]-1);
            pos=ksm(prime[i],b*num+1,mb)+mb-1;
            ans*=pos/(prime[i]-1);
            ans%=mod;
            
        }
    }
    if(n>1)
    {
        mb=mod*(n-1);
        pos=ksm(n,b+1,mb)+mb-1;
        ans*=pos/(n-1);
        ans%=mod;
    }
    printf("%lld",ans);
    return 0;
}

 真的討厭信息處理,位置轉換類的題,所以跳過了一道,現在留坑*2了...

P26 POJ2018

以前寫過的二分,順便復習了一下實數二分和整數二分的板..

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
#define N 100100
#define INF 0x7fffffff
#define eps 1e-5
double a[N],b[N],s[N];
double ans,minx,l,r,mid;
int n,len;
int main()
{
    scanf("%d%d",&n,&len);
    for(int i=1;i<=n;i++)
        scanf("%lf",&a[i]);
    l=-1e6,r=1e6;
    while(r-l>eps)
    {
        ans=-INF,minx=INF;
        mid=(l+r)/2;
        for(int i=1;i<=n;i++)b[i]=a[i]-mid;
        for(int i=1;i<=n;i++)s[i]=s[i-1]+b[i];
        for(int i=len;i<=n;i++)
        {
            minx=min(minx,s[i-len]);
            ans=max(ans,s[i]-minx);
        }
        if(ans>=0)l=mid;
        else r=mid;
    }
    printf("%d\n",int(r*1000));
    return 0;
}

 P28 Codeforces 670C

離散化,比較熟了。開始題讀錯了...比較心急,碼完這道就肝作業,明天就回學校了..

#include<bits/stdc++.h>
using namespace std;
#define N 1000100
int n,m,cnt,cot,mx1,mx2,choice;
int a[N],b[N],z[N],f[N],num[N],tot[N];
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)    
    {
        scanf("%d",&a[i]);
        num[i]=a[i];
    }
    cnt=n;
    scanf("%d",&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d",&z[i]);
        a[++cnt]=z[i];
    }    
    for(int i=1;i<=m;i++)
    {
        scanf("%d",&f[i]);
        a[++cnt]=f[i];
    }
    sort(a+1,a+1+cnt);
    cot=unique(a+1,a+1+cnt)-(a+1);
    for(int i=1;i<=n;i++)
        tot[lower_bound(a+1,a+1+cot,num[i])-a]++;
    choice=1;
    for(int i=1;i<=m;i++)
    {
        int pos1=tot[lower_bound(a+1,a+1+cot,z[i])-a];
        int pos2=tot[lower_bound(a+1,a+1+cot,f[i])-a];
        if(pos1>mx1)
            mx1=pos1,mx2=pos2,choice=i;
        if(pos1==mx1&&pos2>mx2)
                mx2=pos2,choice=i;
    }
    printf("%d",choice);
    return 0;
}

 8.31

逃了開學考躲機房里

P29 BZOJ3032 

中位數不知道有什么其他用處,但是這道題的數學推導確實反反復復看了好久

#include<bits/stdc++.h>
using namespace std;
#define N 101000
#define ll long long
ll n,m,t,xm,ym,base,ans;
ll s[N],x[N],y[N]; 

void solve1()
{
    base=t/n;
    for(ll i=1;i<=t;i++)
        s[x[i]]++;
    for(ll i=1;i<=n;i++)
        s[i]-=base;
    for(ll i=1;i<=n;i++)
        s[i]+=s[i-1];
    sort(s+1,s+1+n);
    for(ll i=1;i<=n;i++)
        ans+=abs(s[i]-s[n+1>>1]);
}

void solve2()
{
    memset(s,0,sizeof(s));
    base=t/m;
    for(ll i=1;i<=t;i++)
        s[y[i]]++;
    for(ll i=1;i<=m;i++)
        s[i]-=base;
    for(ll i=1;i<=m;i++)
        s[i]+=s[i-1];
    sort(s+1,s+1+m);
    for(ll i=1;i<=m;i++)
        ans+=abs(s[i]-s[m+1>>1]);
}

int main()
{
    scanf("%lld%lld%lld",&n,&m,&t);
    for(ll i=1;i<=t;i++)
        scanf("%d%d",&x[i],&y[i]);
    xm=n>=t?n%t:t%n,ym=m>=t?m%t:t%m;
    if(!xm&&ym)
        printf("row "),solve1();    
    if(xm&&!ym)
        printf("column "),solve2();
    if(!xm&&!ym)
        printf("both "),solve1(),solve2();
    if(xm&&ym)
    {    printf("impossible ");return 0;}
    printf("%lld",ans);
    return 0;
    
}

 P32 POJ3784

poj炸了??測不出結果,假裝AC

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std;
#define N 10100
int t,p,n,k,mid,cnt;
int a[N];
priority_queue<int>bq;
priority_queue<int,vector<int>,greater<int> >sq;
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&p,&n);
        while(!bq.empty())bq.pop();
        while(!sq.empty())sq.pop();
        memset(a,0,sizeof(a));
        mid=0;cnt=0;
        printf("%d %d\n",p,n+1>>1);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            if(a[i]<mid)bq.push(a[i]);
            else sq.push(a[i]);
            if(!(i&1))continue;
            while(bq.size()>i/2)
                k=bq.top(),bq.pop(),sq.push(k);
            while(sq.size()>i/2+1)
                k=sq.top(),sq.pop(),bq.push(k);
            mid=sq.top();
            printf("%d ",mid),cnt++;
            if(!(cnt%10))printf("\n");
        }
        if((n+1>>1)%10)printf("\n");
    }
    return 0;
}

 P33 POJ2299

又是樹狀數組+離散化的逆序對

#include<bits/stdc++.h>
using namespace std;
#define N 500050
#define ll long long 
ll n,m,ans;
ll a[N],b[N],t[N];
inline ll lowbit(ll x)
{    return x&(-x);    }
inline void add(ll x)
{
    for(;x<=n;x+=lowbit(x))
        t[x]++;
}
inline ll query(ll x)
{
    ll ret=0;
    for(;x;x-=lowbit(x))
        ret+=t[x];
    return ret;
}
inline void init()
{
    ans=0;
    memset(a,0,sizeof(a));
    memset(t,0,sizeof(t));
    memset(b,0,sizeof(b));
}
int main()
{
    while(scanf("%lld",&n)&&n!=0)
    {
        init();
        for(ll i=1;i<=n;i++)
            scanf("%lld",&a[i]),b[i]=a[i];
        sort(a+1,a+1+n);
        m=unique(a+1,a+1+n)-(a+1);
        for(ll i=1;i<=n;i++)
            b[i]=lower_bound(a+1,a+1+m,b[i])-a;
        for(ll i=n;i;i--)
            ans+=query(b[i]-1),add(b[i]);
        printf("%lld\n",ans);    
    }
    return 0;
}

 9.4

開學了,看了圖論的前兩節,感覺真的沒什么時間寫例題了

P34 POJ2893

poj又炸了??

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
#define N 1010
int a[N*N],t[N*N];
int n,m,k,d,cnt,ans;
inline int lowbit(int x)
{
    return x&(-x);
}

inline int add(int x)
{
    for(;x<=n;x+=lowbit(x))
        t[x]++;
}

inline int query(int x)
{
    int ret=0;
    for(;x;x-=lowbit(x))
        ret+=t[x];
    return ret;
}

int cal()
{
    int ans=0;
    for(int i=n*m-1;i;i--)
    {
        ans+=query(a[i]-1);
        add(a[i]);
    }
    return ans;
}

int main()
{
    while(scanf("%d%d",&n,&m)&&n)
    {
        memset(t,0,sizeof(t));
        memset(a,0,sizeof(a));
        cnt=0;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
            {
                scanf("%d",&k);
                if(!k)
                    d=m-i;
                else
                    a[++cnt]=k;
            }    
        ans=cal();
        if(n&1)d=2;
        d%2==ans%2?printf("YES\n"):printf("NO\n");
    }
    return 0;
} 

 留了倍增的坑,奇怪的OJ懶得注冊了

P37 st表模板

void work()
{
    for(int i=1;i<=n;i++)f[i][0]=a[i];
    int k=log(n)/log(2)+1;
    for(int j=1;j<k;j++)
        for(int i=1;i<=n-(1<<j);i++)
            f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1];
}

int query()
{
    int k=log(r-l+1)/log(2);
    return max(f[l][k],f[r-(1<<k)+1][k]);
}

 P38 POJ3614

又是讀錯題,WA了半天,還以為防曬霜給出的兩個數也是說范圍。。。

前幾次測試在貪心上吃了大虧,現在稍微對比較簡單的算法也提高了警惕性。

#include<bits/stdc++.h>
using namespace std;
#define N 3000
int n,l,ans;
struct email
{
    int maxx,minx;
}a[N];
struct quq
{
    int k,num,id;
} now,use[N];
bool cmp(email a,email b)
{
    return a.minx>b.minx;
}
bool cmp2(quq a,quq b)
{
    return a.k>b.k;
}
int main()
{
    scanf("%d%d",&n,&l);
    for(int i=1;i<=n;i++)
        scanf("%d%d",&a[i].minx,&a[i].maxx);
    for(int i=1;i<=l;i++)
    {
        scanf("%d%d",&use[i].k,&use[i].num);
        use[i].id=i; 
    }    
    sort(a+1,a+1+n,cmp);
    sort(use+1,use+1+l,cmp2);
    for(int j=1;j<=n;j++)
    {
        now.k=0;
        for(int i=1;i<=l;i++)
        {
            if(use[i].k>=a[j].minx&&use[i].k<=a[j].maxx)
                if(use[i].num)
                {
                    now=use[i],use[i].num--;
                    break;
                }        
        }
        if(now.k)ans++;
    }
    printf("%d\n",ans);
    return 0;
}

 P39 POJ3190

POJ炸了就在BSOJ上交,沒注意到沒有SPJ,炸到飛起,后來並想不通為什么,交了標程,也只有10分

才發現沒有SPJ。。

雖然被水題卡半天略氣,但是學到了結構體的優先隊列

#include<bits/stdc++.h>
using namespace std;
#define N 50500
int n,cnt,jl[N];
struct email
{
    int st,ed,id;
}a[N];
bool operator <(const email &a,const email &b)
{
    if(a.ed==b.ed)
        return a.st>b.st;
    return a.ed>b.ed;
}
priority_queue<email>q;
bool cmp(email a,email b)
{
    if(a.st==b.st)
        return a.ed<b.ed;
    return a.st<b.st;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d%d",&a[i].st,&a[i].ed),a[i].id=i;
    sort(a+1,a+1+n,cmp);
    jl[a[1].id]=++cnt;q.push(a[1]);
    for(int i=2;i<=n;i++)
    {
        email tp=q.top();
        int mx=tp.ed,ans=jl[tp.id];
        if(mx<a[i].st)
            jl[a[i].id]=ans,q.pop();
        else
            jl[a[i].id]=++cnt;    
        q.push(a[i]);        
    }
    printf("%d\n",cnt);
    for(int i=1;i<=n;i++)
        printf("%d\n",jl[i]);
    return 0;
}

 9.5

今天晚上七點就寫完了作業??但是也只寫了兩道題

先研究了一下高精,再做了一道很獨特的均分權值。第一章例題基本完結,然而poj還是down

P40 NOIP2012 國王游戲

P41 Color a Tree

感覺越來越慌了...水過去了一年現在終於遭報應了...

9.6

P44 t2

9.10 

占卜DIY

學習分形,細節好多啊

#include<cstdio>
#include<queue>
#include<cstring>
#include<iostream>
using namespace std;
#define N 1010
int n;
char a[N][N];
int power(int x)
{
    int ret=1;
    for(int i=1;i<=x;i++)
        ret*=3;
    return ret;
} 
 
void dfs(int d,int x,int y)
{
    if(d==1)
    {a[x][y]='X';return;} 
    int s=power(d-2);
    dfs(d-1,x,y);
    dfs(d-1,x,y+2*s);
    dfs(d-1,x+s,y+s);
    dfs(d-1,x+2*s,y);
    dfs(d-1,x+2*s,y+2*s);
} 
 
int main()
{
    while(scanf("%d",&n))
    {
        if(n==-1)break;
        memset(a,' ',sizeof(a));//初始化為空格 
        dfs(n,1,1);
        int s=power(n-1);//3^(n-1)行 3^(n-1)列 
        for(int i=1;i<=s;i++)
            a[i][s+1]='\0'; //強制結束 無多余空格 
        for(int i=1;i<=s;i++)
            printf("%s\n",a[i]+1);//往前挪一位 
        printf("-\n");
    }
    return 0;
}

 9.11

今天心態稍微好一點兒了,終於半懵半懂的理解了最近點對

感覺自己好喜歡看題解啊,導致思維上升緩慢,然而在短時間內里理解一道題的能力以及啃看不懂的東西的能力瘋狂增加

雖然並沒有什么用。。。但是講真,最近點對這種拿給我自己想怎么想得出來...

POJ3714 Raid

9.15

前兩天其實已經寫了,今天才有空把題解搗鼓出來

糖果傳遞

第一章還有一點兒題沒做,但是時間是真的來不及了啊,所以第一章的學習暫時完結吧!!

完結

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM