zoukankan      html  css  js  c++  java
  • 算法竞赛进阶指南修习简记

    【一些要要补的东西】

    1.crt

    2.虚树,概率(卑微……)

    3.支配树,圆方树

    0x00基本算法

    0x01 位运算

    1.补码

    2.移位运算

    快速幂

    #include<bits/stdc++.h>
    #define re return
    #define inc(i,l,r) for(int i=l;i<=r;++i)
    
    using namespace std;
    template<typename T>inline void rd(T&x)
    {
        char c;bool f=0;
        while((c=getchar())<'0'||c>'9')if(c=='-')f=1;
        x=c^48;
        while((c=getchar())>='0'&&c<='9')x=x*10+(c^48);
        if(f)x=-x;
    }
    
    int a,b,p;
    int main()
    {
        rd(a),rd(b),rd(p);
        int ans=1%p;
        for(;b;b>>=1)
        {
            if(b&1)ans=(long long)ans*a%p;
            a=(long long)a*a%p;
        }
        
        printf("%d",ans);
        re 0;
    } 
    View Code

    龟速乘

    #include<bits/stdc++.h>
    #define re return
    #define inc(i,l,r) for(int i=l;i<=r;++i)
    
    using namespace std;
    template<typename T>inline void rd(T&x)
    {
        char c;bool f=0;
        while((c=getchar())<'0'||c>'9')if(c=='-')f=1;
        x=c^48;
        while((c=getchar())>='0'&&c<='9')x=x*10+(c^48);
        if(f)x=-x;
    }
    typedef long long ll;
    
    ll a,b,p;
    int main()
    {
        /*龟速乘 */
        rd(a),rd(b),rd(p);
        ll ans=0;
        for(;b;b>>=1)
        {
            if(b&1)ans=(ans+a)%p;
            a=(a<<1)%p;
        }
        
        printf("%lld",ans);
        re 0;
    } 
    View Code

    3.状压

    4.成对变换

    5.lowbit

    0x02递推与递归

    1.概论

    1. 奇怪的汉诺塔

    在三的基础上搞四

    #include<bits/stdc++.h>
    #define re return
    #define inc(i,l,r) for(int i=l;i<=r;++i)
    
    using namespace std;
    template<typename T>inline void rd(T&x)
    {
        char c;bool f=0;
        while((c=getchar())<'0'||c>'9')if(c=='-')f=1;
        x=c^48;
        while((c=getchar())>='0'&&c<='9')x=x*10+(c^48);
        if(f)x=-x;
    }
    
    int n,m,f[16],d[16]; 
    
    int main()
    {
    
        memset(f,0x3f,sizeof f);
        f[1]=d[1]=1;
        inc(i,2,12)
        {
            inc(j,1,i-1)
            f[i]=min(f[i],2*f[j]+d[i-j]);
            d[i]=d[i-1]<<1|1;
        }
        inc(i,1,12)
        printf("%d
    ",f[i]);
        re 0;
    } 
    View Code

    2.费解的开关

    强势枚举第一层

    暴力计算可行度

    2.分治

    3.分形

    分型之城

    恶心的分治模拟

    //一道模拟还要开longlong
    #include<bits/stdc++.h>
    #define re return
    #define inc(i,l,r) for(int i=l;i<=r;++i)
    using namespace std;
    template<typename T>inline void rd(T&x)
    {
        char c;bool f=0;
        while((c=getchar())<'0'||c>'9')if(c=='-')f=1;
        x=c^48;
        while((c=getchar())>='0'&&c<='9')x=x*10+(c^48);
        if(f)x=-x;
    }
    typedef long long ll;
    
    struct nide{
        ll x,y;
    }pa,pb; 
    
    
    ll N,a,b;
    
    inline nide dfs(ll x,ll n)
    {
        if(!n)re (nide){0,0}; 
        ll len=1ll<<(n-1);//2的n-1次方
        ll cnt=1ll<<((n-1)<<1);//4^(n-1),城市数 
        ll pos=x/cnt;
        
        nide ansnow=dfs(x%cnt,n-1);
        
        if(!pos) re (nide){ansnow.y,ansnow.x};
        if(pos==1) re (nide){ansnow.x,len+ansnow.y};
        if(pos==2) re (nide){len+ansnow.x,ansnow.y+len};
        if(pos==3) re (nide){2*len-ansnow.y-1,len-ansnow.x-1};
    }
    
    int main()
    {
        freopen("in.txt","r",stdin);
        int T;
        rd(T);
        while(T--)
        {
            rd(N),rd(a),rd(b);
            pa=dfs(a-1,N);
            pb=dfs(b-1,N);
            double x=pa.x-pb.x;
            double y=pa.y-pb.y;
             double ans=sqrt(x*x+y*y);
             printf("%.0lf
    ",ans*10);
        }
        
        re 0;
    } 
    View Code

    0x03前缀和与差分

    1.二维前缀和计算

    2.差分化区间修改为单点修改

    1.IncDec Sequence

    话说这道题是紫题,您认真的吗

    #include<bits/stdc++.h>
    #define re return
    #define ll long long
    #define inc(i,l,r) for(int i=l;i<=r;++i)
    
    using namespace std;
    template<typename T>inline void rd(T&x)
    {
        char c;bool f=0;
        while((c=getchar())<'0'||c>'9')if(c=='-')f=1;
        x=c^48;
        while((c=getchar())>='0'&&c<='9')x=x*10+(c^48);
        if(f)x=-x;
    }
    
    ll n,x,last,cnt1,cnt2; 
    
    int main()
    {
        rd(n);
        rd(last);
        inc(i,2,n)
        {
            rd(x);
            if(x-last<0)cnt2+=last-x;
            else cnt1+=x-last;
            last=x;
        }
        printf("%lld
    ",max(cnt1,cnt2));
        printf("%lld",abs(cnt1-cnt2)+1);
        re 0;
    } 
    View Code

    2.区间统计Tallest Cow

    #include<bits/stdc++.h>
    #define re return
    #define ll long long
    #define inc(i,l,r) for(int i=l;i<=r;++i)
    
    using namespace std;
    template<typename T>inline void rd(T&x)
    {
        char c;bool f=0;
        while((c=getchar())<'0'||c>'9')if(c=='-')f=1;
        x=c^48;
        while((c=getchar())>='0'&&c<='9')x=x*10+(c^48);
        if(f)x=-x;
    }
    
    map<pair<int,int>,bool>existed;
    
    int n,m,p,h,d[10010];
    
    int main()
    {
        freopen("in.txt","r",stdin);
    
        int x,y;
        rd(n),rd(p),rd(h),rd(m);
        
        inc(i,1,m)
        {
            rd(x),rd(y);
            if(x>y)swap(x,y);
            if(existed[make_pair(x,y)])continue;
            d[x+1]--;
            d[y]++;
            existed[make_pair(x,y)]=1;
        }
        
        int ans=0;
        inc(i,1,n)
        {
            ans+=d[i];
            printf("%d
    ",ans+h);
        }
        
        re 0;
    } 
    View Code

    0x04二分

    1.整数二分

    2.实数二分

    3.三分

    4.二分答案化判定

    我就是看到有一道交互题从乡下刚来城市什么见识,就去水了一道

    真好玩~

    1.特殊排序

    // Forward declaration of compare API.
    // bool compare(int a, int b);
    // return bool means whether a is less than b.
    
    class Solution {
    public:
        vector<int> specialSort(int N) {
            vector<int>v;
            v.push_back(1);
            for(int i=2;i<=N;++i)
            {
                int l=0,r=v.size()-1;
                while(l<=r)
                {
                    int mid=(l+r)>>1;
                    if(compare(v[mid],i))l=mid+1;
                    else r=mid-1;
                }
                v.insert(v.begin()+l,i);
            }
            return v;
        }
    };
    View Code

    0x05排序

    1.离散化

    2.中位数

    奇数(n+1)>>1

    偶数n/2~n/2+1

    0x06倍增

    1.基本

    genius ACM 

    2.st算法

    0x07贪心

    总而言之,就是让你用一种简单的排序或复杂的推式子得到一种最优的贪心celue

    防晒

    0x10 基本数据结构

    0x11 栈

    1.对顶栈

    请参见对顶堆,不过堆可能要维护大小之类的东西

    编辑器

    2.与Catalan数的不解之缘

     火车进出栈问题

    3.表达式求值

    反正中缀转后缀O(n)

    前后缀直接算

    表达式转换

    4.单调栈

    rt

    0x12 队列

    1.

    a.小组队列 

    模拟

    b.蚯蚓 

    维护3个单调递减的队列


    c.双端队列 

    从结果出发,排序,按原标号维持先递减后递增的最少块数

    2.单调队列

    最大子序和

    0x13链表与邻接表

     邻值查找

    模拟维护存在性

    #include<bits/stdc++.h>
    #define re return
    #define dec(i,l,r) for(int i=l;i>=r;--i)
    #define inc(i,l,r) for(int i=l;i<=r;++i)
    using namespace std;
    template<typename T>inline void rd(T&x)
    {
        char c;bool f=0;
        while((c=getchar())<'0'||c>'9')if(c=='-')f=1;
        x=c^48;
        while((c=getchar())>='0'&&c<='9') x=x*10+(c^48); 
        if(f)x=-x;
    }
    
    const int maxn=100005;
    int n,hide[maxn],L[maxn],R[maxn],vis[maxn];
    struct ndoe
    {
        int val,pos;
    }ans[maxn];
    
    struct node
    {
        int val,id;
        inline bool operator<(node a)const
        {
            re val<a.val;
        }
    }a[maxn];
    int main()
    {
    //    freopen("in.txt","r",stdin);
        rd(n);
        inc(i,1,n)
        {
            rd(a[i].val);
            a[i].id=i;
        }
        
        sort(a+1,a+n+1);
        
        inc(i,1,n)
        {
            vis[i]=1;
            hide[a[i].id]=i;
            L[i]=i-1;
            R[i]=i+1;
        }
        
        dec(now,n,2)
        {
            int i=hide[now];
            int l=L[i],r=R[i],minn=0x3f3f3f3f;
            while(l&&vis[l]==0)l=L[i];
            while(r&&vis[r]==0)r=R[r];
            if(l&&a[i].val-a[l].val<minn)
            {
                minn=abs(a[l].val-a[i].val);
                ans[a[i].id].pos=a[l].id;
            }
            if(r&&a[r].val-a[i].val<minn)
            {
                minn=a[r].val-a[i].val;
                ans[a[i].id].pos=a[r].id;
            }
            ans[a[i].id].val=minn;
            
            R[l]=r;
            L[r]=l;
            vis[i]=0;
        }
        
        inc(i,2,n)
        printf("%d %d
    ",ans[i].val,ans[i].pos);
        re 0;
    } 
    View Code

    0x30 Math

    其实数学只要就是推理过程

    0x31质数

    1.质数的判定(试除法||miller_robbin)

    2.质数的筛选

    eratosthenes筛法

    线性筛法

    2.质因数的分解

    算术基本定理

    试除法

    质数距离

    阶乘分解

    0x32约数

    1.算术基本定理推论

    2.试除法推论

    3.倍数法及其推论

    反素数

    见一本通

    余数之和

    #include<bits/stdc++.h>
    #define re return
    #define ll long long
    #define inc(i,l,r) for(int i=l;i<=r;++i)
    using namespace std;
    
    template<typename T>inline void rd(T&x)
    {
        char c;bool f=0;
        while((c=getchar())<'0'||c>'9')if(c=='-')f=1;
        x=c^48;
        while((c=getchar())>='0'&&c<='9')x=x*10+(c^48);
        if(f)x=-x;
    }
    
    ll n,k,gx;
    
    ll ans;
    
    int main()
    {
        rd(n);rd(k);
        ans=n*k;
        for(int x=1;x<=n;x=gx+1)
        {
            gx=k/x?min(k/(k/x),n):n;
            ans-=(k/x)*(gx-x+1)*(x+gx)/2;
        }
        
        printf("%lld",ans);
        re 0;
    } 
    View Code

    4.最大公约数

    5.更相减损术

    6.欧几里得法

    Hankson的趣味题

    7.互质与欧拉函数

    8.积性函数

    可见的点

    0x33同余

    1.同余类以及剩余类

    2.费马小定理

    3.欧拉定理及推论

     最幸运的数字

    #include<bits/stdc++.h>
    #define re return
    #define ll long long
    #define inc(i,l,r) for(ll i=l;i<=r;++i)
    using namespace std;
    template<typename T>inline void rd(T&x)
    {
        char c;bool f=0;
        while((c=getchar())<'0'||c>'9')if(c=='-')f=1;
        x=c^48;
        while((c=getchar())>='0'&&c<='9')x=x*10+(c^48);
        if(f)x=-x;
    }
    
    ll n;
    
    inline int gcd(ll a ,ll b){re b?gcd(b,a%b):a;}
    
    inline int Get_phi(ll x)
    {
        ll ans=x;
        ll largest=sqrt(x);
        for(int i=2;i<=largest;++i)
        if(x%i==0)
        {
            ans=ans*(i-1)/i;
            while(x%i==0)
            x=x/i; 
        }
        if(x!=1)
        ans=(ans)*(x-1)/x; 
        re (ll)ans;
    }
    
    inline ll pow(ll a,ll x,ll mod)
    {
        ll ret=1;
        while(x)
        {
            if(x&1)ret=(ret*a)%mod;
            a=(a*a)%mod;
            x>>=1;
        }
        re ret;
    }
    
    int main()
    {
    //    freopen("in.txt","r",stdin);
        int cnt=0;
        while(2333)
        {
            ++cnt;
            rd(n);
            if(!n)break;
            
            ll mod=n*9/gcd(8,n);
            
            if(gcd(mod,10)!=1){
                printf("Case %d: 0
    ",cnt);
                continue;
            }
            
            ll ol=Get_phi(mod);
            ll ans=9999999999999999;
            inc(i,1,sqrt(ol))
            {
                if(ol%i)continue;
                if(pow(10,i,mod)==1)ans=min(ans,i);
                if(pow(10,ol/i,mod)==1)ans=min(ans,ol/i);
            }
            printf("Case %d: %lld
    ",cnt,ans);
        }
        re 0;
    }
    View Code

    4.扩展欧几里得

    5.裴蜀定理

    6.乘法逆元

    7.线性同余方程

    同余方程

    #include<bits/stdc++.h>
    #define re return
    #define ll long long
    #define inc(i,l,r) for(int i=l;i<=r;++i)
    using namespace std;
    template<typename T>inline void rd(T&x)
    {
        char c;bool f=0;
        while((c=getchar())<'0'||c>'9')if(c=='-')f=1;
        x=c^48;
        while((c=getchar())>='0'&&c<='9')x=x*10+(c^48);
        if(f)x=-x;
    }
    
    inline ll exgcd(ll a,ll b,ll &x,ll &y)
    {
        if(!b)
        {
            x=1;y=0;//小心越界 
            re a;
        }
        
        ll d=exgcd(b,a%b,x,y);
        ll z=x;
        x=y;
        y=z-(a/b)*y;
        re d;
    }
    
    
    
    int main()
    {
        ll a,b,x,y;
        rd(a),rd(b);
        exgcd(a,b,x,y);
        
        printf("%lld",(x%b+b)%b);
        re 0;
    } 
    View Code

    8.中国剩余定理

    表达整数的奇怪方式

    9.高次同余方程(大步小步算法)

    0x34矩阵乘法

    将线性式横摆

    若状态矩阵中第x个数对下一单位时间状态矩阵中的第y个数有影响,

    则转移矩阵中的第x行第y列要赋予恰当的系数

     斐波那契

    石头游戏

    #include<bits/stdc++.h>
    #define re return
    #define ll long long
    #define inc(i,l,r) for(int i=l;i<=r;++i)
    using namespace std;
    template<typename T>inline void rd(T&x)
    {
        char c;bool f=0;
        while((c=getchar())<'0'||c>'9')if(c=='-')f=1;
        x=c^48;
        while((c=getchar())>='0'&&c<='9')x=x*10+(c^48);
        if(f)x=-x;
    }
    
    
    const int maxn=65;
    ll n,m,act,t,ym;
    ll opt[10][10],num[10][10],f[65];
    char s[10][10];
    struct node{
        ll a[65][65];
        inline void pre()
        {
            inc(i,0,ym)inc(j,0,ym)
            a[i][j]=0;
        }
        inline void Identity()
        {
            inc(i,0,ym)a[i][i]=1; 
        }
        inline node operator*(node c)
        {
            node d;
            d.pre();
            inc(i,0,ym)inc(j,0,ym)inc(k,0,ym)
            d.a[i][j]+=a[i][k]*c.a[k][j];
            re d;
        }
        
    
    }J[61],B;
    
    inline void muls(ll u[65],node L)
    {
        ll w[65];memset(w,0,sizeof(w));
        for(int j=0;j<=ym;j++)
            for(int k=0;k<=ym;k++)
                w[j]+=u[k]*L.a[k][j];
        memcpy(u,w,sizeof(w));
    }
    
    inline void mul(ll x)
    {
        while(x)
        {
            if(x&1)muls(f,B);
            B=B*B;
            x>>=1;
        }
    }
    
    int main()
    {
        //freopen("in.txt","r",stdin);
        scanf("%lld%lld%lld%lld",&n,&m,&t,&act);
        ym=n*m;
        char c;
        inc(i,1,n)inc(j,1,m)
        {
            while((c=getchar())<'0'||c>'9');
            opt[i][j]=c-48;
            num[i][j]=(i-1)*m+j;
        }
        inc(i,1,act)
        scanf("%s",s[i-1]+1);
        
        inc(i,1,n)inc(j,1,m)
        {
            ll now=opt[i][j],nowx=0,len=strlen(s[now]+1);
            inc(k,1,60)
            {
                J[k].a[0][0]=1;
                if(++nowx>len)nowx=1;
                switch(s[now][nowx])
                {
                    case 'N':if(i>1)J[k].a[num[i][j]][num[i-1][j]]=1;break;
                    case 'S':if(i<n)J[k].a[num[i][j]][num[i+1][j]]=1;break;
                    case 'W':if(j>1)J[k].a[num[i][j]][num[i][j-1]]=1;break;
                    case 'E':if(j<m)J[k].a[num[i][j]][num[i][j+1]]=1;break;
                    case 'D':break;        
                } 
                if('0'<=s[now][nowx]&&s[now][nowx]<='9')
                    J[k].a[num[i][j]][num[i][j]]=1,J[k].a[0][num[i][j]]=s[now][nowx]-48;    
            }
        }
        
    
        B.pre();B.Identity();
        inc(i,1,60)
        B=B*J[i];
        
        f[0]=1;
        ll w=t/60;
        mul(w);
        
        w=t%60;
        inc(i,1,w)
            muls(f,J[i]);
        
        ll ans=0;
        inc(i,1,ym)
        ans=max(ans,f[i]);
        
        printf("%lld",ans);
        re 0;
    } 
    View Code

     0x35高斯消元与线性空间

    1.高斯消元

    球形空间产生器

    开关问题

    2.线性空间

    装备购买

    0x36组合计数

    1.加法原理

    2.乘法原理

    3.排列与组合数

    4.二项式定理

     计算系数

    二项式定理

    5.多重集的排列与组合
     计数交换

    神神奇奇的转化+莫名其妙的多重集组合数

    6.lucas定理

    古代猪文

    欧拉定理+卢卡斯+crt

    好题(虽然很基础)

    7.catalan数列

    0x38 概率与数学期望

    大致就是让你找到一个期望值位1的状态,然后推到每一维状态

     Rainbow的信号

    已知枚举区间的总期望值为1

    可知你每次选的了l,r本身就是概率

    又由于位运算没有进位的限制

    所以我们针对每一位运算,每次O(n)枚举右区间r

    #include<bits/stdc++.h>
    #define re return
    #define dec(i,l,r) for(int i=l;i>=r;--i)
    #define inc(i,l,r) for(int i=l;i<=r;++i)
    using namespace std;
    template<typename T>inline void rd(T&x)
    {
        char c;bool f=0;
        while((c=getchar())<'0'||c>'9')if(c=='-')f=1;
        x=c^48;
        while((c=getchar())>='0'&&c<='9')x=x*10+(c^48);
        if(f)x=-x;
    }
    
    const int maxn=100005;
    int n,a[maxn],b[maxn],last[2];
    #define D double
    
    D ans_or,ans_xor,ans_and;
    
    inline void work(int k)
    {
        D sum=1<<k,ans_now=0;
        //sum是这一位原本的贡献(basic)
        //所以答案累计时要乘上去
    
        inc(i,1,n)
        {
            b[i]=(a[i]>>k)&1;
            if(b[i])ans_now+=sum*1.0/n/n;
            //区间为1的答案对于三个ans都有贡献
            //我就是懒~
        }
        ans_or+=ans_now;
        ans_xor+=ans_now;
        ans_and+=ans_now;
    
        last[0]=last[1]=0;
        //上一次0,1,出现位置
        int c1=0,c2=0;
        //出现1的个数和为对当前有贡献的个数c2
        //无贡献的个数c1
        inc(i,1,n)
        {
            if(!b[i])//如果这位不是1
            ans_or+=sum*last[1]*2.0/n/n;
            //异或和才有值=》第一位到最后一次出现1的位子为左边界
            else //如果是1
            {
                ans_or+=sum*(i-1)*2.0/n/n;//左边界任意
                ans_and+=sum*(i-1-last[0])*2.0/n/n;//必须是连续的一段1的左边界
            }
    
            ans_xor+=sum*2.0*(b[i]?c1:c2)/n/n;
            //当前数要异或偶数个1,还是奇数个1才能使当前值为1
            ++c1;//累加当前数统计异或数(当前数与当前数异或肯定为0)
            if(b[i])swap(c1,c2);//如果当前数为1,要异或个数的1就要轮转
            last[b[i]]=i;
        }
    }
    
    
    int main()
    {
        //freopen("in.txt","r",stdin);
        rd(n);
        inc(i,1,n)rd(a[i]);
    
        inc(i,0,30)//最多只有26位,但反正又不超,保险
        work(i);
        printf("%.3lf %.3lf %.3lf",ans_xor,ans_and,ans_or);
        re 0;
    } 
    View Code

    绿豆蛙的归宿

    只说了1可以到达任意位置,又没说1一定达到n

    不如设到达n的期望为一

    反向建图

    #include<bits/stdc++.h>
    #define re return
    #define dec(i,l,r) for(int i=l;i>=r;--i)
    #define inc(i,l,r) for(int i=l;i<=r;++i)
    using namespace std;
    template<typename T>inline void rd(T&x)
    {
        char c;bool f=0;
        while((c=getchar())<'0'||c>'9')if(c=='-')f=1;
        x=c^48;
        while((c=getchar())>='0'&&c<='9')x=x*10+(c^48);
        if(f)x=-x;
    }
    
    typedef double D;
    const int maxn=100005;
    D f[maxn];
    int n,m,k,in[maxn],hd[maxn],son[maxn];
    
    struct node{
        int to,nt;
        D val;
    }e[maxn<<1];
    
    inline void add(int x,int y,int z)
    {
        e[++k].to=y;e[k].nt=hd[x];hd[x]=k;e[k].val=z;
    } 
    
    inline void topo()
    {
        queue<int>q;
    
        q.push(n);
        
        f[n]=0;
        
        while(!q.empty())
        {
            int x=q.front();
            if(x==1)re;
            q.pop();
            
            for(int i=hd[x];i;i=e[i].nt)
            {
                int v=e[i].to;
                f[v]+=1.0*(f[x]+e[i].val)/son[v];
                --in[v];
                if(!in[v])
                q.push(v);
            }
        }
    }
    
    int main()
    {
    //    freopen("in.txt","r",stdin);
        
        int x,y,z;
        rd(n),rd(m);
        inc(i,1,m)
        {
            rd(x),rd(y),rd(z);
            add(y,x,z);
            ++in[x];
            ++son[x];
        }
        
        topo();
        
        printf("%.2lf",f[1]);
        
        re 0;
    } 
    View Code

    0x40 数据结构的膜法变身

    0x41 曾经,我也只是一只单纯的并查集

    1.路径压缩与按秩合并

    如果你学过并查集的话,那么你一定感慨曾见过只穿了路径压缩的冰茶姬

    所谓按秩合并就是启发式(小入大)

    题:程序自动分析

    2.扩展域与边带权

    所谓扩展域,也可以理解为一种容易实现的边带权

    (以带权的奇偶性来表示是否在同一集合)

    不过看来,边带权的可运用性更加广泛

    看书,有笔记

    0x41 震惊!线段树竟然有一个血缘关系高达99%

    的亲戚——短小精悍的树状数组

    1.单点修改,区间求和

    2.求逆序对

    楼兰图腾

    常规做法

    3.in combination with 查分

    区间修改,单点查询

    一个简单的整数问题

    区间修改,区间查询

    一个简单的整数问题2

    你要推一波式子

    4.else

    谜一样的牛

    首先想到模拟,然后用二分或倍增优化

    0x42 你从未见过的船新版本

    1.区间最大公约数

    明明就只是弄个差分,取个绝对值,在球球gcd

    为什么我kao了2天

    #include<bits/stdc++.h>
    #define re return
    #define inc(i,l,r) for(int i=l;i<=r;++i)
    using namespace std;
    template <typename T> 
    inline void rd(T &s) {
        s = 0; 
        T w = 1, ch = getchar(); 
        while (!isdigit(ch)) { if (ch == '-') w = -1; ch = getchar(); }
        while (isdigit(ch)) { s = (s << 1) + (s << 3) + (ch ^ 48); ch = getchar(); }
        s *= w; 
    }
    #define ll long long
    #define lowbit(x) (x&(-x))
    const long long maxn=5e5+5;
    ll n,m;
    ll a[maxn],c[maxn],b[maxn];
    
    inline void add(int pos,ll x)
    {
        for(;pos<=n;pos+=lowbit(pos))
            c[pos]+=x;
    }
    
    inline ll sum(int pos)
    {
        ll ret=0;
        for(;pos;pos-=lowbit(pos))
        ret+=c[pos];
        re ret;
    }
    
    struct tree 
    {
        ll l,r,val;
    }t[maxn<<2];
    
    #define ls rt<<1
    #define rs rt<<1|1
    ll gcd(ll a,ll b){b?gcd(b,a%b):a;}
    
    inline void build(int rt,int l,int r)
    {
        t[rt].l=l;t[rt].r=r;
        if(l==r)
        {
            t[rt].val=b[l];
            re;
        }
        
        int mid=(l+r)>>1;
        build(ls,l,mid);
        build(rs,mid+1,r);
        t[rt].val=gcd(t[ls].val,t[rs].val);
    }
    
    inline void addt(ll rt,ll pos,ll x)
    {
        if(t[rt].l==t[rt].r)
        {
            t[rt].val+=x;
            re ;
        }
        
        ll mid=(t[rt].l+t[rt].r)>>1;
        if(pos<=mid)addt(ls,pos,x);
        else addt(rs,pos,x);
        t[rt].val=gcd(t[ls].val,t[rs].val);
    }
    
    inline ll query(ll rt,ll x,ll y)
    {
        if(x<=t[rt].l&&t[rt].r<=y)
        re abs(t[rt].val);
        ll mid=(t[rt].l+t[rt].r)>>1;
        ll ans1=0,ans2=0;
        if(x<=mid)ans1=query(ls,x,y);
        if(y>mid) ans2=query(rs,x,y);
        re abs(gcd(ans1,ans2));
    }
    
    
    int main()
    {
        cin>>n>>m;
        inc(i,1,n)
        {
            scanf("%lld",&a[i]);
            b[i]=a[i]-a[i-1];
        }
    
        build(1,1,n);
        
        ll x,y,z;
        char opt[4];
        inc(i,1,m)
        {
            scanf("%s",opt);
            scanf("%lld%lld",&x,&y);
            if(opt[0]=='C')
            {
                scanf("%lld
    ",&z);
                if(y<n)addt(1,y+1,-z);
                add(x,z);addt(1,x,z);
                add(y+1,-z);
            }
            else
            {
                scanf("
    ");
                ll ans1=a[x]+sum(x),ans2=0;
                if(x<y)ans2=query(1,x+1,y);
                printf("%lld
    ",gcd(ans1,ans2));
            }
        }
        re 0;
    } 
    View Code

    0x50 DP大法吼啊

    0x51 线性dp

    我就是个lj

    都是基础的dp+一些小优化

    杨老师的照相排列

     最长公共上升子序列

    分级

    还有一些

    自己跳过去看吧

    0x52 背包

    1.0/1背包

    滚动数组

    倒序

    2.完全背包

    0/1正序就是

    3.多重背包

    朴素不如二进制 ,二进制不如单调队列

    4.分组背包

    某种形式上的0/1

     陪审团

    多维0/1背包,考虑适当优化

    0x53 区间dp

    1.石子合并 欧阳举了无数次例子的题

    2.Polygon

    比较常规的区间dp了

    要注意转移时负负得正等运算

    #include<bits/stdc++.h>
    #define re  return
    #define inc(i,l,r) for(int i=l;i<=r;++i) 
    using namespace std;
    template<typename T>inline void rd(T&x)
    {
        char c;bool f=0;
        while((c=getchar())<'0'||c>'9')if(c=='-')f=1;
        x=c^48;
        while((c=getchar())>='0'&&c<='9')x=x*10+(c^48);
        if(f)x=-x; 
    }
    
    const int maxn=105;
    int n,f[101][101][2],opt[maxn],a[maxn];
    inline void swup(int &x,int &y)
    {
        x^=y^=x^=y;
    }
    
    int main()
    {
    //    freopen("in.txt","r",stdin);
        
        rd(n);
        
        inc(i,1,n<<1)inc(j,1,n<<1)
        {
            f[i][j][1]=-6666666;
            f[i][j][0]=77777777;
        }
        char op[5];
        inc(i,1,n)
        {
            scanf("%s",op);
            rd(a[i]);
            a[i+n]=a[i];
            f[i][i][0]=f[i][i][1]=a[i]; 
            f[i+n][i+n][0]=f[i+n][i+n][1]=a[i]; 
            opt[i]=(op[0]=='t');//为加是true 
            opt[i+n]=opt[i];
        }
        int max1,min1,max2,min2; 
        inc(L,1,n-1)
        inc(i,1,n+n-L)
        {
            int j=i+L;
            inc(k,i+1,j)
            if(opt[k])//
            {
                f[i][j][1]=max(f[i][j][1],f[i][k-1][1]+f[k][j][1]);
                f[i][j][0]=min(f[i][j][0],f[i][k-1][0]+f[k][j][0]);
            }
            else 
            {
                max1=f[i][k-1][1]*f[k][j][1],min1=f[i][k-1][0]*f[k][j][0];
                if(max1<min1)swup(max1,min1);
                max2=f[i][k-1][1]*f[k][j][0],min2=f[i][k-1][0]*f[k][j][1];
                if(max2<min2)swup(max2,min2);
                f[i][j][1]=max(f[i][j][1],max(max1,max2));
                f[i][j][0]=min(f[i][j][0],max(min1,min2));
            }
            
        }
        
        int ans=-5555555,cnt;
        int q[maxn];
        
        inc(i,1,n)
        if(f[i][i+n-1][1]>ans)
        {
            ans=f[i][i+n-1][1];
            q[cnt=1]=i;
        }
        else if(ans==f[i][i+n-1][1])
        q[++cnt]=i;
        
        printf("%d
    ",ans);
        inc(i,1,cnt)
        printf("%d ",q[i]);
        re 0;
    } 
    View Code

    3.金字塔

    要判断每棵子树进出点应该是同一个数,才行

    #include<bits/stdc++.h>
    #define ll long long
    #define re  return
    #define inc(i,l,r) for(int i=l;i<=r;++i) 
    using namespace std;
    template<typename T>inline void rd(T&x)
    {
        char c;bool f=0;
        while((c=getchar())<'0'||c>'9')if(c=='-')f=1;
        x=c^48;
        while((c=getchar())>='0'&&c<='9')x=x*10+(c^48);
        if(f)x=-x; 
    }
    
    ll n,f[305][305],mod=1000000000;
    char s[305];
    //为什么只用枚举第一棵子树呢,一个是防重,也方便统计 
    
    inline ll dfs(int l,int r)
    {
        if(s[l]!=s[r])re 0;
        if(l==r)re 1;
        if(f[l][r]!=-1)re f[l][r];
        
        f[l][r]=0;
        inc(k,l+2,r)
        f[l][r]=(f[l][r]+dfs(l+1,k-1)*dfs(k,r)%mod)%mod;
        
        re f[l][r];
    }
    
    int main()
    {
    //    freopen("in.txt","r",stdin);
        memset(f,-1,sizeof f);
        scanf("%s",s+1);
        printf("%lld",dfs(1,strlen(s+1))); 
        re 0;
    } 
    View Code

     0x54 树形dp

    1.基础dp

    由根分为左右子树两部分情况 

    二叉苹果树

    树的最长链

    数字转换

    树的最大独立子集

    战略游戏

    普通树的dp

    跟他的儿子与父亲有着不可告人的关系

    皇宫看守

    2.背包类树形dp

    分组背包

    选课

    3.换根与二次扫描法

    对于不定根求解

    Accumulation Degree

    #include<iostream>
    #define re return
    #define inc(i,l,r) for(int i=l;i<=r;++i)
    using namespace std;
    template<typename T>inline void rd(T&x)
    {
        char c;bool f=0;
        while((c=getchar())<'0'||c>'9')if(c=='-')f=1;
        x=c^48;
        while((c=getchar())>='0'&&c<='9')x=x*10+(c^48);
        if(f)x=-x;
    }
    
    const int maxn=200005;
    
    int n,m,k,hd[maxn],in[maxn],D[maxn],f[maxn];
    struct node{
        int to,nt,val;
    }e[maxn<<1];
    
    inline void add(int x,int y,int z)
    {
        ++in[x];++in[y];
        e[++k].to=y;e[k].nt=hd[x];hd[x]=k;e[k].val=z;
        e[++k].to=x;e[k].nt=hd[y];hd[y]=k;e[k].val=z;
    }
    
    inline void dfs(int x,int fa)
    {
    
        for(int i=hd[x];i;i=e[i].nt)
        {
            int v=e[i].to;
            if(v==fa)continue;
            dfs(v,x);
            if(in[v]==1)D[x]+=e[i].val;
            else D[x]+=min(D[v],e[i].val);
        }
    }
    
    inline void dfs1(int x,int fa)
    {
        for(int i=hd[x];i;i=e[i].nt)
        {
            int v=e[i].to;
            if(v==fa)continue;
            if(in[x]==1)f[v]=D[v]+e[i].val;
            else f[v]=D[v]+min(f[x]-min(D[v],e[i].val),e[i].val);
            dfs1(v,x);
        }
    }
    
    int main()
    {
        //freopen("in.txt","r",stdin); 
        int T,x,y,z;
        rd(T);
        while(T--)
        {
            rd(n);
            k=0;
            inc(i,1,n)hd[i]=D[i]=f[i]=in[i]=0;
            inc(i,2,n)
            {
                rd(x),rd(y),rd(z);
                add(x,y,z);
            }
            dfs(1,1);
            f[1]=D[1];
            dfs1(1,1);
            
            int ans=0;
            inc(i,1,n)
            if(f[i]>f[ans])
            ans=i;
            printf("%d
    ",f[ans]);
        }
        re 0;
    }
    View Code

    0x55 环形与后效性处理

    1.环形

    要么开二倍拆链

    要么强制选或不选

    a.Naptime

    #include<bits/stdc++.h>
    #define re return
    #define inc(i,l,r) for(int i=l;i<=r;++i)
    using namespace std;
    template<typename T>inline void rd(T&x)
    {
        char c;bool f=0;
        while((c=getchar())<'0'||c>'9')if(c=='-')f=1;
        x=c^48;
        while((c=getchar())>='0'&&c<='9')x=x*10+(c^48);
        if(f)x=-x; 
    }
    const int maxn=4000;
    
    int n,m,b,ans,f[maxn][maxn][2],val[maxn];
    
    inline void dp()
    {
        inc(now,2,n)
        {
            int i=now&1;
            inc(j,0,min(now-1,m))
            {
                
                f[i][j][0]=max(f[i^1][j][0],f[i^1][j][1]);
                if(j)f[i][j][1]=max(f[i^1][j-1][0],f[i^1][j-1][1]+val[now]);
            }
        }    
    }
    
    inline void pre()
    {
        inc(i,0,1)
        inc(j,0,m)
        f[i][j][0]=f[i][j][1]=-1147483647;
    }
    
    int main()
    {
        //freopen("in.txt","r",stdin);
        rd(n),rd(m);
        inc(i,1,n)rd(val[i]);
        
        pre();    
        f[1][0][0]=f[1][1][1]=0;
        dp();
        ans=max(f[n&1][m][1],f[n&1][m][0]); 
        
        pre();
        f[1][1][1]=val[1];//因为会强制不选
        dp();
        ans=max(ans,f[n&1][m][1]);
        
        printf("%d",ans);
        re 0;
    }
    View Code

    b.环路运输

    也可以不拆,但拆了更简单

    单调队列

    #include<bits/stdc++.h>
    #define re return
    #define inc(i,l,r) for(int i=l;i<=r;++i)
    using namespace std;
    template<typename T>inline void rd(T&x)
    {
        char c;bool f=0;
        while((c=getchar())<'0'||c>'9')if(c=='-')f=1;
        x=c^48;
        while((c=getchar())>='0'&&c<='9')x=x*10+(c^48);
        if(f)x=-x; 
    }
    
    const int maxn=1e6+5;
    int n,a[maxn<<1],ans;
    
    int q[maxn<<1];
    
    int main()
    {
        freopen("in.txt","r",stdin);
        rd(n);
        inc(i,1,n)
        {
            rd(a[i]);
            a[i+n]=a[i];
        }
        
        int nn=n/2;
        int tail=0,head=1;
        inc(i,1,n<<1)
        {
            if(head<=tail&&i-q[head]>nn)++head;
            ans=max(ans,a[q[head]]+a[i]+i-q[head]);
            while(head<=tail&&a[i]-i>=a[q[tail]]-q[tail])--tail;
            q[++tail]=i;
        } 
        
        printf("%d",ans);
        re 0;
    }
    View Code

    2.有后效性

    0x57 倍增优化dp

    1.预处理

    数据小,易处理

    2.倍增

    常数极大

    3.求解

    开车旅行

    计算重复

    为什么这道题不能有scanf读入

    #include<bits/stdc++.h>
    #define re return
    #define ll long long
    #define dec(i,l,r) for(ll i=l;i>=r;--i) 
    #define inc(i,l,r) for(ll i=l;i<=r;++i)
    using namespace std;
    template<typename T>inline void rd(T&x)
    {
        char c;bool f=0;
        while((c=getchar())<'0'||c>'9')if(c=='-')f=1;
        x=c^48;
        while((c=getchar())>='0'&&c<='9')x=x*10+(c^48);
        if(f)x=-x;
    }
    
    const int maxn=105;
    ll n1,n2,l1,l2,f[maxn][32];
    
    char s1[105],s2[105];
    
    int main()
    {
    //    freopen("in.txt","r",stdin);
        while(cin>>s2>>n2>>s1>>n1)
        {
    
            l1=strlen(s1);
            l2=strlen(s2);
            //PRE
            int flag=0;
            inc(i,0,l1-1)
            {
                f[i][0]=0;
                ll pos=i;
                inc(j,0,l2-1)
                {
                    ll cnt=0;
                    while(s1[pos]!=s2[j])
                    {
                        pos=(1+pos)%l1;    
                        if(++cnt>l1+1)
                        {
                            flag=1;
                            break;
                        }
                    }
                    if(flag) break;
                    pos=(1+pos)%l1;//下一位开始 
                    
                    f[i][0]+=cnt+1;//往后累加一位 
                }
                if(flag)break;
            }
            
            if(flag)
            printf("0
    ");
            else 
            {
                //倍增
                inc(i,0,30)
                inc(j,0,l1-1)
                f[j][i+1]=f[j][i]+f[(j+f[j][i])%l1][i];
                
                ll m=0;
            /*    inc(j,0,l1-1)
                {*/
                    ll pos=0,cnt=0;
                    dec(i,31,0)
                    if(pos+f[pos%l1][i]<=n1*l1)
                    {
                        pos+=f[pos%l1][i];
                        cnt+=1<<i;
                    }
                    m=max(m,cnt);
            //    }
                
                printf("%lld
    ",m/n2); 
            }
        }
    }
    View Code

    0x60  tulun

    0x61 最短路

    1.单源最短路

    dijkstra+spfa+bellman-ford

    通信线路 
    最优贸易 

    正反两遍spfa
    道路与航线 

    分开处理

    2.floyd

    任意两点最短路

    传递闭包

     排序 

    传递关系
    观光之旅 

    longlong memset赋0x3f3f3f3f(八位字节会爆)
     牛站

    矩阵最短路???

    0x62最小生成树kuskal+prim

    由于最小生成树太水了,所以总要结合一些奇奇怪怪的算法

    走廊泼水节

    累和型dp
    野餐规划

    强制选择型dp
    沙漠之王

    0/1分数型dp
    黑暗城堡

    计数型dp

    0x63 树的直径与最近公共祖先

    1.树的直径

    巡逻

    竟然恶心的要用2次dfs(无法求负权)与树形dp(无法求路径)

    分别求一次树的直径

    #include<bits/stdc++.h>
    #define re return
    #define inc(i,l,r) for(int i=l;i<=r;++i)
    using namespace std;
    template<typename T>inline void rd(T&x)
    {
        char c;bool f=0;
        while((c=getchar())<'0'||c>'9')if(c=='-')f=1;
        x=c^48;
        while((c=getchar())>='0'&&c<='9')x=x*10+(c^48);
        if(f)x=-x; 
    }
    
    const int maxn=200005;
    int n,m,k=1,ans,total,K,hd[maxn],d[maxn],deep,fa[maxn];
    struct node{
        int to,nt,val;
    }e[maxn<<1];
    
    inline void add(int x,int y)
    {
        e[++k].to=y;e[k].nt=hd[x];hd[x]=k;e[k].val=1;
        e[++k].to=x;e[k].nt=hd[y];hd[y]=k;e[k].val=1;
    }
    
    inline void dfs(int x)
    {
        for(int i=hd[x];i;i=e[i].nt)
        {
            int v=e[i].to;
            if(v==fa[x])continue;
            fa[v]=x;
            d[v]=d[x]+e[i].val;
            if(d[deep]<d[v])deep=v;
            dfs(v);
        }
    }
    
    inline void dfs1(int x)
    {
        for(int i=hd[x];i;i=e[i].nt)
        {
            int v=e[i].to;
            if(v==fa[x])
            {
                e[i].val=e[i^1].val=-1;
                dfs1(v);
                re; 
            }
        }
    }
    
    inline void Get_long()
    {
        deep=0;
        d[1]=0;fa[1]=1;
        dfs(1);
        int rt=deep;
        
        deep=0;
        d[rt]=0;fa[rt]=rt;
        dfs(rt);
    }
    
    inline void dfs2(int x)
    {
        int mson=0;
        for(int i=hd[x];i;i=e[i].nt)
        {
            int v=e[i].to;
            if(v==fa[x])continue;
            d[v]=0;
            fa[v]=x;
            dfs2(v);
            d[v]+=e[i].val;
            ans=max(ans,d[v]+d[mson]);
            if(d[mson]<d[v])mson=v;
        }
        d[x]=max(0,d[mson]); 
    }
    
    int main()
    {
    //    freopen("in.txt","r",stdin);
        rd(n),rd(K);
        
        int x,y;
        inc(i,2,n)
        {
            rd(x),rd(y);
            add(x,y); 
        }
        
        total=n*2-2;
        
        Get_long();
        total=total-d[deep]+1;
        if(K==1);
        else 
        {
            dfs1(deep);
            
            d[1]=0;fa[1]=1;
            dfs2(1);
            total=total-ans+1;
        }
        printf("%d",total);
        re 0;
    } 
    View Code

    树网的核

    noip n=500的数据之水,n^3都阔以

    #include<bits/stdc++.h>
    #define re return
    #define Min(a,b) (a)<(b)?(a):(b)
    #define Max(a,b) (a)>(b)?(a):(b)
    #define inc(i,l,r) for(register int i=l;i<=r;++i)
    using namespace std;
    template<typename T>inline void rd(T&x)
    {
        char c;bool f=0;
        while((c=getchar())<'0'||c>'9')if(c=='-')f=1;
        x=c^48;
        while((c=getchar())>='0'&&c<='9')x=x*10+(c^48);
        if(f)x=-x; 
    }
    
    const int maxn=500005;
    
    int n,m,S,k,ans=0x3f3f3f3f,deep;
    int hd[maxn],dis[maxn],d[maxn],fa[maxn],vis[maxn];
    
    struct node{
        int to,nt,val;
    }e[maxn<<1];
    
    inline void add(int x,int y,int z)
    {
        e[++k].to=y;e[k].nt=hd[x];hd[x]=k;e[k].val=z;
        e[++k].to=x;e[k].nt=hd[y];hd[y]=k;e[k].val=z;
    }
    
    inline void dfs(int x)
    {
        if(d[deep]<d[x])deep=x;
        for(int i=hd[x];i;i=e[i].nt)
        {
            int v=e[i].to;
            if(v==fa[x])continue;
            fa[v]=x;
            d[v]=d[x]+e[i].val;
            dfs(v);
    
        }
    }
    inline void dfs1(int x)
    {
        for(int i=hd[x];i;i=e[i].nt)
        {
            int v=e[i].to;
            if(v==fa[x])continue;
            dfs1(v);
            if(!vis[v])dis[x]=max(dis[x],dis[v]+e[i].val);
        }
    }
        
    inline void Get_long()
    {
        dfs(1);
        
        int rt=deep;
        deep=0;
        fa[rt]=0;
        d[rt]=0;
        dfs(rt);
        
        for(int i=deep,j=deep;i;i=fa[i])
        {
            vis[i]=1;
            while(j&&d[i]-d[fa[j]]<=S)j=fa[j];
            ans=min(ans,max(d[deep]-d[i],d[j]));
        }
        
        d[rt]=0;
        dfs1(rt);
        
        for(int i=deep;i;i=fa[i])
        ans=max(ans,dis[i]);
    }
    int main()
    {
    //    freopen("in.txt","r",stdin);
        int x,y,z;
        rd(n),rd(S);
        inc(i,2,n)
        {
            rd(x),rd(y),rd(z);
            add(x,y,z);
        }
        
        Get_long(); 
        
        printf("%d",ans);
        re 0;
    } 
    View Code

    你看看bzoj n=500000

    我巨大的常数都卡不了

    #include<bits/stdc++.h>
    #define re return
    #define Min(a,b) (a)<(b)?(a):(b)
    #define Max(a,b) (a)>(b)?(a):(b)
    #define inc(i,l,r) for(register int i=l;i<=r;++i)
    using namespace std;
    template<typename T>inline void rd(T&x)
    {
        char c;bool f=0;
        while((c=getchar())<'0'||c>'9')if(c=='-')f=1;
        x=c^48;
        while((c=getchar())>='0'&&c<='9')x=x*10+(c^48);
        if(f)x=-x; 
    }
    
    const int maxn=500005;
    
    int n,m,S,k,ans=0x3f3f3f3f,deep;
    int hd[maxn],dis[maxn],d[maxn],fa[maxn],vis[maxn];
    
    struct node{
        int to,nt,val;
    }e[maxn<<1];
    
    inline void add(int x,int y,int z)
    {
        e[++k].to=y;e[k].nt=hd[x];hd[x]=k;e[k].val=z;
        e[++k].to=x;e[k].nt=hd[y];hd[y]=k;e[k].val=z;
    }
    
    inline void dfs(int x)
    {
        if(d[deep]<d[x])deep=x;
        for(int i=hd[x];i;i=e[i].nt)
        {
            int v=e[i].to;
            if(v==fa[x])continue;
            fa[v]=x;
            d[v]=d[x]+e[i].val;
            dfs(v);
    
        }
    }
    inline void dfs1(int x)
    {
        for(int i=hd[x];i;i=e[i].nt)
        {
            int v=e[i].to;
            if(v==fa[x])continue;
            dfs1(v);
            if(!vis[v])dis[x]=max(dis[x],dis[v]+e[i].val);
        }
    }
        
    inline void Get_long()
    {
        dfs(1);
        
        int rt=deep;
        deep=0;
        fa[rt]=0;
        d[rt]=0;
        dfs(rt);
        
        for(int i=deep,j=deep;i;i=fa[i])
        {
            vis[i]=1;
            while(j&&d[i]-d[fa[j]]<=S)j=fa[j];
            ans=min(ans,max(d[deep]-d[i],d[j]));
        }
        
        d[rt]=0;
        dfs1(rt);
        
        for(int i=deep;i;i=fa[i])
        ans=max(ans,dis[i]);
    }
    int main()
    {
        freopen("in.txt","r",stdin);
        int x,y,z;
        rd(n),rd(S);
        inc(i,2,n)
        {
            rd(x),rd(y),rd(z);
            add(x,y,z);
        }
        
        Get_long(); 
        
        printf("%d",ans);
        re 0;
    } 
    View Code

    2.lca 

    3.差分

    暗的连锁

    附加边产生环

    对附加边差分(塞到两点里)

    代表从他父亲到他的主要边被几个环覆盖

    0个环任意切第二刀

    1环绑定切一刀

    其他不成立

    #include<bits/stdc++.h>
    #define re return
    #define dec(i,l,r) for(int i=l;i>=r;--i)
    #define inc(i,l,r) for(int i=l;i<=r;++i)
    using namespace std;
    template<typename T>inline void rd(T&x)
    {
        char c;bool f=0;
        while((c=getchar())<'0'||c>'9')if(c=='-')f=1;
        x=c^48;
        while((c=getchar())>='0'&&c<='9')x=x*10+(c^48);
    }
    
    const int maxn=1e5+5;
    
    int n,m,k,hd[maxn<<1],d[maxn],ans,fa[maxn][25],deep[maxn];
    struct node{
        int to,nt;
    }e[maxn<<1];
    inline void add(int x,int y)
    {
        e[++k].to=y;e[k].nt=hd[x];hd[x]=k;
        e[++k].to=x;e[k].nt=hd[y];hd[y]=k;
    }
    
    inline void dfs(int x,int pre)
    {
        deep[x]=deep[pre]+1; 
        for(int i=0;fa[fa[x][i]][i];++i)
            fa[x][i+1]=fa[fa[x][i]][i];
        
        for(int i=hd[x];i;i=e[i].nt)
        {
            int v=e[i].to;
            if(v==pre)continue;
            fa[v][0]=x;
            dfs(v,x);
        }
    }
    
    inline int LCA(int x,int y)
    {
        if(deep[x]<deep[y])swap(x,y);
        
        dec(i,20,0)
        if(deep[fa[x][i]]>=deep[y])
            x=fa[x][i];
        
        if(x==y)re x;
        dec(i,20,0)
        if(fa[x][i]!=fa[y][i])
        {
            x=fa[x][i];
            y=fa[y][i];
        }
        re fa[x][0];
    }
    
    inline void Get_ans(int x,int pre)
    {
        for(int i=hd[x];i;i=e[i].nt)
        {
            int v=e[i].to;
            if(v==pre)continue;
            Get_ans(v,x);
            d[x]+=d[v];
        }
        if(!pre)re ;
        if(d[x]==1)++ans;
        if(!d[x])ans+=m;
    }
    int main()
    {
        
        //freopen("in.txt","r",stdin);
        int x,y;
        rd(n),rd(m);
        inc(i,2,n)
        {
            rd(x),rd(y);
            add(x,y);
        }
        
        dfs(1,0);
        inc(i,1,m)
        {
            rd(x),rd(y);
            int v=LCA(x,y);
            ++d[x];
            ++d[y];
            d[v]-=2;
        }
        
        Get_ans(1,0);
        
        printf("%d",ans); 
        re 0;
    } 
    View Code

    雨天的尾巴

    天天爱跑路

     4.lca的综合应用

    异象石

    set应用
    次小生成树 

    dp,dp还是dp
    疫情控制

    stl,dp

    0x65负环与差分约束

    1.负环

    观光奶牛 

    熟悉的味道,熟悉的配方

    是的,0/1分数规划

    2.差分约束

    区间

  • 相关阅读:
    Discuz X 2.5 点点(伪静态)
    jq 、xml 省市级联动
    php memcache 初级使用(2)
    关于windows虚拟内存管理的页目录自映射
    SharePoint 2010 网络上的开发经验和资源
    SharePoint 2010 Reporting Services 报表服务器正在内置 NT AUTHORITY\SYSTEM 账户下运行 解决方法
    SharePoint 2010 Reporting Services 报表服务器无法解密用于访问报表服务器数据库中的敏感数据或加密数据的对称密钥 解决方法
    Active Directory Rights Management Services (AD RMS)无法检索证书层次结构。 解决方法
    SharePoint 2010 Reporting Services 报表服务器实例没有正确配置 解决方法
    SharePoint 2010 页面引用 Reporting Services 展现 List 报表
  • 原文地址:https://www.cnblogs.com/lsyyy/p/11446537.html
Copyright © 2011-2022 走看看