2014 UESTC暑前集训数据结构专题解题报告
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暑前集训数据结构专题解题报告的更多相关文章
- 2014 UESTC暑前集训搜索专题解题报告
A.解救小Q BFS.每次到达一个状态时看是否是在传送阵的一点上,是则传送到另一点即可. 代码: #include <iostream> #include <cstdio> # ...
- 2014 UESTC暑前集训动态规划专题解题报告
A.爱管闲事 http://www.cnblogs.com/whatbeg/p/3762733.html B.轻音乐同好会 C.温泉旅馆 http://www.cnblogs.com/whatbeg/ ...
- 2014 UESTC暑前集训图论专题解题报告
A.方老师和缘分 http://www.cnblogs.com/whatbeg/p/3765621.html B.方老师和农场 http://www.cnblogs.com/whatbeg/p/376 ...
- 2014 UESTC 暑前集训队内赛(1) 解题报告
A.Planting Trees 排序+模拟 常识问题,将耗时排一个序,时间长的先种,每次判断更新最后一天的时间. 代码: #include <iostream> #include < ...
- 2014 UESTC 暑前集训队内赛(3) 部分解题报告
B.Battle for Silver 定理:完全图Kn是平面图当且仅当顶点数n<=4. 枚举所有完全图K1,K2,K3,K4,找出最大总权重. 代码: #include <iostrea ...
- 2014 UESTC 暑前集训队内赛(2) 部分解题报告
B.Cuckoo for Hashing 模拟题. 代码: #include <iostream> #include <cstdio> #include <cstring ...
- Facebook Hacker Cup 2014 Qualification Round 竞赛试题 Square Detector 解题报告
Facebook Hacker Cup 2014 Qualification Round比赛Square Detector题的解题报告.单击这里打开题目链接(国内访问需要那个,你懂的). 原题如下: ...
- 「SHOI2016」黑暗前的幻想乡 解题报告
「SHOI2016」黑暗前的幻想乡 sb题想不出来,应该去思考原因,而不是自暴自弃 一开始总是想着对子树做dp,但是状态压不起去,考虑用容斥消减一些条件变得好统计,结果越想越乱. 期间想过矩阵树定理, ...
- [雅礼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) ...
随机推荐
- 2013最常用的NoSQL数据库
摘要:与关系数据库相比,每个NoSQL都有自己不同的适用场景,这里带大家盘点文档数据库.图数据库.键值数据存储.列存储数据库与内存数据网络等领域的常用的NoSQL. 在几年内,NoSQL数据库一直以性 ...
- word第二讲(0806)
word里的长度单位 绝对长度单位(厘米,英寸) 相对长度单位(字符,像素) 样式 问题: 如何设置多个部分的格式 多次选择,多次设置 多次选择,一次设置 应用格式刷(ctrl+shift+c,ctr ...
- [翻译] GONMarkupParser
GONMarkupParser https://github.com/nicolasgoutaland/GONMarkupParser NSString *inputText = @"Sim ...
- 【代码笔记】iOS-点击加号增加书架,点击减号减少书架
一,效果图. 二,工程图. 三,代码. ReaderViewController.h #import <UIKit/UIKit.h> @interface ReaderViewContro ...
- Caliburn.Micro 关闭父窗体打开子窗体
比如我们在做登录的时候需要关闭父窗体打开子窗体.使用Caliburn.Micro它的时候我们关闭登录窗口的时候主页面也会关闭. 解决方法就是在登录页面的CS里面写 IndexView iv = new ...
- iOS 学习资源
这份学习资料是为 iOS 初学者所准备的, 旨在帮助 iOS 初学者们快速找到适合自己的学习资料, 节省他们搜索资料的时间, 使他们更好的规划好自己的 iOS 学习路线, 更快的入门, 更准确的定位的 ...
- 0. CMMI快乐之旅——内容简介及目录
摘要: 这是我几年前发表于 www.cmmionline.net 网站(现在升级为www.umlonline.org 网站)数十篇文章,全方位分享了我对CMMI的理解,现我打算整理这些文章陆续在CSD ...
- IO流04--毕向东JAVA基础教程视频学习笔记
Day20 01 File概述02 File对象功能-创建和删除03 File对象功能-判断04 File对象功能-获取05 File对象功能-文件列表106 File对象功能-文件列表207 列出目 ...
- 编写可测试的JavaScript代码
<编写可测试的JavaScript代码>基本信息作者: [美] Mark Ethan Trostler 托斯勒 著 译者: 徐涛出版社:人民邮电出版社ISBN:9787115373373上 ...
- ORA-00030: User session ID does not exist.
同事在Toad里面执行SQL语句时,突然无线网络中断了,让我检查一下具体情况,如下所示(有些信息,用xxx替换,因为是在处理那些历史归档数据,使用的一个特殊用户,所以可以用下面SQL找到对应的会话信息 ...