数据结构专题

花神游历各国

分块可以卡卡常过去,听说线段树比较快,其实分块改块长90就能过

游戏

Description

lxhgww最近迷上了一款游戏,在游戏里,他拥有很多的装备,每种装备都有2个属性,这些属性的值用[1,10000]之间的数表示。当他使用某种装备时,他只能使用该装备的某一个属性。并且每种装备最多只能使用一次。 游戏进行到最后,lxhgww遇到了终极boss,这个终极boss很奇怪,攻击他的装备所使用的属性值必须从1开始连续递增地攻击,才能对boss产生伤害。也就是说一开始的时候,lxhgww只能使用某个属性值为1的装备攻击boss,然后只能使用某个属性值为2的装备攻击boss,然后只能使用某个属性值为3的装备攻击boss……以此类推。 现在lxhgww想知道他最多能连续攻击boss多少次?

Input

输入的第一行是一个整数N,表示lxhgww拥有N种装备 接下来N行,是对这N种装备的描述,每行2个数字,表示第i种装备的2个属性值

Output

输出一行,包括1个数字,表示lxhgww最多能连续攻击的次数。

Sample Input

3
1 2
3 2
4 5

Sample Output

2

HINT

【数据范围】
对于30%的数据,保证N < =1000
对于100%的数据,保证N < =1000000

然网上有并查集解法,但是没有前途,网上并查集的标程会被加边顺序ka,可是测试点不卡这个

二分图最坏复杂度很大,但是能过这题

A部是属性值,B部是装备,每条边代表一种装备,每种装备只能用一次,然后每一个属性值向外找增广路

#include<iostream>
#include<cstdio>
#include<cstring>
#define maxn 1000002
using namespace std;
int n;
struct edge
{
    int to,ne;  
}b[2000002];
int k=0,head[maxn];
int lin[maxn];
int vis[maxn]={0};
int ti;
inline void add(int u,int v)
{
     k++;
     b[k].to=v; b[k].ne=head[u]; head[u]=k;
}
bool dfs(int x)
{
     for(int i=head[x];i!=-1;i=b[i].ne)
     if(vis[b[i].to]!=ti){
         vis[b[i].to]=ti;
         if(!lin[b[i].to]||dfs(lin[b[i].to])){
             lin[b[i].to]=x;
             return 1;
         }
     }
     return 0;
}
int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("a.txt","w",stdout);
    memset(head,-1,sizeof(head));
    scanf("%d",&n);
    int x,y;
    for(int i=1;i<=n;i++){
        scanf("%d%d",&x,&y);
        add(x,i); add(y,i);
    }
    int ans=0;
    for(int i=1;i<=10000;i++){
        ti=i;
        if(dfs(i))  ans++;
        else break;
    }
    printf("%d\n",ans);
    //while(1);
    return 0;
}

方块游戏

并查集(银河英雄传说)

#include<iostream>
#include<cstdio>
#include<cstring>
#define maxn 1000004
using namespace std;
int n,P;
int dis[maxn],c[maxn];
int fa[maxn];
inline int read()
{
     int x=0;
     char ch=getchar();
     while(ch<'0'||ch>'9') ch=getchar();
     while(ch>='0'&&ch<='9') {x=x*10+ch-'0';ch=getchar();}
     return x;   
}
int find(int x)
{
     if(fa[x]!=x){
         int t=fa[x];
         fa[x]=find(fa[x]);
         dis[x]=dis[x]+dis[t];
     }
     return fa[x];
}
void Hb(int x,int y)
{
     int fx=find(x),fy=find(y);
     fa[fx]=fy;
     dis[fx]=c[fy];
     c[fy]+=c[fx]; c[fx]=0;
}
int main()
{
     //freopen("in.txt","r",stdin);
     n=30000;
     char type[5];
     for(int i=1;i<=n;i++){ 
         fa[i]=i;  c[i]=1; dis[i]=0;
     } 
     scanf("%d",&P);
     int x,y;
     for(int i=1;i<=P;i++){
          scanf("%s",type);
          if(type[0]=='M'){
              scanf("%d%d",&x,&y);
              Hb(x,y);
          }
          else{
              scanf("%d",&x);
              find(x);
              printf("%d\n",dis[x]);
          }
     }
     //while(1);
     return 0;
}

火车

首先要透彻地理解题意,然后树剖+树状数组差分

树剖把树转移成序列,对于已经访问过路径u->v,id[u]++,(id[v]+1)--

if(sum[id[x]]==0)则x未访问过

#include<iostream>
#include<cstdio>
#include<cstring>
#define LL long long 
#define maxn 500005
using namespace std;
int n,m,now;
long long ans=0;
struct edge
{
     int to,ne; 
}b[maxn*2];
int k=0,head[maxn];
int c[maxn];
int g[maxn];
/*struct Tree
{
     int l,r,visit; 
}t[maxn*4];*/
inline int read()
{
     int x=0;
     char ch=getchar();
     while(ch<'0'||ch>'9') ch=getchar();
     while(ch>='0'&&ch<='9') {x=x*10+ch-'0';ch=getchar();}
     return x;   
}
void add(int u,int v)
{
     k++;
     b[k].to=v; b[k].ne=head[u]; head[u]=k;
}
int fa[maxn],son[maxn],d[maxn],sz[maxn],tp[maxn],id[maxn],pos[maxn],cnt=0;
void dfs1(int x)
{
     sz[x]=1; son[x]=0;
     for(int i=head[x];i!=-1;i=b[i].ne)
     if(b[i].to!=fa[x]){
          fa[b[i].to]=x;
          d[b[i].to]=d[x]+1;
          dfs1(b[i].to);
          sz[x]+=sz[b[i].to];
          if(sz[b[i].to]>sz[son[x]]) son[x]=b[i].to;
     }
}
void dfs2(int x,int top)
{
     tp[x]=top;
     id[x]=++cnt; pos[cnt]=x;
     if(son[x]) dfs2(son[x],top);
     for(int i=head[x];i!=-1;i=b[i].ne)
     if(b[i].to!=fa[x]&&b[i].to!=son[x])   dfs2(b[i].to,b[i].to);
}
int lowbit(int x)
{
     return x&(-x); 
}
void update(int x,int num)
{
     while(x<=n){
         c[x]+=num;
         x+=lowbit(x);
     }
}
int getsum(int x)
{
     int sum=0;
     while(x){
         sum+=c[x];
         x-=lowbit(x);
     }
     return sum;
}
void bj(int x,int y)
{
     update(x,1);
     update(y+1,-1);
}
 
int lca(int x,int y)
{
    int fx=tp[x],fy=tp[y];
    while(fx!=fy){
        if(d[fx]<d[fy]){
           swap(fx,fy); swap(x,y);
        }
        x=fa[fx]; fx=tp[x];
    }
    return d[x]<d[y] ? x : y;    
}
void mark(int x,int y)
{
     int fx=tp[x],fy=tp[y];
     while(fx!=fy){
          if(d[fx]<d[fy]){
              swap(fx,fy); swap(x,y);
          }
          bj(id[fx],id[x]);
          x=fa[fx]; fx=tp[x];
     }
     if(d[x]<d[y]) swap(x,y);
     bj(id[y],id[x]);
     return ;
}
int main()
{
     //freopen("in.txt","r",stdin);
     memset(head,-1,sizeof(head));
     n=read(); m=read(); now=read();
     int x,y;
     for(int i=1;i<n;i++){
         x=read(); y=read();
         add(x,y);  add(y,x);
     }
     dfs1(1);
     dfs2(1,1);
     for(int i=1;i<=m;i++) scanf("%d",&g[i]);
     int anc;
     for(int i=1;i<=m;i++)
     if(!getsum(id[g[i]])){
         // printf("x==%d\n",g[i]); 
          anc=lca(now,g[i]);
          mark(now,g[i]);
          ans+=(LL)(d[g[i]]+d[now]-2*d[anc]);
          now=g[i];
     }
     printf("%lld\n",ans);
     //while(1);
     return 0;
}

[Usaco2016 Open]Diamond Collector

先排序,然后每个点upper_bound求一下最大差不超过K时最远到达的位置to[i](可以用前缀和维护)

考虑合并两个序列,先选定一个序列x->to[i]


往右找

对于有交叉的序列x->to[x],查询[i,to[i]]区间内所有点中to[x]的最大值,那么to[k]-i+1就是最大序列长度

对与没有交叉的序列y->to[y],查询[to[i]+1,n]区间内len的最大值,两个长度加起来

[l,r]内len(to[i]-i+1)的最大值,to[i]的最大值用线段树维护

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 50005
using namespace std;
int n,K;
int a[maxn];
struct Tree
{
     int l,r,max_sz,max_t;  
}b[maxn*4];
int to[maxn],f[maxn];
int upper(int x,int l)
{
    int ans=l;
    int r=n,mid;
    while(l<=r){
        mid=(l+r)/2;
        if(a[mid]<=x){ ans=max(ans,mid); l=mid+1; }
        else r=mid-1;
    }
    return ans;
}
void  build(int l,int r,int z)
{
      b[z].l=l; b[z].r=r; 
      if(l==r){
           b[z].max_sz=f[l];
           b[z].max_t=to[l];
           return ;
      }
      int mid=(l+r)>>1;
      build(l,mid,z<<1);
      build(mid+1,r,z<<1|1);
      b[z].max_sz=max(b[z<<1].max_sz,b[z<<1|1].max_sz);
      b[z].max_t=max(b[z<<1].max_t,b[z<<1|1].max_t);
}
int getmax(int l,int r,int z)
{
    if(l<=b[z].l&&b[z].r<=r) return b[z].max_sz;
    int ans=0;
    int mid=(b[z].l+b[z].r)>>1;
    if(l<=mid) ans=max(ans,getmax(l,r,z<<1));
    if(r>mid)  ans=max(ans,getmax(l,r,z<<1|1));
    return ans;
}
int getmax2(int l,int r,int z)
{
    if(l<=b[z].l&&b[z].r<=r)  return b[z].max_t;
    int ans=0;
    int mid=(b[z].l+b[z].r)>>1;
    if(l<=mid) ans=max(ans,getmax(l,r,z<<1));
    if(r>mid)  ans=max(ans,getmax(l,r,z<<1|1));
    return ans;
}
int main()
{
    //freopen("in.txt","r",stdin);
    scanf("%d%d",&n,&K);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    sort(a+1,a+n+1);
    for(int i=1;i<=n;i++)
    {
        to[i]=upper(a[i]+K,i);   
        f[i]=to[i]-i+1; 
    }
    build(1,n,1);
    int ans=0,t1,t2;
    for(int i=1;i<=n;i++){
        t1=f[i]+getmax(to[i]+1,n,1);
        t2=getmax2(i,to[i],1)-i+1;
        ans=max(ans,max(t1,t2));
    }
    printf("%d\n",ans);
    //while(1);
    return 0;
}

[Usaco2009 Feb]Revamping Trails 道路升级

传送门,二维spfa

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define LL long long
#define maxn 10005
using namespace std;
int n,m,K;
struct edge
{
     int to,ne,w;
}b[maxn*10];
int k=0,head[maxn];
LL dis[maxn][22];
bool vis[maxn][22];
struct node
{
    int id,num;
    bool operator <( const node &x)
    const {
         return dis[id][num]>dis[x.id][x.num]; 
    }
}tmp,now;
priority_queue< node > q;
inline int read()
{
     int x=0;
     char ch=getchar();
     while(ch<'0'||ch>'9') ch=getchar();
     while(ch>='0'&&ch<='9') {x=x*10+ch-'0';ch=getchar();}
     return x;   
}
void add(int u,int v,int w)
{
     k++;
     b[k].to=v; b[k].ne=head[u]; b[k].w=w; head[u]=k;
}
void spfa()
{
     memset(dis,0xf,sizeof(dis));
     dis[1][0]=0; vis[1][0]=1;
     tmp.id=1; tmp.num=0; 
     q.push(tmp); 
     int x,y;
     while(!q.empty()){
          now=q.top(); q.pop();
          x=now.id; y=now.num; vis[x][y]=0;
          for(int i=head[x];i!=-1;i=b[i].ne){
              if(dis[b[i].to][y]>dis[x][y]+(LL)b[i].w){
                    dis[b[i].to][y]=dis[x][y]+(LL)b[i].w;
                    if(!vis[b[i].to][y]){
                        vis[b[i].to][y]=1;
                        tmp.id=b[i].to; tmp.num=y; 
                        q.push(tmp);
                    }
              }
              if(dis[b[i].to][y+1]>dis[x][y]&&y+1<=K){
                    dis[b[i].to][y+1]=dis[x][y];
                    if(!vis[b[i].to][y+1]){
                        vis[b[i].to][y+1]=1;
                        tmp.id=b[i].to; tmp.num=y+1; 
                        q.push(tmp);
                    }
              }  
          }
     }
}
int main()
{
     //freopen("in.txt","r",stdin);
     memset(head,-1,sizeof(head));
     n=read(); m=read(); K=read();
     int x,y,z;
     for(int i=1;i<=m;i++){
         x=read(); y=read(); z=read();
         add(x,y,z);  add(y,x,z);
     }
     spfa();
     printf("%lld\n",dis[n][K]);
     //while(1);
     return 0;
}

【POJ Challenge】生日礼物

set+链表

只能在hzoj上过的算法:先找出所有连续的正数序列,cnt个,if(cnt>m) 合并序列

每合并两个相邻的序列,肯定会有一定的损失,合并损失值最小的序列

合并时,新序列等于两个序列中值最大的一个或两段序列加中间负数连起来

#include<iostream>
#include<cstdio>
#include<cstring>
#define maxn 100005
#define LL long long
#define INF 1000000000
using namespace std;
int n,m,cnt=0,K;
struct node
{
     int s,t;
     LL num; 
}b[maxn];
LL a[maxn],sum[maxn];
int nex[maxn],pre[maxn];
void hb(int x,int mi)
{
     int g=b[x].num+b[nex[x]].num;
     int t1=g+sum[b[nex[x]].s-1]-sum[b[x].t];
     int t2=b[x].num;
     int t3=b[nex[x]].num;
     b[x].num=g-mi;
     if(t1==b[x].num){
         b[x].s=b[x].s; b[x].t=b[nex[x]].t;
     }
     else if(t3==b[x].num){
         b[x].s=b[nex[x]].s; b[x].t=b[nex[x]].t;
     }
     int to=nex[x];
     nex[x]=nex[to];
}
int main()
{
    //freopen("sequence.3.in","r",stdin);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){ 
        scanf("%lld",&a[i]);
        sum[i]=sum[i-1]+a[i];
    }
    for(int i=1;i<=n;i++)
    if(a[i]>0){
        int j=i;
        for(j=i;j<=n;j++)
        if(a[j]<=0) break;
        cnt++; b[cnt].s=i; b[cnt].t=j-1; b[cnt].num=sum[j-1]-sum[i-1];
        i=j-1;
    }
    K=cnt;
    for(int i=1;i<=K;i++){
        pre[i]=i-1; nex[i]=i+1;
    }
    nex[0]=1;
    LL ol,ne,he;
    LL mi,id;
    while(cnt>m){
        mi=INF; id=0;
        for(int i=nex[0];i<=K;i=nex[i])
        if(nex[i]<=K){
            ol=b[i].num+b[nex[i]].num;
            he=ol+sum[b[nex[i]].s-1]-sum[b[i].t];
            ne=max(he,max(b[i].num,b[nex[i]].num));
            if(ol-ne<mi){ mi=ol-ne; id=i; }
        }
        hb(id,mi); cnt--;
    }
    int ans=0;
    for(int i=nex[0];i<=K;i=nex[i])  ans+=b[i].num;
    printf("%d\n",ans);
    //while(1);
    return 0;
}
正解做法:所有连续的符号相同的序列缩成一个数,首尾负数去掉,ans=所有正数和

如果个数>m,去掉一些数,每次贪心选绝对值最小的x,ans-=x

x>0,代表不选这个数

x<0,代表合并两边的正数

同时要满足选x,就不能选两边的数

#include<iostream>
#include<cstdio>
#include<cstring>
#include<set>
#include<cmath>
#define maxn 100005 
#define LL long long 
#define INF 1000000000
using namespace std;
int n,m,cnt=0,K;
LL ans=0;
LL a[maxn],b[maxn],sum[maxn];
struct node
{
    LL first;
    int second;  
    friend bool operator < (const node x,const node y){
        return x.first==y.first ? x.second<y.second : x.first<y.first ; 
    }
}tmp;
set< node > s;
int pre[maxn],nex[maxn];
inline void del(int x)
{
     tmp.first=b[x]; tmp.second=x; s.erase(tmp);
}
inline void push(int x)
{
     tmp.first=b[x]; tmp.second=x; s.insert(tmp);  
}
void hb(int x)
{
     del(x);
     if(!pre[x]){
         del(nex[x]);
         pre[nex[nex[x]]]=0;
         return ;            
     }
     if(!nex[x]){
         del(pre[x]);
         nex[pre[pre[x]]]=0;
         return ;
     } 
     if(pre[x]) del(pre[x]);
     if(nex[x]) del(nex[x]);
     b[x]=b[pre[x]]+b[nex[x]]-b[x];
     push(x);
     int pr=pre[x],to=nex[x]; 
     nex[x]=nex[to]; pre[x]=pre[pr];
     if(pre[pr]) nex[pre[pr]]=x;
     if(nex[to]) pre[nex[to]]=x;
}
int main()
{
     //freopen("sequence.8.in","r",stdin);  
     scanf("%d%d",&n,&m);
     if(!m){
         printf("0\n");
         return 0;
     }
     for(int i=1;i<=n;i++) 
         scanf("%lld",&a[i]);
     int S=1,T=n;
     while(a[S]<=0) S++;
     while(a[T]<=0) T--;
     for(int i=S;i<=T;i++)  sum[i]=sum[i-1]+a[i];
     memset(b,0x7f,sizeof(b));
     for(int i=S;i<=T;i++){
         int j=i;
         if(a[i]<=0){
             for(j=i;j<=T;j++)
             if(a[j]>0)  break;
         }
         else{
             for(j=i;j<=T;j++)
             if(a[j]<=0) break;
         }
         cnt++; b[cnt]=sum[j-1]-sum[i-1];
         i=j-1;
     }
     //b[1]=a[1];
     //for(int i=S;i<=T;i++)
     for(int i=1;i<=cnt;i++){
         if(b[i]>0){  ans+=b[i]; K++; }
         else b[i]=-b[i];
         push(i);
         pre[i]=i-1; nex[i]=i+1;
     }
     nex[cnt]=0; 
     set< node > ::iterator it;
     while(K>m){
         it=s.begin();
         ans-=(*it).first;
         //printf("%lld\n",(*it).first);
         hb((*it).second);
         K--;
     }
     printf("%lld\n",ans);
     //while(1);
     return 0;
}




posted @ 2017-09-23 17:54  HunterxHunterl  阅读(118)  评论(0编辑  收藏  举报