算法竞赛进阶指南修习简记

【一些要要补的东西】

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.差分约束

区间

原文地址:https://www.cnblogs.com/lsyyy/p/11446537.html