【学习笔记】算法竞赛进阶指南第一章

前言

这是一篇流水账式的真·随笔

大概是第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
",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 ",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
",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
",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
",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("
");
        }
        if((n+1>>1)%10)printf("
");
    }
    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
",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
"):printf("NO
");
    }
    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
",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
",cnt);
    for(int i=1;i<=n;i++)
        printf("%d
",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]=''; //强制结束 无多余空格 
        for(int i=1;i<=s;i++)
            printf("%s
",a[i]+1);//往前挪一位 
        printf("-
");
    }
    return 0;
}

 9.11

今天心态稍微好一点儿了,终于半懵半懂的理解了最近点对

感觉自己好喜欢看题解啊,导致思维上升缓慢,然而在短时间内里理解一道题的能力以及啃看不懂的东西的能力疯狂增加

虽然并没有什么用。。。但是讲真,最近点对这种拿给我自己想怎么想得出来...

POJ3714 Raid

9.15

前两天其实已经写了,今天才有空把题解捣鼓出来

糖果传递

第一章还有一点儿题没做,但是时间是真的来不及了啊,所以第一章的学习暂时完结吧!!

完结

“Make my parents proud,and impress the girl I like.”
原文地址:https://www.cnblogs.com/NSD-email0820/p/9543109.html