1 条题解
-
0
自动搬运
来自洛谷,原作者为

demerzel_iv
**搬运于
2025-08-24 21:51:42,当前版本为作者最后更新于2017-04-10 16:55:21,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
首先我们来考虑如何建模。
把中场的每个团体看做左括号。结束时的每个团体看做右括号。然后按照人气值从小到大排序。
这样我们就得到了一个括号序列。每个括号都有一个编号(即其学校编号)。
这个括号序列一开始一定是合法的。因为题目保证有解(虽然题目中并没有写= =)。
再看题目的要求。相当于修改尽量少的右括号的编号。使得每个左括号都能找到一个编号相同的右括号与之对应。
那么我们第一步可以贪心地将已经匹配了的相同编号的括号都删去。
删完后剩下的括号序列就不一定合法了= =。我们称之为坏掉的括号序列。
所以要重新加入一些之前删掉的匹配括号使得序列重新合法。在这过程中可以方便地统计答案。
并且你加入的括号对要尽量少。因为每加入一次都要将相应的右括号编号修改。使得满足题目要求。
考虑加入一对括号时会发生什么:
) <--未匹配的右括号 ==> ( ) )( ) <--新加入的括号 左边产生一对匹配。右边多出一个未匹配的右括号
这相当于加入一对括号使得某个右括号向右边移动了。
那么我们将所有可加入的括号对 按照左端点从小到大排序。然后从左往右扫。用一个堆来维护当前最远的右端点。
如果遇到一个坏掉的括号序列中的右括号。就取出堆顶的位置。并将这个右括号向右移动。可以证明这一定是最优的。
再考虑如何处理坏掉的括号序列中的左括号。
我们将坏掉的括号序列中每一个左括号都看做 右括号在无穷远处的一个括号对。这样它在堆中的优先级也会是最高的。
而当它与一个右括号匹配时。那个右括号将被移到无穷远处。
我们只要将所有的右括号都移到无穷远处就可以了。
统计答案?
加入一对括号时。会产生一对新的匹配括号。和一个新的右括号。
我们只需要将这对括号的右括号编号修改使得它们编号相同。而不需要处理那个新的右括号(想一想)。
这样可以很方便的统计答案了。
好像讲起来不是很明白。不如看看代码= =。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<vector> #include<queue> #include<set> namespace metaphysics { const int N=201000,M=N<<1,INF=2147483647; typedef std::pair<int,int> pii; struct key{int ty,id,w,pos;key(){}key(int a,int b,int c){ty=a,id=b,w=c;}}; //ty表示左右括号 id表示编号 w为人气值 pos为排序后的位置 bool operator < (key a,key b){return a.w==b.w?a.ty>b.ty:a.w<b.w;} key s[M]; pii t[N]; std::priority_queue<int>Q; std::priority_queue<int,std::vector<int>,std::greater<int>>S; //这是小根堆 bool done[M]; int begin[N],next[M],pre[M];//链表 用来将相同编号的括号连起来 int n,m,tot,ans; void gotin() { scanf("%d",&n); for(int i=1,x,y;i<=n;i++)scanf("%d%d",&x,&y),s[i]=key(1,x,y); for(int i=1,x,y;i<=n;i++)scanf("%d%d",&x,&y),s[n+i]=key(-1,x,y); m=n;n<<=1; std::sort(s+1,s+n+1); //排序得到括号序列 for(int i=1;i<=n;i++)s[i].pos=i; for(int i=n,x;i;i--) { x=s[i].id; pre[begin[x]]=i; next[i]=begin[x]; begin[x]=i; } //连接同编号的括号 } void first_greedy() { tot=0; for(int i=1,pos;i<=m;i++) for(pos=begin[i];pos;pos=next[pos]) if(s[pre[pos]].ty>0 && s[pos].ty<0) { pos[next][pre]=pos[pre][pre]; //相当于 pre[next[pos]]=pre[pre[pos]] pos[pre][pre][next]=pos[next]; //相当于 next[pre[pre[pos]]]=next[pos] pos[done]=pos[pre][done]=1; //相当于 done[pos]=done[pre[pos]]=1 //done数组 用来记录是否被匹配 t[++tot]=pii(pre[pos],pos); } //贪心地消除 已匹配的 同编号括号对 int cnt=0; while(!S.empty())S.pop(); for(int i=1;i<=n;i++) if(done[i])continue; else if((s[++cnt]=s[i]).ty>0)t[++tot]=pii(s[i].pos,INF);//将左括号看做右端点无穷远的括号对 else S.push(s[i].pos); std::sort(t+1,t+tot+1); //将所有可加入括号对排序 } void second_not_greedy() { while(!Q.empty())Q.pop(); ans=0; int maxr,mit=S.top(); //mit表示当前最左边的 待匹配右括号 的位置 for(int i=1;i<=tot && !S.empty();i++) { Q.push(t[i].second); while((t[i+1].first>mit || i==tot) && !S.empty()) { maxr=Q.top();Q.pop(); //maxr表示当前可匹配括号中最远的右括号的位置 //如果maxr等于INF 我们可以将其匹配掉 if(maxr<INF)S.push(maxr); S.pop(); mit=S.top(); ans++; //统计答案 } } } void solve() { gotin(); first_gredy(); second_not_gredy(); printf("%d\n",ans); } } int main() { metaphysics::solve(); return 0; }
- 1
信息
- ID
- 2682
- 时间
- 1000ms
- 内存
- 125MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者