8/24 差分lca+数学抽屉原理

P1351 [NOIP2014 提高组] 联合权值

思路:
1.需要找到中间结点.若该点周围有两个顶点,则贡献为(a+b)^2- a^2 -b^2;若该点周围有三个顶点,则贡献为2ab+2ac+2bc=(a+b+c)^2-a^2-b^2-c^2
2.对于贡献的最大值,不需要进行取模操作。
3.对于每一个点进行判断,所有和它相连的点彼此间距离都为2,在其中判断出最大值,累加和即可。

#include<bits/stdc++.h>
#define int long long
#define endl '\n'
#define For(i,a,b) for(i=(a);i<=(b);++i)
#define ios (ios::sync_with_stdio(false),cin.tie(0),cout.tie(0))
using namespace std;
const int N=7e5+7;
const int inf=0x3f3f3f3f;
const int mod=1e4+7;
int n,a[N],ans,mx;
int dep[N];     //存u点的深度
int fa[N][20];  //存从u点向上跳2^i层的祖先节点
vector<int>e[N];
void dfs(int u,int pare)
{
    dep[u]=dep[pare]+1;
    fa[u][0]=pare;
    for(int i=1;i<=19;i++)
        fa[u][i]=fa[fa[u][i-1]][i-1];
    for(int v:e[u])
        if(v!=pare) dfs(v,u);
}

//利用st表求lca
int lca(int u,int v)
{
    if(dep[u]<dep[v]) swap(u,v);
    for(int i=19;i>=0;i--)
        //先跳到同一层
        if(dep[fa[u][i]]>=dep[v])
            u=fa[u][i];
    if(u==v)    return v;
    //跳到lca的下一层
    for(int i=19;i>=0;i--)
        if(fa[u][i]!=fa[v][i])
        u=fa[u][i],v=fa[v][i];
    return fa[u][0];
}

void solve()
{
    cin>>n;
    for(int i=1;i<n;i++)
    {
        int u,v;cin>>u>>v;
        e[u].push_back(v);e[v].push_back(u);
    }
    for(int i=1;i<=n;i++)
        cin>>a[i];
    int mx=0;
    for(int i=1;i<=n;i++)
    {
        int mx1=0,mx2=0;
        int t1=0,t2=0;
        for(int j:e[i])
        {
            if(a[j]>mx1) mx2=mx1,mx1=a[j];
            else if(a[j]>mx2)
                mx2=a[j];
            t1=(t1+a[j])%mod;
            t2=(t2+a[j]*a[j])%mod;
        }
        t1=t1*t1%mod;
        ans=(ans+t1-t2+mod)%mod;
        if(mx<mx1*mx2)
            mx=mx1*mx2;
    }
    cout<<mx<<" "<<ans<<endl;
}
signed main()
{
    //ios;
    //int T;cin>>T;
    //while(T--)
        solve();
    return 0;
}

P3258 [JLOI2014]松鼠的新家

差分LCA,将一条路径上经过的点都加上1
处理方法:将两个起点都加上1,其lca的值g减去1,再将g的父亲f[g][0]的值减去1

#include<bits/stdc++.h>
#define int long long
#define endl '\n'
#define For(i,a,b) for(i=(a);i<=(b);++i)
#define ios (ios::sync_with_stdio(false),cin.tie(0),cout.tie(0))
using namespace std;
const int N=7e5+7;
const int inf=0x3f3f3f3f;
const int mod=1e9+7;
int n,a[N],ans,mx,num[N];
int dep[N];     //存u点的深度
int fa[N][20];  //存从u点向上跳2^i层的祖先节点
vector<int>e[N];
void dfs(int u,int pare)
{
    dep[u]=dep[pare]+1;
    fa[u][0]=pare;
    for(int i=1;i<=19;i++)
        fa[u][i]=fa[fa[u][i-1]][i-1];
    for(int v:e[u])
        if(v!=pare) dfs(v,u);
}

//利用st表求lca
int lca(int u,int v)
{
    if(dep[u]<dep[v]) swap(u,v);
    for(int i=19;i>=0;i--)
        //先跳到同一层
        if(dep[fa[u][i]]>=dep[v])
            u=fa[u][i];
    if(u==v)    return v;
    //跳到lca的下一层
    for(int i=19;i>=0;i--)
        if(fa[u][i]!=fa[v][i])
        u=fa[u][i],v=fa[v][i];
    return fa[u][0];
}
void sum(int u,int pare)
{
    for(int v:e[u])
    {
        if(v==pare) continue;
        sum(v,u);
        num[u]+=num[v];
    }
}
void solve()
{
    cin>>n;
    for(int i=1;i<=n;i++)
        cin>>a[i];
    for(int i=1;i<n;i++)
    {
        int u,v;cin>>u>>v;
        e[u].push_back(v);e[v].push_back(u);
    }
    dfs(1,0);
    for(int i=1;i<n;i++)
    {
        int u=a[i],v=a[i+1];
        int g=lca(u,v);
        num[fa[g][0]]-=1;
        num[g]-=1;
        num[u]+=1;
        num[v]+=1;
    }
    sum(1,0);
    for(int i=2;i<=n;i++)
        num[a[i]]--;
    for(int i=1;i<=n;i++)
        cout<<num[i]<<endl;
}
signed main()
{
    //ios;
    //int T;cin>>T;
    //while(T--)
        solve();
    return 0;
}

P6869 [COCI2019-2020#5] Putovanje

思路:
1.本题也是应用差分LCA,但不同的是,本题是对于边的差分,累加经过的边数,可直接将两点的lca直接减去2.
2.可通过“反悔边”来确定所连的边,从而使用结构内其他成员c1,c2.

#include<bits/stdc++.h>
#define int long long
#define endl '\n'
#define For(i,a,b) for(i=(a);i<=(b);++i)
#define ios (ios::sync_with_stdio(false),cin.tie(0),cout.tie(0))
using namespace std;
const int N=7e5+7;
const int inf=0x3f3f3f3f;
const int mod=1e9+7;
int n,a[N],ans,head[N],cnt,num[N],c1[N],c2[N];
struct node
{
    int to,nxt,c1,c2;
}e[N];
void add(int from,int to,int c1,int c2)
{
    e[++cnt].to=to;
    e[cnt].c1=c1;
    e[cnt].c2=c2;
    e[cnt].nxt=head[from];
    head[from]=cnt;
}
int dep[N];     //存u点的深度
int fa[N][20];  //存从u点向上跳2^i层的祖先节点
//vector<int>e[N];
void dfs(int u,int pare)
{
    dep[u]=dep[pare]+1;
    fa[u][0]=pare;
    for(int i=1;i<=19;i++)
        fa[u][i]=fa[fa[u][i-1]][i-1];
    for(int i=head[u];i;i=e[i].nxt)
    {
        int v=e[i].to;
        if(v!=pare) dfs(v,u);
    }
}

//利用st表求lca
int lca(int u,int v)
{
    if(dep[u]<dep[v]) swap(u,v);
    for(int i=19;i>=0;i--)
        //先跳到同一层
        if(dep[fa[u][i]]>=dep[v])
            u=fa[u][i];
    if(u==v)    return v;
    //跳到lca的下一层
    for(int i=19;i>=0;i--)
        if(fa[u][i]!=fa[v][i])
        u=fa[u][i],v=fa[v][i];
    return fa[u][0];
}
void sum(int u,int pare)
{
    int g;
    for(int i=head[u];i;i=e[i].nxt)
    {
        int v=e[i].to;
        if(v==pare)
        {
            g=i;continue;
        }
        sum(v,u);
        num[u]+=num[v];
    }
    if(e[g].c1*num[u]<e[g].c2)
        ans+=e[g].c1*num[u];
    else
        ans+=e[g].c2;
}
void solve()
{
    cin>>n;
    for(int i=1;i<n;i++)
    {
        int u,v,c1,c2;cin>>u>>v>>c1>>c2;
        add(u,v,c1,c2);
        add(v,u,c1,c2);
    }
    dfs(1,0);
    for(int i=1;i<n;i++)
    {
        int g=lca(i,i+1);
        num[g]-=2;
        num[i]+=1;
        num[i+1]+=1;
    }
    sum(1,0);
    cout<<ans<<endl;
}
signed main()
{
    //ios;
    //int T;cin>>T;
    //while(T--)
        solve();
    return 0;
}

P5002 专心OI - 找祖先

计算公式:(sz[i]-sz[j])*sz[j](i是j的父亲)+sz[i] (自身也可是自身的祖先)
这种计算方式相较于其他方便了非常多。

#include<bits/stdc++.h>
#define int long long
#define endl '\n'
#define For(i,a,b) for(i=(a);i<=(b);++i)
#define ios (ios::sync_with_stdio(false),cin.tie(0),cout.tie(0))
using namespace std;
const int N=7e5+7;
const int inf=0x3f3f3f3f;
const int mod=1e9+7;
int n,r,m,a[N],mx,ans[N],sz[N];
int dep[N];     //存u点的深度
int fa[N][20];  //存从u点向上跳2^i层的祖先节点
vector<int>e[N];
void dfs(int u,int pare)
{
    sz[u]=1;
    dep[u]=dep[pare]+1;
    fa[u][0]=pare;
    for(int i=1;i<=19;i++)
        fa[u][i]=fa[fa[u][i-1]][i-1];
    for(int v:e[u])
        if(v!=pare)
        {
            dfs(v,u);
            sz[u]+=sz[v];
        }
}

void solve()
{
    cin>>n>>r>>m;
    for(int i=1;i<n;i++)
    {
        int u,v;cin>>u>>v;
        e[u].push_back(v);
        e[v].push_back(u);
    }
    dfs(r,0);
    for(int i=1;i<=n;i++)
    {
        for(int j:e[i])
        {
            if(j==fa[i][0]) continue;
            ans[i]=(ans[i]+(sz[i]-sz[j])*sz[j]%mod)%mod;
        }
        ans[i]=(ans[i]+sz[i])%mod;
    }
    while(m--)
    {
        int x;cin>>x;
        cout<<ans[x]<<endl;
    }

}
signed main()
{
    //ios;
    //int T;cin>>T;
    //while(T--)
        solve();
    return 0;
}

C. Kuroni and Impossible Calculation

思路:当n大于m时,肯定会出现两个数对m取模后值相同,若这两个数相减再对m取模后答案为0,在参与运算后ans=0;若n<m时,双重循环暴力即可

#include<bits/stdc++.h>
#define int long long
#define endl '\n'
#define For(i,a,b) for(i=(a);i<=(b);++i)
#define ios (ios::sync_with_stdio(false),cin.tie(0),cout.tie(0))
using namespace std;
const int N=7e5+7;
const int inf=0x3f3f3f3f;
const int mod=1e9+7;
int n,m,a[N];

void solve()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
        cin>>a[i];
    if(n>m)
    {
        cout<<0<<endl;return;
    }
    int ans=1;
    for(int i=1;i<=n;i++)
        for(int j=i+1;j<=n;j++)
            ans=(ans*abs(a[i]-a[j]))%m;
    cout<<ans<<endl;
}
signed main()
{
    //ios;
    //int T;cin>>T;
    //while(T--)
        solve();
    return 0;
}

相关文章

学习编程是顺着互联网的发展潮流,是一件好事。新手如何学习...
IT行业是什么工作做什么?IT行业的工作有:产品策划类、页面...
女生学Java好就业吗?女生适合学Java编程吗?目前有不少女生...
Can’t connect to local MySQL server through socket \'/v...
oracle基本命令 一、登录操作 1.管理员登录 # 管理员登录 ...
一、背景 因为项目中需要通北京网络,所以需要连vpn,但是服...