A.Islands

这种联通块的问题一看就知道是并查集的思想。

做法:从高水位到低水位依序进行操作,这样每次都有新的块浮出水面,可以在前面的基础上进行合并集合的操作。
给每个位置分配一个数字,方便合并集合。同时将这些数字也排一个序,降低枚举的复杂度。合并集合时向四周查询浮出水面但是没有合并到同一集合的点进行合并。

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
#define N 1007 struct node
{
int x,y,h;
}p[N*N]; int a[N*N],sh[],cnt[],fa[N*N];
int n,m;
int dx[] = {,,,-};
int dy[] = {,-,,}; int cmp(node ka,node kb)
{
return ka.h < kb.h;
} int findset(int x)
{
if(x != fa[x])
fa[x] = findset(fa[x]);
return fa[x];
} inline int ok(int x,int y)
{
if(x < n && x >= && y < m && y >= )
return ;
return ;
} int main()
{
int t,i,j,k,q,ind,pos;
scanf("%d",&t);
while(t--)
{
memset(cnt,,sizeof(cnt));
scanf("%d%d",&n,&m);
for(i=;i<n;i++)
{
for(j=;j<m;j++)
{
pos = i*m+j;
fa[pos] = pos;
scanf("%d",&a[pos]);
p[pos].x = i,p[pos].y = j,p[pos].h = a[pos];
}
}
scanf("%d",&q);
for(i=;i<q;i++)
scanf("%d",&sh[i]);
sort(p,p+n*m,cmp);
i = n*m-;
for(j=q-;j>=;j--)
{
cnt[j] = cnt[j+];
while(sh[j] < p[i].h) //浮出水面
{
cnt[j]++;
ind = p[i].x*m+p[i].y;
int fx = findset(ind);
for(k=;k<;k++)
{
int tx = p[i].x + dx[k];
int ty = p[i].y + dy[k];
if(!ok(tx,ty))
continue;
int newind = tx*m+ty;
int fy = findset(newind);
if(fx != fy && sh[j] < a[newind]) //浮出水面且没有合并
{
fa[fy] = fx; //不能使fa[fx] = fy.因为本次fx不会再findset.
cnt[j]--;
}
}
i--;
}
}
for(i=;i<q;i++)
printf("%d ",cnt[i]);
printf("\n");
}
return ;
}

B.母仪天下

线段树单点更新,区间查询。模板题,不解释。不会做说明你线段树很水。。

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#define Mod 1000000007
#define INF 1000000007
#define INT 2147483647
#define lll __int64
#define ll long long
using namespace std;
#define N 100007 struct node
{
ll sum;
}tree[*N]; void pushup(int rt)
{
tree[rt].sum = tree[*rt].sum + tree[*rt+].sum;
} void build(int l,int r,int rt)
{
if(l == r)
{
scanf("%lld",&tree[rt].sum);
return;
}
int mid = (l+r)/;
build(l,mid,*rt);
build(mid+,r,*rt+);
pushup(rt);
} void update(int l,int r,int pos,ll val,int rt)
{
if(l == r)
{
tree[rt].sum += val;
return;
}
int mid = (l+r)/;
if(pos <= mid)
update(l,mid,pos,val,*rt);
else
update(mid+,r,pos,val,*rt+);
pushup(rt);
} ll query(int l,int r,int aa,int bb,int rt)
{
if(aa <= l && bb >= r)
return tree[rt].sum;
int mid = (l+r)/;
ll res = ;
if(aa <= mid)
res += query(l,mid,aa,bb,*rt);
if(bb > mid)
res += query(mid+,r,aa,bb,*rt+);
return res;
} int main()
{
int m,n,aa,bb;
int i,pos;
ll val;
while(scanf("%d%d",&n,&m)!=EOF)
{
build(,n,);
while(m--)
{
scanf("%d",&i);
if(i == )
{
scanf("%d%d",&aa,&bb);
printf("%lld\n",query(,n,aa,bb,));
}
else
{
scanf("%d%lld",&pos,&val);
update(,n,pos,val,);
}
}
}
return ;
}

C.东风不与周郎便

线段树区间更新,区间查询。也是模板题,比B题较难,但是也是学线段树必须要会的。

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#define Mod 1000000007
#define INF 1000000007
#define INT 2147483647
#define lll __int64
#define ll long long
using namespace std;
#define N 100007 struct node
{
ll sum;
ll mark;
}tree[*N]; void pushup(int rt)
{
tree[rt].sum = tree[*rt].sum + tree[*rt+].sum;
} void pushdown(int l,int r,int rt)
{
if(!tree[rt].mark)
return;
int mid = (l+r)/;
tree[*rt].sum += tree[rt].mark*(ll)(mid-l+);
tree[*rt+].sum += tree[rt].mark*(ll)(r-mid);
tree[*rt].mark += tree[rt].mark;
tree[*rt+].mark += tree[rt].mark;
tree[rt].mark = ;
} void build(int l,int r,int rt)
{
if(l == r)
{
scanf("%lld",&tree[rt].sum);
tree[rt].mark = ;
return;
}
int mid = (l+r)/;
build(l,mid,*rt);
build(mid+,r,*rt+);
pushup(rt);
} void update(int l,int r,int aa,int bb,ll val,int rt)
{
if(aa <= l && bb >= r)
{
tree[rt].sum += val*(ll)(r-l+);
tree[rt].mark += val;
return;
}
pushdown(l,r,rt);
int mid = (l+r)/;
if(aa <= mid)
update(l,mid,aa,bb,val,*rt);
if(bb > mid)
update(mid+,r,aa,bb,val,*rt+);
pushup(rt);
} ll query(int l,int r,int aa,int bb,int rt)
{
if(aa <= l && bb >= r)
return tree[rt].sum;
pushdown(l,r,rt);
int mid = (l+r)/;
ll res = ;
if(aa <= mid)
res += query(l,mid,aa,bb,*rt);
if(bb > mid)
res += query(mid+,r,aa,bb,*rt+);
return res;
} int main()
{
int m,n,aa,bb;
int i;
ll val;
while(scanf("%d%d",&n,&m)!=EOF)
{
memset(tree,,sizeof(tree));
build(,n,);
while(m--)
{
scanf("%d",&i);
if(i == )
{
scanf("%d%d",&aa,&bb);
printf("%lld\n",query(,n,aa,bb,));
}
else
{
scanf("%d%d%lld",&aa,&bb,&val);
update(,n,aa,bb,val,);
}
}
}
return ;
}

D.长使英雄泪满襟

这题是原题,参考POJ 2482 Stars In Your Window

E.休生伤杜景死惊开

即用线段树求逆序数。(或树状数组)
求出每一个数字前面比他小的数字个数以及后面比他大的个数,然后相乘求和即为总的以这个数字为中心的锁的个数。
这里用线段树来实现求逆序数,分别从前面和后面进行查找,查找1~a[i]-1的数的个数。以从前往后查为例,因为是即插即查,所以这时区间内数的个数就是在数组前面比他小的数的个数。从后往前查同理。

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <cstdlib>
#define Mod 1000000007
#define INF 1000000007
#define INT 2147483647
#define lll __int64
#define ll long long
using namespace std;
#define N 50007 struct node
{
ll sum;
}tree[*N]; ll a[N],k1[N],k2[N]; void build(int l,int r,int rt)
{
tree[rt].sum = ;
if(l == r)
return;
int mid = (l+r)/;
build(l,mid,*rt);
build(mid+,r,*rt+);
} ll query(int l,int r,int aa,int bb,int rt)
{
if(aa > bb)
return ;
if(aa <= l && bb >= r)
return tree[rt].sum;
int mid = (l+r)/;
ll res = ;
if(aa <= mid)
res += query(l,mid,aa,bb,*rt);
if(bb > mid)
res += query(mid+,r,aa,bb,*rt+);
return res;
} void update(int l,int r,int pos,int rt)
{
tree[rt].sum++;
if(l == r)
return;
int mid = (l+r)/;
if(pos <= mid)
update(l,mid,pos,*rt);
else
update(mid+,r,pos,*rt+);
} int main()
{
int n,i,j;
while(scanf("%d",&n)!=EOF)
{
ll sum = ;
for(i=;i<=n;i++)
scanf("%lld",&a[i]);
build(,n,);
for(i=;i<=n;i++)
{
k1[i] = query(,n,,a[i]-,);
update(,n,a[i],);
}
build(,n,);
for(i=n;i>=;i--)
{
k2[i] = query(,n,,a[i]-,);
update(,n,a[i],);
}
for(i=;i<=n;i++)
sum += k1[i] * k2[i];
printf("%lld\n",sum);
}
return ;
}

F.天下归晋

树状数组可以解决,每条船的等级即统计每条船的左下方的船只数目。

将船只坐标由y从小到大排序,y坐标相同的根据x坐标从小到大排序。总之后面的点要保证在前面的点的右上方,这样插入时后面的船只不会影响到前面船只的等级,即前面船只的等级确定。

求和时sum(x)表示x坐标值小于等于x的船只个数
然后根据排序顺序将船只一个一个插入,求和,求出的和的那个等级的船只数加1,最后输出。

(注意树状数组c[]数组下标只能从1开始,所以所有坐标在处理时都加1)

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
#define N 202010 struct point
{
int x,y;
}a[N]; int n;
int c[N],ans[N]; int cmp(point ka,point kb)
{
if(ka.y == kb.y)
return ka.x < kb.x;
return ka.y < kb.y;
} int lowbit(int x)
{
return x&(-x);
} void modify(int num,int val)
{
while(num<=N)
{
c[num] += val;
num += lowbit(num);
}
} int sum(int x)
{
int res = ;
while(x>)
{
res += c[x];
x -= lowbit(x);
}
return res;
} int main()
{
int i,x,y;
while(scanf("%d",&n)!=EOF)
{
memset(c,,sizeof(c));
memset(ans,,sizeof(ans));
for(i=;i<n;i++)
scanf("%d%d",&a[i].x,&a[i].y);
sort(a,a+n,cmp);
for(i=;i<n;i++)
{
ans[sum(a[i].x+)]++;
modify(a[i].x+,);
}
for(i=;i<n;i++)
{
printf("%d\n",ans[i]);
}
}
return ;
}

G.程序设计竞赛

即线段树的区间最大连续和问题,我有一篇博文讲过了。

每个节点维护4个值:
max:此区间内的最大连续和
sum:该节点以下的节点值得总和
lmax:此区间的从左端开始的最大连续和
rmax:此区间的从右端开始的最大连续和
合并区间时,该区间的最大连续和为:max(左子节点的最大连续和,右子节点的最大连续和,左子节点的最大右连续和+右子节点的最大左连续和)

查询时返回一个整节点。因为每次要查询左子节点和右子节点,并且要比较它们的右连续最大和和左连续最大和,所以需要返回整个节点以便求值。

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
#define N 200007 struct node
{
int maxi,lmaxi,rmaxi,sum;
}tree[*N]; void pushup(int rt)
{
tree[rt].sum = tree[*rt].sum + tree[*rt+].sum;
tree[rt].maxi = max(tree[*rt].maxi,max(tree[*rt+].maxi,tree[*rt].rmaxi+tree[*rt+].lmaxi));
tree[rt].lmaxi = max(tree[*rt].lmaxi,tree[*rt].sum + tree[*rt+].lmaxi);
tree[rt].rmaxi = max(tree[*rt+].rmaxi,tree[*rt+].sum + tree[*rt].rmaxi);
} void build(int l,int r,int rt)
{
if(l == r)
{
scanf("%d",&tree[rt].sum);
tree[rt].maxi = tree[rt].lmaxi = tree[rt].rmaxi = tree[rt].sum;
return;
}
int mid = (l+r)/;
build(l,mid,*rt);
build(mid+,r,*rt+);
pushup(rt);
} void update(int l,int r,int pos,int val,int rt)
{
if(l == r)
{
tree[rt].maxi = tree[rt].lmaxi = tree[rt].rmaxi = tree[rt].sum = val;
return;
}
int mid = (l+r)/;
if(pos <= mid)
update(l,mid,pos,val,*rt);
else
update(mid+,r,pos,val,*rt+);
pushup(rt);
} node query(int l,int r,int aa,int bb,int rt)
{
if(aa <= l && bb >= r)
return tree[rt];
int mid = (l+r)/;
node ka,kb,res;
int flag1 = ;
int flag2 = ;
if(aa <= mid)
{
ka = query(l,mid,aa,bb,*rt);
flag1 = ;
}
if(bb > mid)
{
kb = query(mid+,r,aa,bb,*rt+);
flag2 = ;
}
if(flag1 && flag2)
{
res.sum = ka.sum + kb.sum;
res.lmaxi = max(ka.lmaxi,ka.sum+kb.lmaxi);
res.rmaxi = max(kb.rmaxi,kb.sum+ka.rmaxi);
res.maxi = max(ka.rmaxi+kb.lmaxi,max(ka.maxi,kb.maxi));
}
else
{
if(flag1) //left
res = ka;
else
res = kb;
}
return res;
} int main()
{
int n,m,op,aa,bb;
scanf("%d%d",&n,&m);
build(,n,);
while(m--)
{
scanf("%d%d%d",&op,&aa,&bb);
if(!op)
{
node res = query(,n,aa,bb,);
printf("%d\n",res.maxi);
}
else
update(,n,aa,bb,);
}
return ;
}

H.Cookies Test

线段树解决。每次将数据插入到合适位置,注意这里的值比较大,所以需要哈希,哈希过后就能开线段树了。

遇到查询时就查询线段树中第中位数个点(值),返回其值,然后将该点置一个标记,表示没有了。同时更新其上的节点的sum值(子树的数(有值的叶子节点)的个数)。

代码不太容易懂。我也是头一次写这种嵌套很多层的数组的代码。
代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <cstdlib>
#include <string>
#include <vector>
#include <map>
#define Mod 1000000007
#define INF 1000000007
#define INT 2147483647
#define lll __int64
#define ll long long
using namespace std;
#define N 600007 struct node
{
int sum;
}tree[*N]; struct A
{
int ind,val;
}a[N],b[N]; int hash[N],k[N]; int cmp(A ka,A kb)
{
return ka.val < kb.val;
} void build(int l,int r,int rt)
{
tree[rt].sum = ;
if(l == r)
return;
int mid = (l+r)/;
build(l,mid,*rt);
build(mid+,r,*rt+);
} int query(int l,int r,int pos,int rt)
{
tree[rt].sum--;
if(l == r)
return l;
int mid = (l+r)/;
if(tree[*rt].sum >= pos)
return query(l,mid,pos,*rt);
else
return query(mid+,r,pos-tree[*rt].sum,*rt+);
} void update(int l,int r,int pos,int rt)
{
tree[rt].sum++;
if(l == r)
return;
int mid = (l+r)/;
if(pos <= mid)
update(l,mid,pos,*rt);
else
update(mid+,r,pos,*rt+);
} int main()
{
int n,i,j,m;
char ss[];
i = ;
while(scanf("%s",ss)!=EOF)
{
if(ss[] == '#')
a[i].val = b[i].val = INF,a[i].ind = b[i].ind = i++;
else
a[i].val = b[i].val = atoi(ss),a[i].ind = b[i].ind = i++;
}
m = i-;
sort(b+,b+m+,cmp);
int now = ;
int pre = -;
for(i=;i<=m;i++)
{
if(b[i].val == INF)
continue;
if(b[i].val != pre)
{
pre = b[i].val;
b[i].val = now++;
}
else
b[i].val = now-;
hash[b[i].val] = b[i].ind;
}
for(i=;i<=m;i++)
k[b[i].ind] = b[i].val;
n = ;
build(,n,);
int cnt = ;
for(i=;i<=m;i++)
{
if(k[i] == INF)
{
if(cnt%) //odd
printf("%d\n",a[hash[query(,n,(cnt+)/,)]].val);
else
printf("%d\n",a[hash[query(,n,cnt/+,)]].val);
cnt--;
}
else
{
update(,n,k[i],);
cnt++;
}
}
return ;
}

I.方师傅学数字逻辑

J.方师傅的01串

字典树,结构node维护两个值: count 和 deep ,结果即为节点的count * deep 的最大值。

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
#define N 50007 struct node
{
int count,deep;
node *next[];
}*root; char ss[N];
int maxi; node *create()
{
node *p;
p = (node *)malloc(sizeof(node));
p->count = ;
p->deep = ;
for(int i=;i<;i++)
p->next[i] = NULL;
return p;
} void release(node *p)
{
for(int i=;i<;i++)
{
if(p->next[i] != NULL)
release(p->next[i]);
}
free(p);
} void insert(char *ss)
{
node *p = root;
int i = ,k;
while(ss[i])
{
k = ss[i++] - '';
if(p->next[k] == NULL)
p->next[k] = create();
p->next[k]->deep = p->deep + ;
p = p->next[k];
p->count++;
maxi = max(maxi,p->count*p->deep);
}
} int main()
{
int t,n,i;
root = create();
scanf("%d",&n);
maxi = -;
for(i=;i<n;i++)
{
scanf("%s",ss);
insert(ss);
}
cout<<maxi<<endl;
release(root);
return ;
}

K.方师傅与栈

栈上的模拟题。
根据终态来定过程。
终态此时需要出现什么值,初始栈中就要一直找到该值,找的过程中将值都压入一个栈中,然后将该值出栈,如此循环往复,总之要依循终态的顺序。
出现以下两种情况则答案是NO:
1.终态的数还没模拟完,原始数组中的数就已经全部压入栈中了。并且栈顶还不等于终态此时需要的数
2.如果最后栈不为空,说明还有没有匹配的,自然不行。

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
#define N 1000007 int a[N],want[N],stk[N]; int main()
{
int n,i,k,top;
scanf("%d",&n);
for(i=;i<=n;i++)
scanf("%d",&a[i]);
for(i=;i<=n;i++)
scanf("%d",&want[i]);
k = ;
top = ;
stk[] = ;
int flag = ;
for(i=;i<=n;i++)
{
while(stk[top] != want[i])
{
if(k <= n)
stk[++top] = a[k++];
else
{
flag = ;
break;
}
}
if(stk[top] == want[i])
top--;
if(!flag)
break;
}
if(!flag || top)
puts("No");
else
puts("Yes");
return ;
}

L.冰雪奇缘

M.方师傅玩炉石

(没做出来的以后更新)

2014 UESTC暑前集训数据结构专题解题报告的更多相关文章

  1. 2014 UESTC暑前集训搜索专题解题报告

    A.解救小Q BFS.每次到达一个状态时看是否是在传送阵的一点上,是则传送到另一点即可. 代码: #include <iostream> #include <cstdio> # ...

  2. 2014 UESTC暑前集训动态规划专题解题报告

    A.爱管闲事 http://www.cnblogs.com/whatbeg/p/3762733.html B.轻音乐同好会 C.温泉旅馆 http://www.cnblogs.com/whatbeg/ ...

  3. 2014 UESTC暑前集训图论专题解题报告

    A.方老师和缘分 http://www.cnblogs.com/whatbeg/p/3765621.html B.方老师和农场 http://www.cnblogs.com/whatbeg/p/376 ...

  4. 2014 UESTC 暑前集训队内赛(1) 解题报告

    A.Planting Trees 排序+模拟 常识问题,将耗时排一个序,时间长的先种,每次判断更新最后一天的时间. 代码: #include <iostream> #include < ...

  5. 2014 UESTC 暑前集训队内赛(3) 部分解题报告

    B.Battle for Silver 定理:完全图Kn是平面图当且仅当顶点数n<=4. 枚举所有完全图K1,K2,K3,K4,找出最大总权重. 代码: #include <iostrea ...

  6. 2014 UESTC 暑前集训队内赛(2) 部分解题报告

    B.Cuckoo for Hashing 模拟题. 代码: #include <iostream> #include <cstdio> #include <cstring ...

  7. Facebook Hacker Cup 2014 Qualification Round 竞赛试题 Square Detector 解题报告

    Facebook Hacker Cup 2014 Qualification Round比赛Square Detector题的解题报告.单击这里打开题目链接(国内访问需要那个,你懂的). 原题如下: ...

  8. 「SHOI2016」黑暗前的幻想乡 解题报告

    「SHOI2016」黑暗前的幻想乡 sb题想不出来,应该去思考原因,而不是自暴自弃 一开始总是想着对子树做dp,但是状态压不起去,考虑用容斥消减一些条件变得好统计,结果越想越乱. 期间想过矩阵树定理, ...

  9. [雅礼NOIP集训 2017] number 解题报告 (组合数+二分)

    题解: 令$S(i)={i+1,i+2,...,i<<1}$,f(i,k)表示S(i)中在二进制下恰好有k个1的数的个数 那么我们有$f(i,k)=\sum_{x=1}^{min(k,p) ...

随机推荐

  1. 唯美!分享8款响应式的 WordPress 餐厅主题

    您是否拥有一个餐厅,酒吧,咖啡馆,小酒馆,比萨饼店?如果答案是肯定的,请确保您在网上也提供服务.为了使您的工作更轻松,我们选择了一些新的和独特的餐厅主题,覆盖了范围很广的食品企业.这些主题提供了很多很 ...

  2. 设置时间 new Date

    1.标准格式下的时间是object ,操作时可以通过 var cDate=new Date(startTime.getTime());来复制当前日期避免操作给初始日期带来影响 2. 以月为单位计算想要 ...

  3. ae_datagridview显示属性

    public partial class FrmAttributeTable : Form { private AxMapControl m_MapCtl; public FrmAttributeTa ...

  4. 如何实现SP文档库类似百度文档库的效果 (副标题:如何在SP2013文档库的SWF文件用FlexPager显示)

    1. 编辑文档库列表显示页面,如下图: 2. 添加内容编辑器,如下图: 3. 添加如下在[内容编辑器中]-[编辑源],添加如下JS代码,如下图: ​ 代码如下: <scrip type=&quo ...

  5. 为ListView组件加上快速滑块以及修改快速滑块图像

    本文转载自:http://blog.csdn.net/ouyang_peng/article/details/46919723 作者:欧阳鹏  欢迎转载,与人分享是进步的源泉! 转载请保留原文地址:h ...

  6. Android 在C代码中调用logcat

    本文给<Android java传递int类型数组给C>中添加C代码中调用logcat的功能 Android.mk文件增加以下内容 LOCAL_LDLIBS += -llog C代码中增加 ...

  7. xcode 删除文件后编译会出现*** is missing from working copy

    删除文件后  工程中会出现如图所示 如果你使用了svn管理工具  你就会看到如图所示 然后  选中  删除 就可以了 好了 多了 不多说了    最近比较忙   博客写的比较 少   等闲了  一定会 ...

  8. Android中将xml布局文件转化为View树的过程分析(下)-- LayoutInflater源码分析

    在Android开发中为了inflate一个布局文件,大体有2种方式,如下所示: // 1. get a instance of LayoutInflater, then do whatever yo ...

  9. [转 载] android 谷歌 新控件(约束控件 )ConstraintLayout 扁平化布局

    序 在Google IO大会中不仅仅带来了Android Studio 2.2预览版,同时带给我们一个依赖约束的库. 简单来说,她是相对布局的升级版本,但是区别与相对布局更加强调约束.何为约束,即控件 ...

  10. 学习Coding-iOS开源项目日志(一)

    前言:作为初级程序员,想要提高自己的水平,其中一个有效的学习方法就是学习别人好的项目.本篇开始会陆续更新本人对github上开源的一个很不错的项目的一点点学习积累.也就是,探究着别人写的源码,我学到了 ...