https://www.luogu.com.cn/problem/P4310
第2題 最長 檢視測評資料資訊
給定一個長度是n的序列a[1,2,...n],現在你需要構造一個長度為m的陣列b[1,2,...m],需要滿足如下條件:
(1)b是a的子序列
(2)b[i]&b[i-1]!=0 (2<=i<=m)
問構造出b後,m的最大值是多少。
輸入格式
輸入檔案共2行。第一行包括一個整數n。
第二行包括n個整數,第i個整數表示a[i]。
1<=n<=1e5,1<=a[i]<=1e9
輸出格式
一個整數,表示m的最大值
輸入/輸出例子1
輸入:
3
1 2 3
輸出:
2
樣例解釋
無
位運算都考慮拆位!一位一位看!
做法一:
f[i]:以第i個數結尾滿足的最長序列
記錄lst陣列,第i進位制位是1屬於的最近的a陣列下標
https://www.luogu.com.cn/article/ncpi87q5
做法二:
逐步考慮每一位
對於每一位,二進位制位數上是1的可以建一個圖,例如
a的第3個進位制位是1
b的第3個進位制位是1
那麼我們建 a->b 的有向邊
圖保證無環,我們找圖的最長鏈就是答案(拓撲)
#include <bits/stdc++.h> using namespace std; const int N=1e5+5; int n, b[N], d[N], ans=0, dis[N]; vector<int> a[N]; queue<int> q; void topsort() { q.push(0); while (!q.empty()) { int u=q.front(); q.pop(); for (int i=0; i<a[u].size(); i++) { int v=a[u][i]; if (dis[v]<dis[u]+1) dis[v]=dis[u]+1; ans=max(ans, dis[v]); d[v]--; if (!d[v]) q.push(v); } } } int main() { scanf("%d", &n); for (int i=1; i<=n; i++) scanf("%d", &b[i]); for (int k=0; k<=32; k++) { int last=0; for (int i=1; i<=n; i++) if (b[i]&(1<<k)) { a[last].push_back(i); d[i]++; last=i; } } topsort(); printf("%d", ans); return 0; }
做法三:
覆蓋
https://www.luogu.com.cn/article/w99xswjw
#include <bits/stdc++.h> using namespace std; const int N=50; int n, a, f[N], ans=0; int main() { scanf("%d", &n); for (int i=1; i<=n; i++) { int t=0, Max=0, cnt=0; scanf("%d", &a); t=a; while (t>0) { cnt++; if (t&1) Max=max(Max, f[cnt]); t>>=1; } cnt=0, t=a; while (t>0) { cnt++; if (t&1) f[cnt]=max(Max+1, f[cnt]); t>>=1; } } for (int i=1; i<=32; i++) ans=max(ans, f[i]); printf("%d", ans); return 0; }