数据结构专题
花神游历各国
分块可以卡卡常过去,听说线段树比较快,其实分块改块长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;
}

浙公网安备 33010602011771号