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("\n");
    for(int i=1;i<=5;i++) printf("%d==",fron[i]);
    printf("\n");
}

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\n");
            else printf("%d\n",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\n",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==\n",disa[i]);
    for(int i=1;i<=n;i++)
    {
        tmp=max(tmp,min(disa[i],disb[i]));
    }
    printf("%lld\n",ans+tmp);
    return 0;
}
View Code

 

posted @ 2018-05-18 07:43  Captain_fcj  阅读(274)  评论(0编辑  收藏  举报