一道很好的題,綜合很多知識點。
首先複習差分:
將原來的每個點a[i]轉化為b[i]=a[i]^a[i+1],(如果是求和形式就是b[i]=a[i+1]-a[i])
我們發現這樣的方便在於我們可以運用字首和的形式,求出單點值,當然,差分一般支援區間修改
單點查詢,同時我們發現異或也滿足轉化的性質,我們發現異或的區間修改,也可以化為單點修改
然後進行問題轉換:在乙個序列中按要求修改端點,問最少修改多少次區間全部為0
把原來的每個數轉化為差分形式,注意要多加乙個b[0]=b[0]^b[1],然後我們發現假設修改
a[1]-a[5]的區間,那麼在差分中實際影響的是b[0],b[5]兩個值。
同時有一條顯然的性質,差分陣列中的1是偶數個。
然後再次轉化問題:
我們已經知道每種操作的長度(實際在差分陣列中長度+1,自己思考)
那麼我們可以給數與數連邊,連邊長度為一,當兩個1相遇後,相當於反轉,變為0
那麼我們將問題轉化為圖論問題:在n個節點圖中,每個節點最多m個邊,求出兩兩最短距離,
然後兩個1相遇後全為0,求變為1的最小貢獻
然後跑最短路
最後我們發現k的值很小,可以轉為狀壓dp的形式
總的複雜度nmk+(k^2)*(2^k)
1 #include2 #include3 #include4 #includeview code5 #include6 #include7 #include
8 #include9 #include10 #include11
#define maxn 3000001
12using
namespace
std;
13int
a[maxn],sum[maxn];
14int
c[maxn];
15int
head[maxn],tot;
16struct nodee[2700000*2
];17
void add(int u,int
v)18
21int
n,k,m;
22int
vis[maxn];
23int dis[301][301
];24
intspfa_dis[maxn];
25int
the_1[maxn];
26int
belong[maxn];
27void spfa(int
root)
2849}50
}51}52
for(int i=1;i<=the_1[0];++i)
5359}60
void
init()
6173
int r=i+c[j];
74if(r<=n)
7579}80
}81for(int i=0;i<=n;++i)
8286}87
int fir=0,base[30
];88
int f[1
<<18
];89
void
dp()
9095
base[0]=1;96
for(int i=1;i<=n;++i)
97100 memset(f,0x3f3f3f,sizeof
(f));
101 f[base[the_1[0]]-1]=0
;102
for(int i=base[the_1[0]]-1;i>=0;--i)
103117
}118
}119
}120
}121 printf("
%d\n
",f[0
]);122
}123
signed main()
124132
for(int i=0;i<=n;++i)
133140
//printf("sum[%d]=%d\n",i,sum[i]);
141}
142for(int i=1;i<=m;++i)
143146
init();
147dp();
148 }
星空 差分,狀壓dp
總算不再是能用暴力卡常 隨機化水過的好t3了。說是打了兩個標籤,實際上最關鍵的是題意轉化。如果你絲毫不轉化的話也可以 1 include2 using namespace std 3int dp 2 1048577 b 65 k,n,m,x 9 f 1 mx 4int main 15 printf ...
題解 星空 狀壓DP
這道題思維難度非常高,有很多處理的小技巧,並且 也有很多細節 這道題是一種序列的區間操作,我們都知道,區間操作比較麻煩,所以我們要想辦法將區間操作轉換成單點修改 這時,我們想到了差分,假如我們對乙個序列進行操作,這時乙個序列裡的相對狀態不會變只有兩端改變,換句話說就是這個序列的差分並不會發生改變,只...
NOIP模擬 乘積(狀壓DP)
題意 選擇不超過 k 個 n以內的正整數撐起來,使得乘積是乙個無平方因子數 不能夠被任意乙個質數的平方整除 一共有多少種取法?n 500,k 500 題解 首先,很容易想到狀壓,壓縮當前已經選過某質數的狀態。對於質數個數小於 20 可以直接過,但是問題是現在 n 以內的質數個數很多,無法壓縮。考慮狀...