1. 程式人生 > >2018.09.29【Atcoder Regular Contest 103】E

2018.09.29【Atcoder Regular Contest 103】E

傳送門

解析:

短程式碼神題。如果把我打比賽習慣的標頭檔案去掉,真的是非常短的程式碼量了。
然而這道題並不是如此簡單好想。

思路:

首先一棵樹必然有葉子節點,也就是說s[1]s[1]必須為11,因為我們必然會有一刀能夠切斷該葉子節點的與其父親的辦法。

s[n]s[n]不能為1,因為無論如何切,剩下的聯通塊都必然會減少節點個數。

對應的,我們切出了一個大小為ii的聯通塊,那麼另一個聯通塊大小就是nin-i,所以必須滿足s[i]==s[ni]s[i]==s[n-i]

不然,必然有解。

懶得證明了,直接構造出解就能說明有解了。

考慮一條鏈,鏈上每個節點要麼是菊花樹,要麼是一個單獨節點。(就像斐波那契堆一樣)

我們可以通過調整菊花樹的大小來滿足題意。

具體實現就是我們記錄一個nownow節點,然後在上面連邊,如果要求有之前菊花樹大小之和的塊,我們就將nownow節點向後移動至剛才的連上的點,繼續處理。

這樣構造出來的樹,就是一個以菊花樹構成節點的鏈。

這樣為什麼是對的?

我們考慮砍一條邊。
這條邊要麼是菊花樹上的邊,砍了之後會剩下一個大小為11的聯通塊,為其葉子節點,和另一個大小為n1n-1的聯通塊。

不然這條邊就是這條鏈上的邊,砍了之後會剩下左右兩個菊花樹大小的聯通塊。

而我們構造菊花樹的方法就已經決定了這樣必然會構造出大小滿足題意的聯通塊。

程式碼:

#include<bits/stdc++.h>
using namespace std; #define ll long long #define re register #define gc getchar #define pc putchar #define cs const inline int getint(){ re int num=0; re char c; re bool f=0; while(!isdigit(c=gc()))f^=c=='-';num=c^48; while(isdigit(c=gc()))num=(num<<1)+(num<<3)+(c^48); return num;
} inline void outint(int a){ static char ch[23]; if(a==0)pc('0'); while(a)ch[++ch[0]]=a-a/10*10,a/=10; while(ch[0])pc(ch[ch[0]--]^48); } char s[100005]; int n; signed main(){ cin>>(s+1); n=strlen(s+1); if(s[1]=='0'||s[n]=='1')return puts("-1"),0; for(int re i=1;i<n;++i)if(s[i]^s[n-i])return puts("-1"),0; int now=1; for(int re i=2;i<=n;++i){ outint(now),pc(' '),outint(i),pc('\n'); if(s[i-1]=='1')now=i; } return 0; }