题目大意
有一个正 n n n边形,每个点的颜色为红色、蓝色、绿色中的一种,保证每种颜色至少出现一次且 n n n边形上相邻的两个点颜色不同。你想要连接 n − 3 n-3 n−3条对角线,使得对角线把图分成 n − 2 n-2 n−2个三角形,并且每个三角形的三个顶点的颜色两两不同。
请输出任意一个合法的方案,有 n − 3 n-3 n−3行,每行两个正整数 x , y x,y x,y,表示一条对角线。如果不存在合法方案,则输出 I m p o s s i b l e ! Impossible! Impossible!。
样例输入
6
RBRGBG
样例输出
2 6
3 5
3 6
4 ≤ n ≤ 1 0 6 4\leq n\leq 10^6 4≤n≤106
题解
如果一个颜色只出现了一次,则因为相邻的两个点颜色不同,我们直接将这个点与所有其他点连接即可。
否则,一定会出现相邻三个点颜色两两不同,直接将这三个点划分为一个三角形,并删除中间那个点即可。可以发现,始终满足相邻的两个点颜色不同,所以这样做之后还是满足条件、可以继续删下去的。
当第一次出现有一种颜色只出现了一次的情况时,按上面所说,我们直接将这个点与所有其他点连接即可。
我们可以用链表来维护没有被删除的点,然后一路删下去即可。因为每种颜色至少出现一次,而且一直都不会出现两个点颜色相同的情况,所以在出现有一种颜色只出现一次的情况之前,总会出现相邻三个点颜色两两不同,总能一直删下去。那么, 就不会有无解的情况。
时间复杂度为 O ( n ) O(n) O(n)。
code
#include<bits/stdc++.h>
using namespace std;
const int N=1000000;
int n,ct[3],v[N+5],l[N+5],r[N+5],z[N+5];
char s[N+5];
struct node{
int x,y;
};
vector<node>ans;
void del(int i){
z[i]=1;
r[l[i]]=r[i];l[r[i]]=l[i];
}
void solve(){
int bz=0;
for(int i=1;i<=n;i++){
if(!z[i]&&ct[v[i]]==1){
bz=i;break;
}
}
for(int i=1;i<=n;i++){
if(!z[i]&&i!=bz&&l[i]!=bz&&r[i]!=bz){
ans.push_back((node){bz,i});
}
}
for(int i=0;i<ans.size();i++){
printf("%d %d\n",ans[i].x,ans[i].y);
}
}
int main()
{
// freopen("polygon.in","r",stdin);
// freopen("polygon.out","w",stdout);
scanf("%d",&n);
scanf("%s",s+1);
for(int i=1;i<=n;i++){
if(s[i]=='R') v[i]=0;
else if(s[i]=='G') v[i]=1;
else v[i]=2;
++ct[v[i]];
}
for(int i=1;i<=n;i++){
l[i]=i-1;r[i]=i+1;
}
l[1]=n;r[n]=1;
if(ct[0]==1||ct[1]==1||ct[2]==1){
solve();return 0;
}
for(int i=1;;i=r[i]){
while(v[l[l[i]]]+v[l[i]]+v[i]==3){
--ct[v[l[i]]];
ans.push_back((node){l[l[i]],i});
del(l[i]);
if(ct[0]==1||ct[1]==1||ct[2]==1){
solve();return 0;
}
}
}
return 0;
}