【2019 HDU多校集训第三场】

本文深入探讨了算法竞赛中常见的几种问题类型及解决方案,包括支配树问题、数据结构维护DP、威尔逊定理的应用以及k上升子序列和最大问题等。通过具体的代码实现,解析了如何高效地解决这些问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.1002.支配树问题,对原图反向建图,然后中心城市变为入度为0的点。把这些点收集一下,建一个原点,所有中心城市与原点连边,问题转化为从原点到目标的两个点的必经之点有多少个?对这个图建立支配树,支配树是个啥玩意就不多说了,因为我也是现学。建完之后就是求两个点到原点加起来有多少个不同的点,LCA问题。

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+5;
int t,n,m,q,in[maxn],lg[maxn];
queue<int>que;

struct ControlTree
{
    struct Node
    {
        vector<int>e[maxn];
        inline void adde(int u,int v)
        {
            e[u].push_back(v);   //有向图
        }
    } a,b,c,d;  //d存的就是支配树
    int dfn[maxn],id[maxn],fa[maxn],cnt;
    void dfs(int u)
    {
        dfn[u]=++cnt;
        id[cnt]=u;
        int sz=a.e[u].size();
        for(int i=0; i<sz; i++)
        {
            int v=a.e[u][i];
            if(dfn[v])
                continue;
            fa[v]=u;
            dfs(v);
        }
    }

    int semi[maxn],idom[maxn],bel[maxn],val[maxn];
    inline int Find(int x)
    {
        if(x==bel[x])
            return x;
        int tmp=Find(bel[x]);
        if(dfn[semi[val[bel[x]]]]<dfn[semi[val[x]]])
            val[x]=val[bel[x]];
        return bel[x]=tmp;
    }

    inline void tarjan()
    {
        for(int i=cnt; i>1; i--)
        {
            int u=id[i],sz=b.e[u].size();
            for(int i=0; i<sz; i++)
            {
                int v=b.e[u][i];
                if(!dfn[v])
                    continue;
                Find(v);
                if(dfn[semi[val[v]]]<dfn[semi[u]])
                    semi[u]=semi[val[v]];
            }
            c.adde(semi[u],u);
            bel[u]=fa[u];
            u=fa[u];
            sz=c.e[u].size();
            for(int i=0; i<sz; ++i)
            {
                int v=c.e[u][i];
                Find(v);
                if(semi[val[v]]==u)
                    idom[v]=u;
                else
                    idom[v]=val[v];
            }
        }
        for(int i=2; i<=cnt; i++)
        {
            int u=id[i];
            if(idom[u]!=semi[u])
                idom[u]=idom[idom[u]];
        }
    }


    int dep[maxn],father[maxn][22];
    void build(int u,int f)
    {
        dep[u]=dep[f]+1;
        father[u][0]=f;
        for(int i=1; (1<<i)<=dep[u]; i++)
            father[u][i]=father[father[u][i-1]][i-1];
        for(int i=0; i<(int)d.e[u].size(); i++)
            build(d.e[u][i],u);
    }

    int lca(int x,int y)
    {
        if(dep[x]<dep[y])
            swap(x,y);
        while(dep[x]>dep[y])
            x=father[x][lg[dep[x]-dep[y]]];
        if(x==y)
            return x;
        for(int k=lg[dep[x]-1]; k>=0; k--)
            if(father[x][k]!=father[y][k])
            {
                x=father[x][k];
                y=father[y][k];
                k=min(k,lg[dep[x]-1]+1);
            }
        return father[x][0];
    }

     void ini()
    {
        for(int i=0;i<=n+2;i++)
        {
            a.e[i].clear();
            b.e[i].clear();
            c.e[i].clear();
            d.e[i].clear();
            dfn[i]=id[i]=fa[i]=dep[i]=idom[i]=0;
        }
        cnt=0;
    }
} ct;


int main()
{
    for(int i=2; i<maxn; i++)
        lg[i]=lg[i-1]+(i==(i&-i));
    cin>>t;
    while(t--)
    {
        cin>>n>>m;
        ct.ini();
        for(int i=0;i<=n+2;i++) in[i]=0;
        for(int i=1; i<=n+2; i++)
            ct.semi[i]=ct.bel[i]=ct.val[i]=i; // 初始化

        for(int i=1; i<=m; i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            in[u]++;
            ct.a.adde(v,u); //反向建图
            ct.b.adde(u,v); //建立反图
        }
        for(int i=1;i<=n;i++)
            if(in[i]==0) que.push(i);
        int s=n+1;
        while(!que.empty())
        {
            int u=que.front();
            que.pop();
            ct.a.adde(s,u);
            ct.b.adde(u,s);
        }
        ct.dfs(s);
        ct.tarjan();
        for(int i=1;i<=n;i++)
            ct.d.adde(ct.idom[i],i);
        
        ct.build(s,s+1);
        cin>>q;
        for(int i=1;i<=q;i++)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            int ans=ct.dep[a]+ct.dep[b]-ct.dep[ct.lca(a,b)]-1;
            printf("%d\n",ans);
        }
    }
}

2.1004.数据结构维护DP,其实就是k个最大字段和,但是转移状态比较多,所以用数据结构加速一下,可以使用树状数组或者其他。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int maxn=2e5+5;
ll a[maxn],f[maxn];

int bit[maxn*2+100];
void update(int x,int d){
    for(int i=x;i>0;i-=i&-i) bit[i]=max(bit[i],d);
}
int querysuf(int x,int B){
    int ret=-1e9;
    for(int i=x;i<B;i+=i&-i) ret=max(bit[i],ret);
    return ret;
}
void remove(int x,int B){
    for(int i=x;i<B;i+=i&-i) bit[i]=-1e9;
    for(int i=x;i>0;i-=i&-i) bit[i]=-1e9;
}

vector<ll> disc;
int getid(ll x){return lower_bound(disc.begin(),disc.end(),x)-disc.begin()+1;}

bool check(ll ans,int n,int k){// sum<=ans
    disc.clear();
    for(int i=1;i<=n;i++) {
        disc.push_back(a[i]);
        disc.push_back(a[i]-ans);
    }
    disc.push_back(0);
    sort(disc.begin(),disc.end());
    disc.erase(unique(disc.begin(),disc.end()),disc.end());

    for(int i=1;i<=disc.size();i++) remove(i,disc.size());
    f[0]=0;
    update(getid(0),f[0]);
    for(int i=1;i<=n;i++) {
        f[i]=querysuf(getid(a[i]-ans),disc.size()+1)+1;
        update(getid(a[i]),f[i]);
        if(f[i]>=k) return true;
    }
    return false;
}

int main(){
    int T; scanf("%d",&T);
    while(T--){
        int n,k;
        scanf("%d%d",&n,&k);
        for(int i=1;i<=n;i++) {
            scanf("%lld",a+i);
            a[i]+=a[i-1];
        }
        ll l=-1e9*1e6,r=1e9*1e6;
        while(l+1<r){//  ans -> ( ]
            ll mid=(l+r)/2;//
            if(check(mid,n,k)) r=mid;//
            else l=mid;
        }
        printf("%lld\n",r);
    }
}

3.1006威尔逊定理。因为两个素数的间隔是很小的,int范围内不会超过500.所以直接向后找,对于每个数MR判定一下。

#include<bits/stdc++.h>
using namespace std;

#define inf  1000000000
//#define mod 1000000007
#define maxn 100005

#define pb push_back
#define mp make_pair
#define F first
#define S second
#define pii pair

#define lowbit(x) (x&(-x))
#define debug cout<<"hi"<<endl;

typedef long long ll;
typedef long double ld;

//inline void addmod(int &a,int b){a+=b;if(a>=mod) a-=mod;}
//inline void decmod(int &a,int b){a-=b;if(a<0)a+=mod;}
//inline void addmod(ll &a,ll b){a+=b;if(a>=mod) a-=mod;}
//inline void decmod(ll &a,ll b){a-=b;if(a<0)a+=mod;}

/********** show time **********/

ll p,t;

using namespace std;

namespace ddd
{
    //****************************************************************
    // Miller_Rabin 算法进行素数测试
    //速度快,而且可以判断 <2^63的数
    //****************************************************************
    const int S = 20;//随机算法判定次数,S越大,判错概率越小


    //计算 (a*b)%c.   a,b都是long long的数,直接相乘可能溢出的
    //  a,b,c <2^63
    long long mult_mod(long long a, long long b, long long c)
    {
        a %= c;
        b %= c;
        long long ret = 0;
        while (b)
        {
            if (b & 1) { ret += a; ret %= c; }
            a <<= 1;
            if (a >= c)a %= c;
            b >>= 1;
        }
        return ret;
    }



    //计算  x^n %c
    long long pow_mod(long long x, long long n, long long mod)//x^n%c
    {
        if (n == 1)return x % mod;
        x %= mod;
        long long tmp = x;
        long long ret = 1;
        while (n)
        {
            if (n & 1) ret = mult_mod(ret, tmp, mod);
            tmp = mult_mod(tmp, tmp, mod);
            n >>= 1;
        }
        return ret;
    }





    //以a为基,n-1=x*2^t      a^(n-1)=1(mod n)  验证n是不是合数
    //一定是合数返回true,不一定返回false
    bool check(long long a, long long n, long long x, long long t)
    {
        long long ret = pow_mod(a, x, n);
        long long last = ret;
        for (int i = 1; i <= t; i++)
        {
            ret = mult_mod(ret, ret, n);
            if (ret == 1 && last != 1 && last != n - 1) return true;//合数
            last = ret;
        }
        if (ret != 1) return true;
        return false;
    }


    bool Miller_Rabin(long long n)
    {
        if (n < 2)return false;
        if (n == 2)return true;
        if ((n & 1) == 0) return false;//偶数
        long long x = n - 1;
        long long t = 0;
        while ((x & 1) == 0) { x >>= 1; t++; }
        for (int i = 0; i < S; i++)
        {
            long long a = rand() % (n - 1) + 1;//rand()需要stdlib.h头文件
            if (check(a, n, x, t))
                return false;//合数
        }
        return true;
    }
}


ll qm(ll a, ll b,ll mod)
{
    ll ans = 0;
    while(b)
    {
        if(b & 1)        //看b的末位是否为1
        {
            ans = (ans + a) % mod;
        }
        a = (a * 2) % mod;
        b >>= 1;
    }
    return ans;
}

ll qp(ll a,ll b,ll mod)
{
    ll res=1;
    while(b)
    {
        if(b&1)
            res=qm(res,a,mod);//res=res*a%mod;
        b>>=1;
        a=qm(a,a,mod);
        //a=a*a%mod;
    }
    return res;
}

void gao()
{
    ll q;
    ll x=p;
    while(1)
    {
        x--;
        if(ddd::Miller_Rabin(x))
        {
            q=x;
            break;
        }
    }
    ll ans=-1;
    ans+=p;
    //cout<<ans<<endl;
    for(ll i=q+1;i<=p-1;i++)
    {
        ans=qm(ans,qp(i,p-2,p),p);
        //ans=ans*qp(i,p-2,p);
        ans%=p;
    }
    printf("%lld\n",ans);
}



int main()
{
    cin>>t;
    while(t--)
    {
        cin>>p;
        gao();
    }
}

4.1007CF原题。。。。

5.k上升子序列和最大问题。费用流+拆点限流。很多人都是直接网络流过去的,但是其实真的要说网络流是不能AC的,需要优化建图。所以我觉得这个题目有点XX。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值