Codeforces Round #601 (Div. 2)
C - League of Leesins
题意:有一个由数字1~n构成的序列,现在给你这个序列所有相邻3个数的值,他们不一定按顺序。现在要根据这些值输出原序列。有多解输出其中一个。如,具体见下图样例。
-
分析:
- 简单想一想就可以发现如果你知道ans[i-2],ans[i-1],那么就可以唯一确定ans[i]。当然第一个值p1和第二个值p2可以用各种方法都能得到,但是当想要用p1,p2确定下一个值时就不能遍历去却确定了。所以整道题的关键就变成了如何快速根据前面两个值去确定下一个值。
- 仔细想想维护由两个值确定一个值,而且这两个值还有可能确定其他值挺麻烦的,但是如果你发现其实每一行输入处了三个值外还有一个被忽略的值,那就是这三个值输入的位置(在数组中的下标)。
- 那么每次输入a,b,c。我们可以给他们维护所在下标,即每个值在第几个3元集中出现过。这样那个只出现一次的值就是p1,根据p1所在下标,找出现2次的数就是p2。就很容易了。
- 这时候就会发现一定有一个3元集中包含ans[i-2],ans[i-1],ans[i]。所以你在确定ans[i]时只需要找到这个3元集的下标就好办了。由于是从前面更新过来的,所以当你处理ans[i-2],ans[i-1],ans[i]时,ans[i-2]的其他所在3元集一定已经处理过了。所以只要你之前用标记数组vis标记已经处理的3元集下标,现在只要确定ans[i-2]中那个还没有处理的下标就是你要用来确定ans[i]的。具体见代码。
-
代码:
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int MA=1e5+15; int n,p1,p2,New; int a[MA],b[MA],c[MA]; vector<int>index[MA]; vector<int>ans; int vis[MA]; void in() { scanf("%d",&n); for(int i=1;i<=n-2;++i){ scanf("%d%d%d",&a[i],&b[i],&c[i]); index[a[i]].push_back(i); index[b[i]].push_back(i); index[c[i]].push_back(i); } for(int i=1;i<=n;++i){ if(index[i].size()==1){ p1=i; New=index[i][0]; break; } } } void findp2() { int i=index[p1][0]; if(a[i]==p1){ if(index[b[i]].size()==2){ p2=b[i]; } else { p2=c[i]; } } if(b[i]==p1){ if(index[a[i]].size()==2){ p2=a[i]; } else { p2=c[i]; } } if(c[i]==p1){ if(index[a[i]].size()==2){ p2=a[i]; } else { p2=b[i]; } } } void update_New(int x) { for(int i=0;i<index[x].size();++i){ int idx=index[x][i]; if(!vis[idx]){ New=idx; break; } } } int main() { in(); //输入并确定p1 ans.push_back(p1); findp2(); //确定p2 ans.push_back(p2); for(int i=3;i<=n;++i){ int other=a[New]+b[New]+c[New]-ans[i-3]-ans[i-2]; //由ans[i-3],ans[i-2],确定ans[i-1] vis[New]=1; //标记用过的下标 update_New(ans[i-2]); //更新New ans.push_back(other); } for(int i=0;i<ans.size();++i){ if(i==n-1) printf("%d\n",ans[i]); else printf("%d ",ans[i]); } return 0; }