【计划】复习计划part 1

前言

因为经常考试,考着考着就开始气,明明想到/会做,但是模板不熟,或者哪个细节忘了,或者思想有点忘了,所以不敢写/写的很慢/写挂

所以要记录一下要复习的内容,还是以时间为序,完成时间会在以后用红字在后面添上。

复习大概是要包括 重温算法+熟练模板+写(不是板的)例题

lca

【happy那道题,想出来过后开心了半天,结果...敲不对】

模板(注解的地方是踩得雷)

poj1330

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define N 10100
int n,t,x,y,cnt,root;
int first[N],in[N],dep[N];
int fa[N][20];
struct email
{
    int u,v;
    int nxt;
}e[N*4];
inline void add(int u,int v)
{
    e[++cnt].nxt=first[u];first[u]=cnt;
    e[cnt].u=u;e[cnt].v=v;
}

void dfs(int u,int f)
{
    for(int i=1;(1<<i)<=dep[u];i++)
        fa[u][i]=fa[fa[u][i-1]][i-1];
    for(int i=first[u];i;i=e[i].nxt)
    {
        int v=e[i].v;
        if(v==f)continue;
        fa[v][0]=u;
        dep[v]=dep[u]+1;
        dfs(v,u);
    }
}

int lca(int x,int y)
{
    if(dep[x]<dep[y])
        swap(x,y);
    int k=dep[x]-dep[y];
    for(int i=0;(1<<i)<=k;i++)//1-->0 
        if(k&(1<<i))//手滑了 (i<<1)-->(1<<i)
            x=fa[x][i];
    if(x==y)return x;
    for(int i=19;i>=0;i--)
        if(fa[x][i]!=fa[y][i])
            x=fa[x][i],y=fa[y][i];
    return fa[x][0]; 
} 

inline void init()
{
    memset(in,0,sizeof(in));
    memset(dep,0,sizeof(dep));
    memset(first,0,sizeof(first));
    memset(fa,0,sizeof(fa));
    cnt=0;
}

int main()
{
    scanf("%d",&t);
    while(t--)
    {
        init();//不能省 
        scanf("%d",&n);
        for(int i=1;i<n;i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            add(u,v);add(v,u);in[v]++;
        }
        for(int i=1;i<=n;i++)
            if(!in[i])
            {root=i;break;}
        dfs(root,0);
        scanf("%d%d",&x,&y);
        printf("%d
",lca(x,y));
    }
}

 hdu2586

#include<bits/stdc++.h>
using namespace std;
#define N 40400 
int n,t,m,x,y,cnt;
int first[N],dep[N],d[N];
int fa[N][20];
struct email
{
    int u,v,w;
    int nxt;
}e[N*4];
inline void add(int u,int v,int w)
{
    e[++cnt].nxt=first[u];first[u]=cnt;
    e[cnt].v=v;e[cnt].u=u;e[cnt].w=w;
}

inline void init()
{
    memset(in,0,sizeof(in));
    memset(dep,0,sizeof(dep));
    memset(first,0,sizeof(first));
    memset(fa,0,sizeof(fa));
    memset(d,0,sizeof(d));
    cnt=0;
}

void dfs(int u,int f)
{
    for(int i=1;(1<<i)<=dep[u];i++)
        fa[u][i]=fa[fa[u][i-1]][i-1];
    for(int i=first[u];i;i=e[i].nxt)
    {
        int v=e[i].v,w=e[i].w;
        if(v==f)continue;
        d[v]=d[u]+w;
        dep[v]=dep[u]+1;
        fa[v][0]=u;
        dfs(v,u);
    }
}

int lca(int x,int y)
{
    if(dep[x]<dep[y])
        swap(x,y);
    int k=dep[x]-dep[y];
    for(int i=0;(1<<i)<=k;i++)
        if(k&(1<<i))
            x=fa[x][i];
    if(x==y)return x;
    for(int i=19;i>=0;i--)
        if(fa[x][i]!=fa[y][i])
            x=fa[x][i],y=fa[y][i];
    return fa[x][0];
}

int main()
{
    scanf("%d",&t);
    while(t--)
    {
        init();
        scanf("%d%d",&n,&m);
        for(int i=1;i<n;i++)
        {
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            add(u,v,w);add(v,u,w);in[v]++;
        }
        dfs(1,0);
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&x,&y);
            printf("%d
",d[x]+d[y]-2*d[lca(x,y)]);
        }
    }
}

 不是板的例题:HDU4912

 结束 

tarjan

【也是因为happy那道题...看到环的第一想法,在脑子里默默过了一遍,发现好像不熟了?】

首先是比较熟的有向图的强连通分量模板

POJ2186 受欢迎的牛

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<stack>
using namespace std;
#define N 20020
int dfn[N],low[N],siz[N],col[N],vis[N],out[N],first[N];
int n,m,cnt,tot,gro,ans;
stack<int>s;
struct email
{
    int u,v;
    int nxt;
}e[N*5];
inline void add(int u,int v)
{
    e[++cnt].nxt=first[u];first[u]=cnt;
    e[cnt].u=u;e[cnt].v=v;
}

void tarjan(int u)
{
    dfn[u]=low[u]=++tot;
    s.push(u);vis[u]=1;
    for(int i=first[u];i;i=e[i].nxt)
    {
        int v=e[i].v;
        if(!dfn[v])
        {
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else
            if(vis[v])
                low[u]=min(low[u],dfn[v]);
    }
    if(low[u]==dfn[u])
    {
        ++gro;
        while(1)
        {
            int t=s.top();s.pop();vis[t]=0;
            siz[gro]++;col[t]=gro;
            if(t==u)break;
        }
    }
    
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        add(u,v);
    }
    for(int i=1;i<=n;i++)
        if(!col[i])
            tarjan(i);
    for(int i=1;i<=m;i++)
    {
        int u=e[i].u,v=e[i].v;
        if(col[u]!=col[v])
            out[col[u]]++;
    }
    for(int i=1;i<=gro;i++)
    {
        if(out[i]==0)//out[col[i]]==0;
        {
            if(ans)
            {
                ans=0;
                break;
            }
            ans=i;
        }
    }
    printf("%d
",siz[ans]);
    
}

 HDU 1269

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<stack>
using namespace std;
#define N 10100
#define M 100100
int vis[N],dfn[N],low[N],col[N],first[N];
int n,m,cnt,tot,gro,flag;
stack<int>s;
struct email
{
    int u,v;
    int nxt;
}e[M*2];
inline void add(int u,int v)
{
    e[++cnt].nxt=first[u];first[u]=cnt;
    e[cnt].u=u;e[cnt].v=v;
}

inline void init()
{
    cnt=0,tot=0,gro=0;
    memset(first,0,sizeof(first));
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(vis,0,sizeof(vis));
    memset(col,0,sizeof(col));
}

void tarjan(int u)
{
    dfn[u]=low[u]=++tot;
    s.push(u);vis[u]=1;
    for(int i=first[u];i;i=e[i].nxt)
    {
        int v=e[i].v;
        if(!dfn[v])
        {
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else
            if(vis[v])
                low[u]=min(low[u],dfn[v]);
    }
    if(low[u]==dfn[u])
    {
        ++gro;
        while(1)
        {
            int t=s.top();s.pop();vis[t]=0;    
            col[t]=gro;
            if(t==u)break;
        }
    }
}

int main()
{
    while(scanf("%d%d",&n,&m)&&n)
    {
        init();
        for(int i=1;i<=m;i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            add(u,v);
        }
        for(int i=1;i<=n;i++)
            if(!col[i])
                tarjan(i);
        if(gro==1)
            printf("Yes
");
        else
            printf("No
");
    }
}

 难受啊,没想到割边割点对我来说比缩点更困难

终于割边的板子出来了

luogu 3388

给自己写一点reminders吧

1.根据定义,只有一个子节点的根不是割点,所以dfs时,分是根或者不是根,结束dfs当前节点前补回这个u可能是根且是割点的情况

2.割点数量最后统计,不要懒得打那一个for循,在过程中统计包挂

3.割点是>=

4.图不一定联通

5.不需要判v==fa,因为dfn能阻断已经访问过的点

6.如果v不能回到比dfn[u]小的点,说明u是割点,而不是v(只能回到u点,u也是割点)

#include<bits/stdc++.h>
using namespace std;
#define N 100100
int n,m,cnt,tot,num,root,child;
int dfn[N],low[N],cut[N],first[N];
struct email
{
    int u,v;
    int nxt;
}e[N*4];
inline void add(int u,int v)
{
    e[++cnt].nxt=first[u];first[u]=cnt;
    e[cnt].u=u;e[cnt].v=v;
}

inline void tarjan(int u)
{
    dfn[u]=low[u]=++tot;child=0;
    for(int i=first[u];i;i=e[i].nxt)
    {
        int v=e[i].v;
        if(!dfn[v])
        {
            tarjan(v);
            low[u]=min(low[u],low[v]);
            if(low[v]>=dfn[u]&&u!=root) 
                cut[u]=1;//,num++
            if(u==root)child++;
        }
        else
            low[u]=min(low[u],dfn[v]);
    }
    if(child>1&&u==root)cut[u]=1;//,num++
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        add(u,v);add(v,u);
    }
    for(int i=1;i<=n;i++)
        if(!dfn[i])
            root=i,tarjan(i);
    for(int i=1;i<=n;i++)
        if(cut[i])
            num++;
        printf("%d
",num);        
    for(int i=1;i<=n;i++)
        if(cut[i])
            printf("%d ",i);
    return 0;
 } 

今晚商量了一下停课的事情,割边终于也出炉了,tarjan这一块儿终于可以放心地做题了

没测过,只自己手造了一组,假装是对的吧【找不到模板题测啊】

又是可爱的reminders

1.这个不需要判根,但是需要严格防止搜爆,比起割点,多传参了一个u。

2.有一句  v!=fa&&dfn[v]<dfn[u],双重保险吧,我也不知道哪句必要,干脆都写

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 100100
int dfn[N],low[N],first[N];
int n,m,cnt,tot,num;
struct email
{
    int u,v;
    int nxt;
}e[N*4],cut[N*4]; 
inline void add(int u,int v)
{
    e[++cnt].nxt=first[u];first[u]=cnt;
    e[cnt].u=u;e[cnt].v=v;
}

inline void tarjan(int u,int fa)
{

    dfn[u]=low[u]=++tot;
    for(int i=first[u];i;i=e[i].nxt)
    {
        int v=e[i].v;
        if(!dfn[v])
        {
            tarjan(v,u);
            low[u]=min(low[u],low[v]);
            if(low[v]>dfn[u])
                cut[++num]=e[i];
        }
        else
            if(v!=fa&&dfn[v]<dfn[u])
                low[u]=min(low[u],dfn[v]);
    }
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        add(u,v);add(v,u);
    }
    for(int i=1;i<=n;i++)
        if(!dfn[i])
            tarjan(i,0);
    for(int i=1;i<=num;i++)
    {
        int u=cut[i].u,v=cut[i].v;
        printf("%d-->%d
",u,v);
    }
    return 0;
}
/*
8 9
1 2
1 3
1 4
2 4
3 4
4 5
4 6
6 8
6 7
*/

敲熟了 完结

线段树

【脑抽,t1去敲线段树,关键是还敲挂了】

模板

注解的地方是踩雷区。总结一下感觉还是对lazy的理解不深刻

合并区间的时候脑抽一下,lazy其实对于当前节点是执行了加权操作,只是暂时不修改更多的儿子罢了..

#include<bits/stdc++.h>
using namespace std;
#define N 100100
#define ll long long
#define lc (p<<1)
#define rc (p<<1|1)
#define mid (t[p].l+t[p].r>>1)
ll n,m,op;
ll a[N];
struct email
{
    ll l,r,sum,lazy;
}t[N*4];

inline void pushnow(ll p,ll v)
{
    t[p].sum+=(t[p].r-t[p].l+1)*v;
    t[p].lazy+=v; 
}

inline void pushdown(ll p)
{
    if(t[p].lazy)
    {
        pushnow(lc,t[p].lazy);
        pushnow(rc,t[p].lazy);
        t[p].lazy=0;
    }
}

inline void pushup(ll p)
{
    t[p].sum=t[lc].sum+t[rc].sum;
    //t[p].sum+=t[lc].sum+t[rc].sum;
}

inline void build(ll p,ll l,ll r)
{
    t[p].l=l;t[p].r=r;
    if(l==r)
    {
        t[p].sum=a[l];
        return ;
    }
    ll bmid=l+r>>1;
    build(lc,l,bmid);build(rc,bmid+1,r);
    pushup(p);
}

inline void update(ll p,ll ql,ll qr,ll v)
{
    if(ql<=t[p].l&&qr>=t[p].r)
    {
        pushnow(p,v);//t[p].sum+=v;t[p].lazy+=v;
        return ; 
    }
    pushdown(p);
    if(ql<=mid)update(lc,ql,qr,v);
    if(qr>mid)update(rc,ql,qr,v);
    pushup(p);
}

inline ll query(ll p,ll ql,ll qr)
{
    ll ret=0;
    if(ql<=t[p].l&&qr>=t[p].r)
        return t[p].sum;
    pushdown(p);
    if(ql<=mid)    ret+=query(lc,ql,qr);
    if(qr>mid)    ret+=query(rc,ql,qr);
    return ret; 
}

int main()
{
    scanf("%lld%lld",&n,&m);
    for(ll i=1;i<=n;i++)
        scanf("%lld",&a[i]);
    build(1,1,n);
    for(ll i=1;i<=m;i++)
    {
        scanf("%lld",&op);
        if(op==1)
        {
            ll x,y,v;
            scanf("%lld%lld%lld",&x,&y,&v);
            update(1,x,y,v);
        }
        if(op==2)
        {
            ll x,y;
            scanf("%lld%lld",&x,&y);
            printf("%lld
",query(1,x,y));
        }
    }
    return 0;
}

 例题

结束

树状数组

【敲挂了线段树过后就敲了树状数组,又挂了。。】

模板

区间修改单点查询(区间查询的我选择用线段树...怕加加减减混了影响心态)

#include<bits/stdc++.h>
using namespace std;
#define N 100100
int n,m,op;
int a[N],b[N];
inline int lowbit(int x)
{
    return x&(-x);    
} 

inline void add(int x,int y)
{
    for(;x<=n;x+=lowbit(x))
        b[x]+=y;
}

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

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    for(int i=1;i<=m;i++)
    {
        scanf("%d",&op);
        if(op==1)
        {
            int l,r,v;
            scanf("%d%d%d",&l,&r,&v);
            add(l,v);add(r+1,-v);
        }
        if(op==2)
        {
            int k;
            scanf("%d",&k);
            printf("%d
",query(k)+a[k]);
        }
    }
    return 0;
}

 例题,逆序对

结束

树的直径

【花花森林虽然虚惊一场,但是树的直径确实只是知道思想,还是得去敲一敲】

dp求直径模板

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 100010
int n,m,cnt,ans;
int d[N],vis[N],first[N];
struct email
{
    int u,v,w;
    int nxt;
}e[N*4];
inline void add(int u,int v,int w)
{
    e[++cnt].nxt=first[u];first[u]=cnt;
    e[cnt].u=u;e[cnt].v=v;e[cnt].w=w;
}
void dp(int u)
{
    vis[u]=1;
    for(int i=first[u];i;i=e[i].nxt)
    {
        int v=e[i].v,w=e[i].w;
        if(vis[v])continue;
        dp(v);
        ans=max(ans,d[v]+d[u]+w);
        d[u]=max(d[u],d[v]+w);
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        int u,v,w;
        char dir[3];
        scanf("%d%d%d%s",&u,&v,&w,dir);
        add(u,v,w);add(v,u,w);
    }
    dp(1);
    printf("%d
",ans);
    return 0;
}

两次dfs

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 100010
int n,m,cnt,ans1,ans2;
int d[N],vis[N],first[N];
struct email
{
    int u,v,w;
    int nxt;
}e[N*4];
inline void add(int u,int v,int w)
{
    e[++cnt].nxt=first[u];first[u]=cnt;
    e[cnt].u=u;e[cnt].v=v;e[cnt].w=w;
}
void dfs(int u)
{
    vis[u]=1;
    for(int i=first[u];i;i=e[i].nxt)
    {
        int v=e[i].v,w=e[i].w;
        if(vis[v])continue;
        d[v]=d[u]+w;
        dfs(v);
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        int u,v,w;
        char dir[3];
        scanf("%d%d%d%s",&u,&v,&w,dir);
        add(u,v,w);add(v,u,w);
    }
    dfs(1);
    for(int i=1;i<=n;i++)
        if(ans1<d[i])
            ans1=d[i],ans2=i;
    memset(vis,0,sizeof(vis));
    memset(d,0,sizeof(d));
    dfs(ans2);
    for(int i=1;i<=n;i++)
        ans1=max(ans1,d[i]);
    printf("%d
",ans1);
    return 0;
}

逆元

【到现在也不是很懂,花花森林的逆元为什么不能用费马小定理啊..还是我写挂了的原因??】

终于填上此坑了!!

四种方法求逆元

值得拥有!!!证明都很简单,手玩一下吧。玩不出来就背呗。

完结

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