題解 星空
阿新 • • 發佈:2021-06-30
這題什麼神仙轉化……
24pts狀壓賊好打考場上腦子抽了一直想特判
如果要在狀壓時同時找到兩個1轉移,第一層可以不列舉,而用第一個1代替,因為沒有第一個1的狀態也會被列舉到
「LATEX待補充」
想不到……就先順一遍題解思路吧
首先n有點大,而這裡需要區間修改
套個資料結構能維護修改
但是這只是不斷用區間修改嘗試統一整個區間的值,考慮差分
類比藍書差分例題增減序列 ,只不過那道是求統一差分陣列的最小步數及方案數,這裡是求統一差分陣列的最小步數而已
然後轉化成了一個處理差分序列的問題
發現這裡\(k\)很小,差分序列裡只有不超過16個1,令其為\(cnt\)
再看所謂「翻轉」操作,在這裡即為選固定間距的點進行取反
取反兩個1相當於用一次操作將它們消掉
取反一個0和一個1相當於移動這個1
最後要把序列消成全0
\(2*k\leqslant16\)這個範圍適合暴力,考慮預處理通過移動消掉i,j的最少步數
這裡可以用最短路
然後列舉消點的過程可以狀壓
注意狀壓的時候可以用到一開始說的優化
如果要在狀壓時同時找到兩個1轉移,第一層可以不列舉,而用第一個1代替,因為沒有第一個1的狀態早晚被列舉到,就與這裡的轉移產生了重複
狀壓時間複雜度可以從\(O(k^2*2^{cnt})\)優化成\(O(k*2^{cnt})\)
Code:
#include <bits/stdc++.h> using namespace std; #define INF 0x3f3f3f3f #define N 40010 #define ll long long #define ld long double #define usd unsigned #define ull unsigned long long //#define int long long #define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++) char buf[1<<21], *p1=buf, *p2=buf; inline int read() { int ans=0, f=1; char c=getchar(); while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();} while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();} return ans*f; } int n, k, m; namespace force{ int dp[1<<16], s, cnt; bool vis[1<<16]; queue<int> q; int len[N], b[N]; void solve() { memset(dp, 127, sizeof(dp)); s=(1<<n)-1; for (int i=1; i<=k; ++i) s^=(1<<(read()-1)); for (int i=1; i<=m; ++i) {len[i]=read(); b[i]=(1<<len[i])-1;} q.push(s); dp[s]=0; while (!q.empty()) { //++cnt; s=q.front(); q.pop(); vis[s]=0; for (int i=1,to; i<=m; ++i) for (int j=n-len[i]; j>=0; --j) { to = s^(b[i]<<j); if (dp[s]+1<dp[to]) { dp[to]=dp[s]+1; if (!vis[to]) q.push(to); } } } printf("%d\n", dp[(1<<n)-1]); //cout<<"cnt: "<<cnt<<endl; //cout<<"2^n: "<<(1<<n)<<endl; exit(0); } } namespace task{ int a[20], len[70], pos[20], cnt, cnt2; int dis[20][N], dp[1<<16]; queue<int> q; bool vis[1<<16], point[N]; void bfs(int s, int dis[]) { //cout<<"bfs "<<s<<endl; int cnt2=0; dis[s]=0; q.push(s); while (!q.empty()) { s=q.front(); q.pop(); vis[s]=0; if (point[s] && ++cnt2>=cnt) {while (!q.empty()) {vis[q.front()]=0; q.pop();} return ;} for (int i=1,t; i<=m; ++i) { //cout<<"len: "<<len[i]<<endl; if ((t=s-len[i])>0 && dis[t]>dis[s]+1) { dis[t]=dis[s]+1; if (!vis[t]) q.push(t); } if ((t=s+len[i])<=n && dis[t]>dis[s]+1) { dis[t]=dis[s]+1; if (!vis[t]) q.push(t); } } } //cout<<"dis: "; for (int i=1; i<=n; ++i) cout<<dis[i]<<' '; cout<<endl; } void solve() { ++n; memset(dis, 127, sizeof(dis)); memset(dp, 127, sizeof(dp)); for (int i=1; i<=k; ++i) a[i]=read(); for (int i=1; i<=k; ++i) { if (i==1 || (i!=1&&a[i-1]!=a[i]-1)) pos[++cnt]=a[i], point[a[i]]=1; if (a[i+1]!=a[i]+1) pos[++cnt]=a[i]+1, point[a[i]+1]=1; } //cout<<"cnt: "<<cnt<<' '<<k*2<<endl; //cout<<"pos: "; for (int i=1; i<=cnt; ++i) cout<<pos[i]<<' '; cout<<endl; for (int i=1; i<=m; ++i) len[i]=read(); for (int i=1; i<=cnt; ++i) bfs(pos[i], dis[i]); int s; dp[(1<<cnt)-1]=0; q.push((1<<cnt)-1); while (!q.empty()) { s=q.front(); q.pop(); vis[s]=0; for (int i=0,t2,t3; i<cnt; ++i) if (s&(1<<i)) { for (int j=i+1; j<cnt; ++j) if (s&(1<<j)) { t2=s^(1<<i)^(1<<j); t3=dp[s]+dis[i+1][pos[j+1]]; //cout<<"disk: "; for (int k=1; k<=n; ++k) cout<<dis[i+1][k]<<' '; cout<<endl; cout<<t3<<endl; cout<<pos[j+1]<<endl; if (dp[t2]>t3) { dp[t2]=t3; if (!vis[t2]) q.push(t2); } } break; } } printf("%d\n", dp[0]); //cout<<cnt2<<' '<<(1<<cnt)<<endl; exit(0); } } signed main() { #ifdef DEBUG freopen("1.in", "r", stdin); #endif n=read(); k=read(); m=read(); task::solve(); return 0; }