数据结构专题
花神游历各国
分块可以卡卡常过去,听说线段树比较快,其实分块改块长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
1 2
3 2
4 5
Sample Output
2
HINT
【数据范围】
对于30%的数据,保证N < =1000
对于100%的数据,保证N < =1000000
二分图最坏复杂度很大,但是能过这题
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; }