• NOI 1997~2003 远古题目


    [NOI2002]银河英雄传说

    这题就是并茶几,但是注意还要维护每个点到队尾的距离,用front[]来记前面的,不包括自己,用size[]来记每个队的大小,一减就行了,最后要减1

    代码:

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    const int maxn=3e4+10;
    int n,m;
    int fath[maxn],fron[maxn],si[maxn];
    
    inline int father(int x)
    {
        if(fath[x]==x) return x;
        int tmp=father(fath[x]);
        fron[x]+=fron[fath[x]];
        fath[x]=tmp;
        return fath[x];
    }
    
    inline void Union(int x,int y)
    {
        int f1=father(x),f2=father(y);
        if(f1!=f2)
        {
            fath[f1]=f2;
            fron[f1]+=si[f2];
            si[f2]+=si[f1];
        }
    }
    
    inline void deb()
    {
        printf("
    ");
        for(int i=1;i<=5;i++) printf("%d==",fron[i]);
        printf("
    ");
    }
    
    int main()
    {
        for(int i=1;i<=maxn-10;i++) fath[i]=i,fron[i]=0,si[i]=1;
        scanf("%d",&m);
        while(m--)
        {
            int u,v;
            char op;
            cin>>op>>u>>v;
            if(op=='M') Union(u,v);
            else
            {
                int f1=father(u),f2=father(v);
                if(f1!=f2) printf("-1
    ");
                else printf("%d
    ",abs(fron[u]-fron[v])-1);
            }
        }
        return 0;
    }
    View Code

    [NOI2003]智破连环阵

    很值得做的一道题,参考楼教主的部分搜索+二分图匹配:

    https://wenku.baidu.com/view/83d4a76925c52cc58bd6beac.html?from=search

    基本思想是先给武器分段,对于每一段找到可以炸完的炸弹连一条边,若全部匹配上了,就更新炸弹数,否则修改分段,还有几个剪枝:

    1、用DP算出来每个武器到最后一个武器炸完所需的最少炸弹,可进行最优性剪枝

    2、若当前段无法增广,直接return

    3、每一次修改从以前的基础上修改,我们在枚举区间长度时,可以先用O(n^2)的广搜求出最大可攻击到的武器编号maxL,显然编号小于等于maxL的武器都可以被攻击到,这样我们保证了总有炸弹能把这一段炸完

    代码:

    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <algorithm>
    using namespace std;
    const int maxn=101;
    int n,m,k,cnt,ans;
    int x[maxn],y[maxn],a[maxn],b[maxn];
    int ml[maxn],mr[maxn],vis[maxn],q[maxn],dis[maxn];
    int g[maxn][maxn],can[maxn][maxn][maxn],maxt[maxn][maxn];
    
    inline int Hun(int x)
    {
        for(int i=1;i<=n;i++)
        if(g[x][i] && vis[i]!=cnt)
        {
            vis[i]=cnt;
            if(!mr[i] || Hun(mr[i]))
            {
                mr[i]=x,ml[x]=i;
                return 1;
            }
        }
        return 0;
    }
    
    inline int sqr(int x)
    {
        return x*x;
    }
    
    inline void dfs(int l,int id)
    {
        if(l>m)
        {
            ans=id-1;
            return;
        }
        if(id-1+dis[l]>=ans) return;
        ++cnt;
        int ML[maxn],MR[maxn],h=0,t=0,maxl=l-1;
        for(int i=1;i<=n;i++) if(!mr[i]) vis[i]=cnt,q[++t]=i;
        while(h<t)
        {
            h++;
            int x=q[h];
            maxl=max(maxl,maxt[x][l]);
            for(int i=1;i<id;i++)
                if(g[i][x] && vis[ml[i]]!=cnt) vis[ml[i]]=cnt,q[++t]=ml[i];
        }
        memcpy(ML,ml,sizeof(ml));
        memcpy(MR,mr,sizeof(mr));
        for(int i=1;i<=n;i++) g[id][i]=can[i][l][maxl];
        ++cnt,Hun(id);
        for(int r=maxl;r>=l;r--)
        {
            for(int i=1;i<=n;i++)
                g[id][i]=can[i][l][r];
            dfs(r+1,id+1);
        }
        memcpy(ml,ML,sizeof(ML));
        memcpy(mr,MR,sizeof(MR));
    }
    
    int main()
    {
        scanf("%d%d%d",&m,&n,&k);
        for(int i=1;i<=m;i++) scanf("%d%d",&x[i],&y[i]);
        for(int i=1;i<=n;i++)
        {
            scanf("%d%d",&a[i],&b[i]);
            for(int j=1;j<=m;j++)
                g[i][j]=(sqr(a[i]-x[j])+sqr(b[i]-y[j])<=k*k);
        }
        for(int p=1;p<=n;p++)
        {
            for(int i=1;i<=m;i++) can[p][i][i]=g[p][i],maxt[p][i]=g[p][i]?i:i-1;
            for(int i=1;i<=m;i++)
                for(int j=i+1;j<=m;j++)
                {
                    can[p][i][j]=can[p][i][j-1]&g[p][j];
                    if(can[p][i][j]) maxt[p][i]=j;            
                }
        }
        for(int i=m;i>=1;i--)
        {
            dis[i]=1e9;
            for(int j=i;j<=m;j++)
                for(int p=1;p<=n;p++)
                if(can[p][i][j]) dis[i]=min(dis[i],dis[j+1]+1);
        }
        ans=1e9;
        dfs(1,1);
        printf("%d
    ",ans);
        return 0;
    }
    View Code

    [NOI2003]逃学的小孩

    往最坏的情况想,他们无论如何都要走一遍两家(b,c)之间的路径,那我们就把两家放在直径的两端,现在考虑起点a,起点的贡献显然是:min( dis[a,c] , dis[a,b] ) 所以从两端分别spfa枚举a,取最大值就行了

    代码:

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <queue>
    using namespace std;
    const int maxn=2e5+10;
    struct point
    {
        int to;
        int nxt;
        long long w;
    }edge[maxn*2];
    int n,m,tot;
    int head[maxn],vis[maxn];
    long long disa[maxn],disb[maxn];
    
    inline void add(int u,int v,long long w)
    {
        tot++;
        edge[tot].nxt=head[u];
        edge[tot].to=v;
        edge[tot].w=w;
        head[u]=tot;
    }
    
    inline int spfa(int s,long long *dis)
    {
        queue<int> q;
        memset(vis,0,sizeof(vis));
        vis[s]=1;
        for(int i=1;i<=n;i++) dis[i]=1e16;
        dis[s]=0;
        q.push(s);
        while(!q.empty())
        {
            int tt=q.front();
            q.pop();vis[tt]=0;
            for(int i=head[tt];i;i=edge[i].nxt)
            {
                int v=edge[i].to;
                if(dis[v]>dis[tt]+edge[i].w)
                {
                    dis[v]=dis[tt]+edge[i].w;
                    if(!vis[v]) vis[v]=1,q.push(v);
                }
            }
        }
        int who=s;
        for(int i=1;i<=n;i++)
            if(dis[i]>dis[who]) who=i;
        return who;
    }
    
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++)
        {
            int u,v;
            long long w;
            scanf("%d%d%lld",&u,&v,&w);
            add(u,v,w),add(v,u,w);
        }
        int a=spfa(1,disa);
        int b=spfa(a,disa);
        long long ans=disa[b],tmp=0;
        spfa(a,disa),spfa(b,disb);
        //for(int i=1;i<=n;i++)     printf("%lld==
    ",disa[i]);
        for(int i=1;i<=n;i++)
        {
            tmp=max(tmp,min(disa[i],disb[i]));
        }
        printf("%lld
    ",ans+tmp);
        return 0;
    }
    View Code
  • 相关阅读:
    多线程(一) NSThread
    Swift 烧脑体操(一)
    Swift 烧脑体操(二)
    UINavigationController使用的注意事项
    更多请查看我的文章
    本地通知
    网络编程(二)NSURLSessionConfiguration
    A
    51Nod 1116 K进制下的大数(暴力枚举)
    51Nod 1065 最小正子段和
  • 原文地址:https://www.cnblogs.com/linda-fcj/p/9054274.html
Copyright © 2020-2023  润新知