数据结构专题

来源:互联网 发布:php 判断域名格式 编辑:程序博客网 时间:2024/05/16 09:53

花神游历各国

分块可以卡卡常过去,听说线段树比较快,其实分块改块长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 1000002using 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 1000004using 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 500005using 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 50005using 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 10005using 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 1000000000using 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 1000000000using 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;}