poi2007

时间:2024-06-20 19:07:32

序:为什么写poi,zy说poi都是思路题目,不像hnoi妈的数据结构队。。。。。

1.bzoj1102

题目大意:定义了一个山谷和山峰,求他们数量。

题解:这种题bfs咯,在bfs的时候记录一下相邻的比我大的有多少,比我小的有多少,然后更新答案;

代码:

 #include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
#define maxn 2000
using namespace std;
const int dx[]={-,,,-,,-,,};
const int dy[]={-,-,-,,,,,};
int n,summ,sumn,ansm,ansn;
int lis[maxn*maxn][];
int a[maxn][maxn];
bool vis[maxn][maxn];
int read()
{
int x=; char ch; bool bo=;
while (ch=getchar(),ch<''||ch>'') if (ch=='-') bo=;
while (x=x*+ch-'',ch=getchar(),ch>=''&&ch<='');
if (bo) return -x; return x;
}
void bfs(int x,int y)
{
int t=,h=; lis[][]=x; lis[][]=y; sumn=summ=; vis[x][y]=;
while (h<=t)
{
int aa=lis[h][],bb=lis[h][];
for (int i=; i<; i++)
{
int xx=aa+dx[i],yy=bb+dy[i];
if (xx< || xx>n || yy< || yy>n) continue;
if (a[xx][yy]==a[aa][bb] && !vis[xx][yy])
{
++t; lis[t][]=xx; lis[t][]=yy;
vis[xx][yy]=;
}
else
{
if (a[xx][yy]<a[aa][bb]) summ++;
if (a[xx][yy]>a[aa][bb]) sumn++;
}
}
h++;
}
if (!summ && !sumn) ansm++,ansn++;
else if (summ && sumn) return;
else if (summ) ansm++;
else ansn++;
}
int main()
{
memset(vis,,sizeof(vis));
n=read();
for (int i=; i<=n; i++)
for (int j=; j<=n; j++)
a[i][j]=read();
ansn=ansm=;
for (int i=; i<=n; i++)
for (int j=; j<=n; j++)
if (!vis[i][j]) bfs(i,j);
printf("%d %d\n",ansm,ansn);
}

2.bzoj1103

题目大意:给你一颗树(全是土路),然后有两个操作,一个将一条土路改为公路,另外一个是求一个点到根的路径上土路的数目。

题解:dfs序+线段树维护

代码:

 #include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<cmath>
#define maxn 2000005
using namespace std;
int tot,n,m,tmp;
int pre[maxn],now[maxn],v[maxn],dis[maxn],fa[maxn],in[maxn],out[maxn];
int lazy[maxn],sum[maxn],st[maxn];
int read()
{
int x=; char ch; bool bo=;
while (ch=getchar(),ch<''||ch>'') if (ch=='-') bo=;
while (x=x*+ch-'',ch=getchar(),ch>=''&&ch<='');
if (bo) return -x; return x;
}
void insert(int a,int b){++tot; pre[tot]=now[a]; now[a]=tot; v[tot]=b;
}
void dfs()
{
int top=;
st[++top]=; fa[]=;
while (top)
{
int x=st[top]; top--;
if (!in[x])
{
in[x]=++tmp;
st[++top]=x;
for (int p=now[x]; p; p=pre[p])
{
int son=v[p];
if (son==fa[x]) continue;
st[++top]=son; dis[son]=dis[x]+; fa[son]=x;
}
}
else out[x]=++tmp;
}
}
void updata(int k,int val){lazy[k]+=val; sum[k]+=val;}
void pushdown(int k)
{
if (!lazy[k]) return;
sum[k*]+=lazy[k]; sum[k*+]+=lazy[k];
lazy[k*]+=lazy[k]; lazy[k*+]+=lazy[k];
lazy[k]=;
}
void change(int k,int l,int r,int x,int y,int val)
{
if (x<=l && r<=y) {updata(k,val); return; }
int mid=(l+r)>>;
if (x<=mid) change(k*,l,mid,x,y,val);
if (y>mid) change(k*+,mid+,r,x,y,val);
}
int query(int k,int l,int r,int pos)
{
pushdown(k);
if (l==r && r==pos) return sum[k];
int mid=(l+r)>>;
if (pos<=mid) return query(k*,l,mid,pos);
else return query(k*+,mid+,r,pos);
}
void changeroad(int x,int y)
{
if (fa[y]==x) swap(x,y);
change(,,tmp,in[x],out[x],-);
}
void askans(int x)
{
int k=query(,,tmp,in[x]);
printf("%d\n",dis[x]+k);
}
int main()
{
//freopen("meg.in","r",stdin);
//freopen("meg.out","w",stdout);
n=read();
for (int i=; i<n; i++){int x=read(),y=read(); insert(x,y); insert(y,x);}
tmp=tot=;
memset(sum,,sizeof(sum));
memset(dis,,sizeof(dis));
memset(in,,sizeof(in));
memset(out,,sizeof(out));
memset(fa,,sizeof(fa));
dfs();
//for (int i=1; i<=n; i++) cout<<" "<<dis[i]<<" "<<in[i]<<" "<<out[i]<<endl;
m=n+read()-;
for (int i=; i<=m; i++)
{
char ch[];
scanf(" %s",ch+);
if (ch[]=='A') {int x=read(),y=read(); changeroad(x,y);}
else {int x=read(); askans(x);}
}
}

注:会爆栈,写人工栈。

3.bzoj1104

题目大意:

    你手头有一张AKD市的地图。这张地图是边长为m*n的矩形,被划分
    为m*n个1*1的小正方形。对于每个小正方形,地图上已经标注了它的海拔高度以及它是否是AKD市的一个组成部分
    。地图上的所有部分都被水淹没了。并且,由于这张地图描绘的地面周围都被高山所环绕,洪水不可能自动向外排
    出。显然,我们没有必要抽干那些非AKD市的区域。每个巨型抽水机可以被放在任何一个1*1正方形上。这些巨型抽
    水机将持续地抽水直到这个正方形区域里的水被彻底抽干为止。当然,由连通器原理,所有能向这个格子溢水的格
    子要么被抽干,要么水位被降低。每个格子能够向相邻的格子溢水,“相邻的”是指(在同一高度水平面上的射影
    )有公共边。

题解:

  显然,如果一个点和许多点相邻并且此点的海拔最高,那么和它相连的点势必是可以抽干它的,于是我们可以把每个点作为关键点排序,那么对于一个点,则向它的四周bfs如果,遇到一个比他小的则不需要更新答案并且向下扩展,如果没有比他小的,则答案+1,退出;

代码:

 #include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<iostream>
#include<algorithm>
#define N 1100
using namespace std;
struct node{int x,y,d;}A[N*N],p[N*N];
int n,m,a[N][N],num,ans,tail,step[][];
bool b[N][N],v[N][N];
bool cmp(node x,node y)
{
if(x.d<y.d) return ;
return ;
}
int main()
{
step[][]=;step[][]=;
step[][]=-;step[][]=;
step[][]=;step[][]=;
step[][]=;step[][]=-;
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++)
for(int j=;j<=m;j++)
{
scanf("%d",&a[i][j]);
if(a[i][j]>) A[++num].x=i,A[num].y=j,A[num].d=a[i][j];
a[i][j]=abs(a[i][j]);
}
sort(A+,A+num+,cmp);
ans=; for(int i=;i<=num;i++)
{
if(b[A[i].x][A[i].y]) continue;
bool bo=;
tail=;p[].x=A[i].x;p[].y=A[i].y;
v[A[i].x][A[i].y]=;
for(int j=;j<=tail;j++)
{
int x=p[j].x,y=p[j].y,xx,yy;
for(int k=;k<;k++)
{
xx=x+step[k][];yy=y+step[k][];
if(xx>n || xx< || yy>m || yy< || a[xx][yy]>A[i].d) continue;
if(v[xx][yy]) continue;
if(b[xx][yy]) {bo=;continue;}
p[++tail].x=xx;p[tail].y=yy;
v[xx][yy]=;
}
}
if(bo==) ans++;
for(int j=;j<=tail;j++)
{
int x=p[j].x,y=p[j].y;
b[x][y]=;v[x][y]=;
}
}
printf("%d\n",ans);
return ;
}

4.bzoj1105

题目大意:

    Blue Mary是一个有名的石头收藏家。迄今为止,他把他的藏品全部放在他的宫殿的地窖中。现在,他想将他
    的藏品陈列在他的花园中。皇家花园是一个边长为1000000000单位的平行于坐标轴的正方形。对于每个石头,Blue
      Mary都有一个他想放置的坐标,然后将他告诉他的属下。不幸的是,他忘了告诉他们坐标的顺序(比如无法分辨(
    x,y)和(y,x))。属下们,就自己决定了每个石头的具体位置。为了保护他的藏品,Blue Mary决定建造一个篱笆来
    保护他们。为了美学的需要,篱笆也被设计为平行于坐标轴的矩形。如果花园的布局知道了,那么就可以知道最短
    长度的篱笆的布局。他的属下们需要变换石头的坐标使得篱笆的长度最少。每个石头只能从(x,y)变换为(y,x),由
    于每个石头的重量不一样。属下们希望他们移动的石头的重量和最少。

题解:

  显然他们应该以直线y=x为分界线,所以分四种情况讨论(自己想想吧)

代码:

 #include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
#define inf 0x7fffffff
#define maxn 1000005
#define ll long long
using namespace std;
int n;
int a[maxn],b[maxn],c[maxn];
int lx,rx,ry,ly;
bool bo[maxn],v[maxn];
ll ans;
int read()
{
int x=; char ch; bool bo=;
while (ch=getchar(),ch<''||ch>'') if (ch=='-') bo=;
while (x=x*+ch-'',ch=getchar(),ch>=''&&ch<='');
if (bo) return -x; return x;
}
void find(int lx,int rx,int ly,int ry)
{
int tmp=;
for (int i=; i<=n; i++)
{
if (lx<=a[i] && a[i]<=rx && ly<=b[i] && b[i]<=ry)
{
bo[i]=;
}
else
if (lx<=b[i] && b[i]<=rx && ly<=a[i] && a[i]<=ry)
{
tmp+=c[i];
bo[i]=;
}
else return ;
}
if (tmp<ans)
{
ans=tmp;
memcpy(v,bo,sizeof(bool)*(n+));
}
}
int main()
{
n=read(); lx=ly=ans=inf; rx=ry=-inf;
for (int i=; i<=n; i++)
{
a[i]=read(); b[i]=read(); c[i]=read();
int minn=min(a[i],b[i]),maxnn=max(a[i],b[i]);
lx=min(lx,minn); rx=max(rx,minn); ly=min(ly,maxnn); ry=max(ry,maxnn);
}
//cout<<rx<<" "<<lx<<" "<<ry<<" "<<ly<<endl;
printf("%lld ",2ll*(rx-lx+ry-ly));
find(lx, rx, ly, ry);
find(lx, ry, ly, rx);
find(ly, rx, lx, ry);
find(ly, ry, lx, rx);
printf("%lld\n",ans);
for (int i=; i<=n; i++)
printf("%d",v[i]);
}

5.bzoj1106

题目大意:

    一个叫做立方体大作战的游戏风靡整个Byteotia。这个游戏的规则是相当复杂的,所以我们只介绍他的简单规
    则:给定玩家一个有2n个元素的栈,元素一个叠一个地放置。这些元素拥有n个不同的编号,每个编号正好有两个
    元素。玩家每次可以交换两个相邻的元素。如果在交换之后,两个相邻的元素编号相同,则将他们都从栈中移除,
    所有在他们上面的元素都会掉落下来并且可以导致连锁反应。玩家的目标是用最少的步数将方块全部消除。

题解:

    它只有两个,如果他有多个怎么写呢?(我猜可以写dp哦),我们从左读到右,显然,我们如果是第二次遇到这个点就把他们合并;

    为什么:

    例如:1.123321那么先合并两个2一定比先合并两个1更优所以发现如果两对元素的位置是嵌套关系的话先删掉中间那对更优

       2.12123456712 合并1和先合并2没影响。

    SO:就可以了,虽然证明不严谨。。。。。

代码:

 #include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
#define maxn 50005
using namespace std;
int c[maxn*],n;
long long ans;
int cz[maxn*];
int read()
{
int x=; char ch; bool bo=;
while (ch=getchar(),ch<''||ch>'') if (ch=='-') bo=;
while (x=x*+ch-'',ch=getchar(),ch>=''&&ch<='');
if (bo) return -x; return x;
}
int lowbit(int x)
{
return -x&x;
}
void modify(int k,int val)
{
for (int i=k; i<=*n; i+=lowbit(i))
c[i]+=val;
}
int query(int x)
{
int kk=;
for (int i=x; i; i-=lowbit(i))
kk+=c[i];
return kk;
}
int main()
{
n=read();
for (int i=; i<=*n; i++)
{
int x=read();
if (cz[x])
{
ans+=query(i-)-query(cz[x]);
modify(cz[x],-); }
else
{
cz[x]=i;
modify(i,);
}
}
printf("%lld\n",ans);
return ;
}

6.bzoj1098

题目大意:FGD开办了一家电话公司。他雇用了N个职员,给了每个职员一部手机。每个职员的手机里都存储有一些同事的
     电话号码。由于FGD的公司规模不断扩大,旧的办公楼已经显得十分狭窄,FGD决定将公司迁至一些新的办公楼。FG
     D希望职员被安置在尽量多的办公楼当中,这样对于每个职员来说都会有一个相对更好的工作环境。但是,为了联
     系方便起见,如果两个职员被安置在两个不同的办公楼之内,他们必须拥有彼此的电话号码。

题解:很容易想到怎么写,就是求连通图的补图,可是怎么写?并查集暴力 n2,那就gg了,于是参看题解:链表+bfs

   好神啊,受教了。

代码:

 #include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
#define maxn 100000
#define maxm 2000000
using namespace std;
int ans,n,m,tot;
int now[maxn],v[maxm*],pre[maxm*],a[maxm],lis[maxm];
int ppre[maxm],next[maxm];
bool bo[maxm];
int read()
{
int x=; char ch; bool bo=;
while (ch=getchar(),ch<''||ch>'') if (ch=='-') bo=;
while (x=x*+ch-'',ch=getchar(),ch>=''&&ch<='');
if (bo) return -x; return x;
}
void ins(int a,int b)
{
++tot; pre[tot]=now[a]; now[a]=tot; v[tot]=b;
}
void del(int x)
{
int t=ppre[x];
next[t]=next[x];
ppre[next[x]]=t;
}
void bfs(int x)
{
int h=,t=;lis[]=x;
while (h!=t)
{
a[ans]++;
h++; int x=lis[h];
for (int i=now[x]; i; i=pre[i]) bo[v[i]]=;
for (int i=next[]; i<=n; i=next[i])
if (!bo[i])
{
del(i); lis[++t]=i;
}
for (int i=now[x]; i; i=pre[i]) bo[v[i]]=;
}
}
int main()
{
n=read(); m=read();
for (int i=; i<=n; i++) next[i]=i+;
for (int i=; i<=n+; i++) ppre[i]=i-;
for (int i=; i<=m; i++)
{
int u=read(),v=read();
ins(u,v); ins(v,u);
}
for (int i=next[]; i<=n; i=next[])
del(i),ans++,bfs(i);
printf("%d\n",ans);
sort(a+,a++ans);
for (int i=; i<=ans; i++)
printf("%d ",a[i]);
printf("\n");
}

7.bzoj1110

题目大意:在byteotian公司搬家的时候,他们发现他们的大量的精密砝码的搬运是一件恼人的工作。公司有一些固定容
     量的容器可以装这些砝码。他们想装尽量多的砝码以便搬运,并且丢弃剩下的砝码。每个容器可以装的砝码数量有
     限制,但是他们能够装的总重量不能超过每个容器的限制。一个容器也可以不装任何东西。任何两个砝码都有一个
     特征,他们的中总有一个的重量是另外一个的整数倍,当然他们也可能相等。

题解:第一眼dp,第二眼贪心,可是不知道怎么写,好吧,看了题解,再次受教,原来这个题可以利用进制这么个东东,太强了!

代码:

 #include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
#define maxn 100005
using namespace std;
int n,m,ans,tot;
int v[maxn],w[maxn],cnt[maxn];
int a[maxn],b[maxn],c[maxn];
int read()
{
int x=; char ch; bool bo=;
while (ch=getchar(),ch<''||ch>'') if (ch=='-') bo=;
while (x=x*+ch-'',ch=getchar(),ch>=''&&ch<='');
if (bo) return -x; return x;
}
bool chafen(int now)
{
for (int i=now+; i<=tot; i++)
if (cnt[i])
{
cnt[now]+=c[i]/c[now];
cnt[i]--;
return ;
}
return ;
}
int main()
{
n=read(); m=read();
for (int i=; i<=n; i++) a[i]=read();
for (int i=; i<=m; i++) b[i]=read();
sort(b+,b++m);
for (int i=; i<=m; i++)
if (b[i]!=b[i-]) c[++tot]=b[i];
for (int i=; i<=n; i++)
for (int j=tot; j; j--)
{
while (a[i]>=c[j])
a[i]-=c[j],cnt[j]++;
}
int now=; c[]=;
for (int i=; i<=m; i++)
{
while (b[i]>c[now]) now++;
if (cnt[now]) cnt[now]--;
else if (chafen(now)) cnt[now]--;
else
{
printf("%d\n",i-);
return ;
} }
printf("%d\n",m);
return ;
}

8.bzoj1109

题目大意:Mary在她的生日礼物中有一些积木。那些积木都是相同大小的立方体。每个积木上面都有一个数。Mary用他的
     所有积木垒了一个高塔。妈妈告诉Mary游戏的目的是建一个塔,使得最多的积木在正确的位置。一个上面写有数i
     的积木的正确位置是这个塔从下往上数第i个位置。Mary决定从现有的高塔中移走一些,使得有最多的积木在正确
     的位置。请你告诉Mary她应该移走哪些积木。

题解:我们设最后的答案的序列为S,那么这个序列则满足:i递增,a[i]递增,i-a[i]单调不减,这是一个三维偏序,但是真的需要全部满足吗,实际上i-a[i]  +  a[i] =i;

   于是只要a[i]递增,i-a[i]单调不减,二维偏序,随便搞搞就可以了。

代码:

 #include<iostream>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<algorithm>
#define maxn 100010
using namespace std;
struct data{
int x,y;
}b[maxn];
int tot,ans,n;
int a[maxn],c[maxn];
int read()
{
int x=; char ch; bool bo=;
while (ch=getchar(),ch<''||ch>'') if (ch=='-') bo=;
while (x=x*+ch-'',ch=getchar(),ch>=''&&ch<='');
if (bo) return -x; return x;
}
bool cmp(data a,data b){ if (a.x==b.x) return a.y<b.y; return a.x<b.x;
}
int lowbit(int x){return x&-x;
}
void updata(int x,int val)
{
for (int i=x; i<=n; i+=lowbit(i)) c[i]=max(c[i],val);
}
int query(int x)
{
int res=;
for (int i=x; i; i-=lowbit(i)) res=max(res,c[i]);
return res;
}
int main()
{
n=read();
for (int i=; i<=n; i++)
{
a[i]=read();
if (i<a[i]) continue;
b[++tot].x=i-a[i]; b[tot].y=a[i];
}
sort(b+,b++tot,cmp);
for (int i=; i<=tot; i++)
{
int temp=query(b[i].y-)+;
ans=max(ans,temp);
updata(b[i].y,temp);
}
printf("%d\n",ans);
return ;
}

9.bzoj1108

题目大意:Mary试图控制成都的天然气市场。专家已经标示出了最好的天然气井和中转站在成都的地图。现在需要将中转
     站和天然气井连接起来。每个中转站必须被连接到正好一个钻油井,反之亦然。 Mary特别指名,建设的天然气管
     道必须从某个天然气井开始,向南或者向东建设。Mary想知道怎么连接每个天然气井和中转站,使得需要的天然气
     管道的总长度最小。

题解:注意,注意,注意,是向南或者向东建设,于是难题瞬间变sb,自己想想!

代码:

 #include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<cstring>
#define inf 1000000000
#define ll long long
using namespace std;
inline int read()
{
int x=,f=;char ch=getchar();
while(ch<''||ch>''){if(ch=='-')f=-;ch=getchar();}
while(ch>=''&&ch<=''){x=x*+ch-'';ch=getchar();}
return x*f;
}
int n;
ll ans;
int main()
{
n=read();
for(int i=;i<=n;i++)
ans-=read(),ans+=read();
for(int i=;i<=n;i++)
ans+=read(),ans-=read();
printf("%lld\n",ans);
return ;
}

待更------------------------------------------------------------