A. Case of Matryoshkas
题目链接
http://codeforces.com/contest/555/problem/A
题目大意
俄罗斯套娃。
一套套娃的形态如一条链:1->2->3->4…
可以对一条链进行断开操作:1->2->3->4变成1->2和3->4
也可以在一条链后面套上一个新数字,但是新数字必须是链尾数字大小+1:1->2->3->4+5变成1->2->3->4->5
现在给你若干个代表套娃的数字链,在所有的数字链中,数字1~n里每个数字只出现过一次,问最少进行多少次操作,才能使所有的套娃套在一起,成为数字链1->2->3->…->n
思路
很显然,除了1开头的连续那段数字链(1->2->3…)以外,其他部分的数字链在一开始就需要全部断开,然后把这些断开的数字再从小到大一个一个套在1开头的连续的数字链后面。
这样这个题目的做法就很简单了,找出1开头的连续那段数字链的大小,然后对初始的所有数字链求出全部断开它们、再重新拼接它们所需的步数,扣去因为1开头的连续那段数字链不需要动而不必操作的步数即可。
代码
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#define MAXN 210000
using namespace std;
int tmp[MAXN],n,m;
int ans=0,maxblock=0;
bool flag;
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
int tot,L=1,R=1;
scanf("%d",&tot);
scanf("%d",&tmp[1]);
for(int j=2;j<=tot;j++)
{
scanf("%d",&tmp[j]);
if(tmp[j]==tmp[j-1]+1)
R++;
else
{
if(maxblock<R-L+1)
{
if(tmp[L]==1)
maxblock=R-L+1;
}
R++;
L=R;
}
}
if(maxblock<R-L+1)
{
if(tmp[L]==1)
maxblock=R-L+1;
}
ans+=tot*2-1;
}
printf("%d\n",ans-maxblock-maxblock+1);
return 0;
}
B. Case of Fugitive
题目链接
http://codeforces.com/contest/555/problem/B
题目大意
给出
n
个岛屿以及
思路
比赛时想到的贪心做法是错的,所以比赛时没做出来这个题QAQQAQ
对于桥 i,i+1 而言,我们约定它们之间架设的桥的合法长度为区间 [minvi,maxvi]
可以考虑按照长度从小到大枚举每个桥,那么对于一个长度为 leni 的桥而言,我们已经让其之前枚举过的每个桥都放在某些间隔上了(实在没办法放的除外)。现在它可以架设在剩下的所有的 minvj≤leni≤maxvj 的岛屿 j,j+1 之间。而这个桥放在所有的合法岛屿点对 (j,j+1) 里 maxvj 最小的那个岛屿点对是最优的,因为这个岛屿点对是最难满足要求的,如果这一次不把这个桥放在那个岛屿间隔上,那么以后可能这个岛屿间隔就无法放其他更大的桥了。
我们可以先预处理对每个岛屿间隔按照
minv
进行排序。在枚举过程中维护一个按照
maxvj
大小作为关键字的小根堆,其中包含的是剩下没有放桥的所有的
minvj≤leni≤maxvj
的岛屿
j,j+1
。枚举完长度第
i
小桥,开始枚举长度第
代码
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <queue>
#define MAXN 210000
using namespace std;
typedef long long int LL;
pair<LL,LL> pr[MAXN];
pair<LL,LL> bridgelen[MAXN];
int n,m,ans[MAXN];
struct Info
{
LL minv,maxv;
int id;
Info(){}
Info(LL _minv,LL _maxv,int _id):minv(_minv),maxv(_maxv),id(_id){}
}islandlength[MAXN];
bool cmp(Info a,Info b)
{
return a.minv<b.minv;
if(a.minv==b.minv) return a.maxv<b.maxv;
}
bool operator<(Info a,Info b)
{
return a.maxv<b.maxv;
}
bool operator>(Info a,Info b)
{
return a.maxv>b.maxv;
}
priority_queue<Info,vector<Info> ,greater<Info> >pq;
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%I64d%I64d",&pr[i].first,&pr[i].second);
for(int i=1;i<=m;i++) scanf("%I64d",&bridgelen[i].first),bridgelen[i].second=i;
for(int i=1;i<n;i++)
islandlength[i]=Info(pr[i+1].first-pr[i].second,pr[i+1].second-pr[i].first,i);
sort(bridgelen+1,bridgelen+m+1);
sort(islandlength+1,islandlength+n,cmp);
int p=1; //已经放入队列的岛屿间隙是[1,p-1]
for(int i=1;i<=m;i++)
{
Info now;
while(!pq.empty())
{
now=pq.top();
if(now.maxv<bridgelen[i].first) pq.pop();
else break;
}
while(bridgelen[i].first>=islandlength[p].minv&&bridgelen[i].first<=islandlength[p].maxv&&p<=n-1)
{
pq.push(islandlength[p]);
p++;
}
if(pq.empty()) continue;
now=pq.top();
pq.pop();
ans[now.id]=bridgelen[i].second;
}
for(int i=1;i<n;i++)
if(!ans[i])
{
printf("No\n");
return 0;
}
printf("Yes\n");
for(int i=1;i<n;i++) printf("%d ",ans[i]);
printf("\n");
return 0;
}
C. Case of Chocolate
题目链接
http://codeforces.com/contest/555/problem/C
题目大意
给你一个三角状的网格,每次你可以选择斜边上的某格巧克力,向上或向左截取尽量长的连续的巧克力,每次操作后问最多可以截取多长的巧克力。
思路
离散化+线段树
我们可以维护两棵支持区间取max修改+区间查询最大值的线段树,分别代表行和列上,每个列\行里下标最大的被截取的那个格子的标号。如下图分别代表了维护列、维护行的线段树里,每个下标对应的最大值位置(图中星形):
那么每次查询时,我们只需要在其中一个线段树里查询某个点的最大值,就可以得到答案了,然后再去区间更新两个线段树
由于此题n的范围太大,因此需要离线进行离散化,只保留下在线段树中可能需要的下标(也就是每个询问的x和y)。而且很显然可以发现,每次线段树操作的数字都会在所有询问的数字x和y里出现,因此离散化的做法没有问题
代码
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <map>
#define MAXN 210000
using namespace std;
struct SegmentTree
{
int maxv[MAXN<<2],pushdowntag[MAXN<<2];
void pushdown(int o)
{
maxv[o<<1]=max(maxv[o<<1],pushdowntag[o]);
maxv[o<<1|1]=max(maxv[o<<1|1],pushdowntag[o]);
pushdowntag[o<<1]=max(pushdowntag[o],pushdowntag[o<<1]);
pushdowntag[o<<1|1]=max(pushdowntag[o],pushdowntag[o<<1|1]);
}
void pushup(int o)
{
maxv[o]=max(maxv[o<<1],maxv[o<<1|1]);
}
void update(int o,int L,int R,int ql,int qr,int val)
{
if(ql<=L&&R<=qr)
{
maxv[o]=max(maxv[o],val);
pushdowntag[o]=max(pushdowntag[o],val);
return;
}
pushdown(o);
int M=(L+R)>>1;
if(ql<=M) update(o<<1,L,M,ql,qr,val);
if(qr>M) update(o<<1|1,M+1,R,ql,qr,val);
pushup(o);
}
int query(int o,int L,int R,int ql,int qr)
{
if(ql<=L&&R<=qr)
return maxv[o];
pushdown(o);
int M=(L+R)>>1,ans=0;
if(ql<=M) ans=max(ans,query(o<<1,L,M,ql,qr));
if(qr>M) ans=max(ans,query(o<<1|1,M+1,R,ql,qr));
pushup(o);
return ans;
}
}segtreeCol,segtreeRow;
struct Query
{
int x,y;
char cmd[5];
}query[MAXN];
int n,q;
map<int,int>idOfRow,idOfCol; //每一行离散化后的标号、每一列离散化后的标号
int stackOfCol[MAXN],topCol,stackOfRow[MAXN],topRow;
int main()
{
scanf("%d%d",&n,&q);
for(int i=1;i<=q;i++)
{
scanf("%d%d%s",&query[i].x,&query[i].y,query[i].cmd);
stackOfCol[++topCol]=query[i].x;
stackOfRow[++topRow]=query[i].y;
}
topCol=unique(stackOfCol+1,stackOfCol+topCol+1)-stackOfCol;
topRow=unique(stackOfRow+1,stackOfRow+topRow+1)-stackOfRow;
sort(stackOfCol+1,stackOfCol+topCol+1);
sort(stackOfRow+1,stackOfRow+topRow+1);
for(int i=1;i<=topCol;i++)
idOfCol[stackOfCol[i]]=i;
for(int i=1;i<=topRow;i++)
idOfRow[stackOfRow[i]]=i;
for(int i=1;i<=q;i++)
{
if(query[i].cmd[0]=='U')
{
int tmp=segtreeCol.query(1,1,topCol,idOfCol[query[i].x],idOfCol[query[i].x]);
printf("%d\n",query[i].y-tmp);
if(!(query[i].y-tmp)) continue;
segtreeCol.update(1,1,topCol,idOfCol[query[i].x],idOfCol[query[i].x],query[i].y);
segtreeRow.update(1,1,topRow,idOfRow[tmp],idOfRow[query[i].y],query[i].x);
}
else
{
int tmp=segtreeRow.query(1,1,topRow,idOfRow[query[i].y],idOfRow[query[i].y]);
printf("%d\n",query[i].x-tmp);
if(!(query[i].x-tmp)) continue;
segtreeRow.update(1,1,topRow,idOfRow[query[i].y],idOfRow[query[i].y],query[i].x);
segtreeCol.update(1,1,topCol,idOfCol[tmp],idOfCol[query[i].x],query[i].y);
}
}
return 0;
}