10.9 做題筆記
T1
非常水,處理處前驅後繼即可。
程式碼:
#include<bits/stdc++.h> #define dd double #define ld long double #define ll long long #define uint unsigned int #define ull unsigned long long #define N 500010 #define M number using namespace std; const int INF=0x3f3f3f3f; template<typename T> inline void read(T &x) { x=0; int f=1; char c=getchar(); for(;!isdigit(c);c=getchar()) if(c == '-') f=-f; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; x*=f; } template<typename T> inline T Min(T a,T b){return a<b?a:b;} int t,n,l[N],r[N]; ll ans; char s[N]; int main(){ // freopen("my.in","r",stdin); // freopen("my.out","w",stdout); read(t); for(int i=1;i<=t;i++){ ans=0; read(n);scanf("%s",s+1); for(int i=0;i<=n+1;i++) l[i]=r[i]=INF; for(int i=1;i<=n;i++){ if(s[i]=='1') l[i]=0; else l[i]=l[i-1]+1; } for(int i=n;i>=1;i--){ if(s[i]=='1') r[i]=0; else r[i]=r[i+1]+1; } for(int i=1;i<=n;i++){ ans+=1ll*Min(l[i],r[i]); // printf("i:%d l:%d r:%d\n",i,l[i],r[i]); } printf("Case #%d: %lld",i,ans);if(i!=t) puts(""); } }
T2
這個構造比較巧妙,用到了二進位制下的構造,沒做出來說明我還是對進位制下構造不夠熟悉。
容易發現最長鏈不會超過 \(60\),所以可以按照以前題目的套路來做,不過不同的是不能再用拓撲序了。
考慮二進位制,設 \(f(x)\) 為 \(x\) 最高二進位制位。
對於任意一條極長鏈,我們發現相鄰的兩個數的最高二進位制位必然不相同,所以我們可以把最高二進位制位當做拓撲序,然後按照 \(4\) 個分,\(16\) 個分,分別分成小組和大組。因為在最劣情況下( \(2\) 的冪 )這種構造仍然有效,所以這個題目就做完了。
程式碼:
#include<bits/stdc++.h> #define dd double #define ld long double #define ll long long #define uint unsigned int #define ull unsigned long long #define N 1010 #define M number using namespace std; const int INF=0x3f3f3f3f; template<typename T> inline void read(T &x) { x=0; int f=1; char c=getchar(); for(;!isdigit(c);c=getchar()) if(c == '-') f=-f; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; x*=f; } int f[N],n,ans[N][N]; ll x[N]; inline int F(ll x){ if(!x) return 1; int cnt=0;while(x){x>>=1;cnt++;}return cnt; } int main(){ read(n); for(int i=1;i<=n;i++) read(x[i]); for(int i=1;i<=n;i++) f[i]=F(x[i]); for(int i=1;i<=n;i++){ for(int j=i+1;j<=n;j++){ if(f[i]/4==f[j]/4) ans[i][j]=1; else if(f[i]/16==f[j]/16) ans[i][j]=2; else ans[i][j]=3; } } for(int i=1;i<=n-1;i++){ for(int j=1;j<=i;j++){ printf("%d ",ans[j][i+1]); } puts(""); } return 0; }
T3
這個題就差一點就能想出來。做題還是太少。
首先,不難發現的是如果不連通一定不行。
否則,最終狀態是確定的,我們可以得到最終狀態。
那麼我們考慮從最終狀態如何到達初始狀態。
不難發現,這就是一個最長公共子串問題。
注意是環,且翻轉同構。
因為兩邊都是 \(n\) 的階乘,所以可以轉化成最長上升子序列。
程式碼:
#include<bits/stdc++.h> #define dd double #define ld long double #define ll long long #define uint unsigned int #define ull unsigned long long #define N 1010 #define M number using namespace std; const int INF=0x3f3f3f3f; template<typename T> inline void read(T &x) { x=0; int f=1; char c=getchar(); for(;!isdigit(c);c=getchar()) if(c == '-') f=-f; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; x*=f; } template<typename T> inline T Max(T a,T b){return a<b?b:a;} template<typename T> inline T Min(T a,T b){return a<b?a:b;} struct edge{ int to,next; inline void Init(int to_,int ne_){ to=to_;next=ne_; } }li[N<<1]; int head[N],tail; inline void Add(int from,int to){ li[++tail].Init(to,head[from]); head[from]=tail; } int n,b[N],bt,c[2][N],a[N]; bool vis[N]; inline void Dfs(int k){ vis[k]=1;b[++bt]=k; for(int x=head[k];x;x=li[x].next){ int to=li[x].to; if(vis[to]) continue; Dfs(to); } } int f[N],rk[N],ans=INF; //返回 a 和 c[id] 的 LIS inline int GetLIS(int id){ int len=1; for(int i=1;i<=n;i++) a[i]=b[i]; for(int i=1;i<=n;i++) rk[c[id][i]]=i; for(int i=1;i<=n;i++) a[i]=rk[a[i]]; f[len]=a[1]; for(int i=2;i<=n;i++){ if(a[i]>f[len]) f[++len]=a[i]; else{ int w=lower_bound(f+1,f+len+1,a[i])-f; f[w]=a[i]; } } return len; } inline void Turn(){ int now=b[1]; for(int i=2;i<=n;i++) b[i-1]=b[i]; b[n]=now; } inline void Clear(){ ans=INF;bt=0; for(int i=1;i<=n;i++) head[i]=0; for(int i=1;i<=n;i++) vis[i]=0; tail=0; } int main(){ // freopen("my.in","r",stdin); // freopen("my.out","w",stdout); while(scanf("%d",&n)!=EOF&&n){ for(int i=1;i<=n;i++){ int a,b;read(a);read(b); Add(i,a);Add(i,b); } for(int i=1;i<=n;i++){ c[0][i]=i;c[1][i]=n-i+1; } Dfs(1); if(bt!=n){ puts("Not solvable.");Clear(); continue; } puts("Knot solvable."); for(int i=1;i<=n;i++){ int now1=GetLIS(0); int now2=GetLIS(1); ans=Min(ans,Min(n-now1,n-now2)); Turn(); } printf("%d\n",ans); Clear(); } return 0; }
T4
不難想到的是我們列舉兩條邊,同時維護 \(size\)。能夠拿到 \(50\) 分。不過這個不能再繼續優化了。
我們考慮另一種做法,也就是說我們改為列舉點,列舉某個點就相當於斷開其與父親的連邊。
設 \(s(x)\) 表示節點 \(x\) 的子樹大小,這裡我們預設以 \(1\) 為根。
如果 \(y\) 為 \(x\) 的祖先,那麼三個連通塊大小就是 \(s(1)-s(y),s(y)-s(x),s(x)\)。
否則,三個連通塊大小就是 \(s(1)-s(x)-s(y),s(x),s(y)\)。
我們列舉 \(y\),考慮尋找 \(x\)。通過計算得知(就是把帶絕對值的式子列出來然後劃到數軸上)對於祖先關係,\(s(x)\) 應該是要最接近 \(\frac{s(1)+s(x)}{2}\),而下面這個是 \(\frac{s(1)-s(x)}{2}\)。
這其實我們維護所有可行 \(x\) 的集合。我們設法維護這個集合並放在 \(set\) 裡面,然後在裡面二分查詢就可以把複雜度降至 \(O(n\log n)\)。
我們關注一下 dfs 過程,設 \(A\) 集合表示已經遍歷的還未回溯的點,\(B\) 集合表示已經遍歷且已經回溯的點。
對於一個 \(k\),我們在回溯到 \(k\) 的時候在 \(A\) 裡找對應 \(x\)。這個是更新祖先關係的答案。
對於一個 \(k\),我們在遍歷到 \(k\) 的時候在 \(B\) 裡找對應 \(x\)。這個是更新並列關係的答案。
程式碼:
#include<bits/stdc++.h>
#define dd double
#define ld long double
#define ll long long
#define uint unsigned int
#define ull unsigned long long
#define N 200010
#define M number
using namespace std;
const int INF=0x3f3f3f3f;
template<typename T> inline void read(T &x) {
x=0; int f=1;
char c=getchar();
for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
for(;isdigit(c);c=getchar()) x=x*10+c-'0';
x*=f;
}
template<typename T> inline T Min(T a,T b){return a<b?a:b;}
template<typename T> inline T Max(T a,T b){return a<b?b:a;}
struct edge{
int from,to,next;
inline void Init(int fr_,int to_,int ne_){
from=fr_;to=to_;next=ne_;
}
}li[N];
int head[N],tail;
inline void Add(int from,int to){
li[++tail].Init(from,to,head[from]);
head[from]=tail;
}
int Siz[N],flag=-1,n;
int none;
inline void dfs(int k,int fa){
Siz[k]=1;
for(int x=head[k];x;x=li[x].next){
if(flag==x||x==flag+1) continue;
int to=li[x].to;assert(to!=none);
if(to==fa) continue;
dfs(to,k);Siz[k]+=Siz[to];
}
}
int ans1=INF,ans2=-INF,ans=INF;
inline void Dp(int rt,int k,int fa){
for(int x=head[k];x;x=li[x].next){
if(x==flag||x==flag+1) continue;
int to=li[x].to;
if(to==fa) continue;
int now1=Siz[rt]-Siz[to],now2=Siz[to];
if(abs(now1-now2)<abs(ans1-ans2)){
ans1=now1;ans2=now2;
}
Dp(rt,to,k);
}
}
inline void Update(int val){
ans=Min(ans,Max(ans1,Max(ans2,val))-Min(ans1,Min(ans2,val)));
}
inline void Solve(){
for(int i=1;i<=tail;i+=2){
flag=i;
none=li[i].to;dfs(li[i].from,-1);
none=li[i].from;dfs(li[i].to,-1);
ans1=INF;ans2=-INF;
Dp(li[i].from,li[i].from,-1);
Update(Siz[li[i].to]);
ans1=INF;ans2=-INF;
Dp(li[i].to,li[i].to,-1);
Update(Siz[li[i].from]);
}
}
int main(){
// freopen("my.in","r",stdin);
// freopen("my.out","w",stdout);
read(n);
for(int i=1;i<=n-1;i++){
int from,to;read(from);read(to);
Add(from,to);Add(to,from);
}
Solve();
printf("%d\n",ans);
return 0;
}