Codeforces Round #300 解题报告(ABCDEF)

来源:互联网 发布:电子商务网络调研 编辑:程序博客网 时间:2024/05/28 16:12

A Cutting Banner

        给一个串,从头尾各切一段下来(也可以不切),问头+尾能否组成"CODEFORCES"。对头尾进行匹配分别得到长度,然后长度相加看是否>=10。


B Quasi Binary

        给一个数,问这个数最少能用多少个只包含0和1的数相加得到,输出任意解(数都是十进制)。贪心,但是要注意,设当前还需要凑cur,不能去找最大的不超过cur的只含01的数。比如20,如果减去11,剩下部分还要9个1。。远不如10+10。正确做法是按位来看,如果某一位大于0,那么该位填1,否则填0。


C Tourist's Notes

        爬山,每天海拔只能和前一天一样或增减1。给出总共爬的天数,记录下了某些天的海拔,问最高可能到达海拔。和之前的一场CF的某题几乎一样。。设相邻两个记录相距d(i+1)-d(i)天,高度为h(i)和h(i+1),中间可到达最大高度就是(d(i+1)-d(i)+h(i)+h(i+1))/2。第一个记录前和最后一个记录后让它逐天递增1就好,找出最大。


D Weird Chess

        在n*n的棋盘上,有个(dx,dy)的集合,定义了当棋子在(x,y)可以跳到(x+dx,y+dy)。给出一个棋盘,上面放了若干棋子,标出了这些棋子能跳到的所有位置。现在(2*n-1)*(2*n-1)的棋盘中心有一枚棋子,要求在上面画出(dx,dy)集合(任意解)。

        我的做法是暴力。对每一个(dx,dy),用棋盘上所有棋子去验证这个(dx,dy)是否可行。


E Demiurges Play Again

        一棵树,有m个叶子,所有叶子标有号1~m。两个人轮流从根开始往下走,走到叶子结束。先手希望走到的叶子号尽可能大,后手希望尽可能小,两个人都是最优决策。问先手安排叶子号和后手安排叶子号时,走到的结果分别是什么。

        这题目出得好,我弄了很久才理解。首先假设树的所有叶子号我们都已经知道。定义MAX(u)是希望最大的人先手u为根的子树会走到的结果,MIN(u)是希望最小的人先手u为根的子树会走到的结果。vi是u的孩子,那么,MAX(u)就等于MIN(vi)中的最大值,MIN(u)就等于MAX(vi)中的最小值。

        但是,树的叶子号我们不知道(可以安排)。只考虑子树中每个叶子的相对大小,就是在子树中所有叶子的rank(从大到小依次1,2,3...)。记f(u)为希望最大的人先行动u子树得到的rank,g(u)为希望最小的人先行动u子树得到的rank。那么对于叶子有f(u)=g(u)=1,非叶子有f(u)=min(g(vi)),g(u)=sum(f(vi))。最后结果就是m-f(1)+1,g(1)(因为对称性)。

#include <bits/stdc++.h>using namespace std;const int maxn=2e5+10;const int INF=1e9;int n;int head[maxn];int pre[maxn];int to[maxn];int f[maxn];int g[maxn];int tote;void addedge(int u,int v){to[tote]=v;pre[tote]=head[u];head[u]=tote++;}static int leafcnt=0;void dfs(int u){f[u]=INF;for(int i=head[u];~i;i=pre[i]){int v=to[i];dfs(v);if(g[v]<f[u])f[u]=g[v];g[u]+=f[v];}if(!g[u]){leafcnt++;g[u]=f[u]=1;}}int main(){memset(head,-1,sizeof(head));tote=0;cin>>n;for(int i=1;i<n;i++){int u,v;scanf("%d%d",&u,&v);addedge(u,v);}dfs(1);cout<<leafcnt-f[1]+1<<" "<<g[1]<<endl;return 0;}



F A Heap of Heaps

        给一个n个元素的数组。问如果把这个数组当成k(k=1,2,3...n-1)叉堆(小顶堆),分别有多少个元素不合法。不合法是指节点小于自己的父节点。

        首先对于k叉堆,每个元素孩子在数组中的下标范围是可以用O(1)计算出来的。我们可以将这n个数从小到大排序,记录下每个元素原来的位置。从小到大将元素的位置加入到树状数组里(其实是先查询),并查询树状数组,自己孩子的那个区间内有多少比自己小的。注意一个细节,先查询完同样大小的所有元素,再更新树状数组。详见代码。

        值得一提的是复杂度。因为2叉树大致有n/2个节点有孩子,3叉树大致有n/3,4叉n/4...调和级数的和趋近于ln(n),而树状数组的操作复杂度是log(n)。因此,总复杂度是nlognlogn。。。

#include <bits/stdc++.h>using namespace std;const int maxn=2e5+10;int n;int a[maxn];int rank[maxn];bool cmp(int x,int y){return a[x]<a[y];}inline int Lmostson(int id,int k){return id*k-k+2;}inline int Rmostson(int id,int k){return id*k+1;}inline int lowbit(int x){return x&(-x);}int c[maxn]; void update(int pos){while(pos<=n){c[pos]++;pos+=lowbit(pos);}}int query(int pos){int re=0;while(pos){re+=c[pos];pos-=lowbit(pos);}return re;}int ans[maxn];int main(){cin>>n;a[0]=-1e9-1;for(int i=1;i<=n;i++){scanf("%d",&a[i]);}for(int i=0;i<=n;i++)rank[i]=i;sort(rank,rank+n+1,cmp);for(int rnk=1;rnk<=n;rnk++){//枚举排第rnk位的数 int pos=rank[rnk];for(int k=1;k<n;k++){//k叉树 int lson=Lmostson(pos,k);if(lson>n)break;//没有孩子了,跳出 int rson=Rmostson(pos,k);if(rson>n)rson=n;//查询孩子中有多少个小于自己的int tmp=query(rson)-query(lson-1);ans[k]+=tmp;}//只有当下一个数不同才去更新 if(a[rank[rnk]]!=a[rank[rnk+1]]){int cur=rnk;while(1){update(rank[cur]);cur--;if(a[rank[cur]]!=a[rank[cur+1]])break;}}}for(int i=1;i<n;i++){printf("%d ",ans[i]);}return 0;}




0 0
原创粉丝点击