看着感觉很吓人 毕竟是 A G C AGC AGC的 E E E题 ,但是其实只要给一点提示这题还是很好着手的。
也仅仅是看着很好着手罢了。
看了一下官方题解,感觉比翻译的清晰多了。
直接讲做法了。数据删除 这道题的状态设计非常巧妙,设 ( k , s , f , m a s k ) (k,s,f,mask) (k,s,f,mask)表示一个子问题,包含这样的信息:
1.1 1.1 1.1 k k k表示只考虑末尾 k k k位,也就是后 k k k位的一个截断
1.2 1.2 1.2 s s s表示初始状态,其中 s = 0 s=0 s=0表示初始状态为 S S S, s = 1 s=1 s=1表示初始状态全为 0 0 0; t t t表示目标状态,其中 t = 0 t=0 t=0表示目标状态为 T T T,并且过程中不产生 k − 1 → k k-1\to k k−1→k的进位,也就是说进位操作只影响后 k k k位; t = 1 t=1 t=1表示有且仅发生一次恰好从 k − 1 → k k-1\to k k−1→k的进位,这句话也隐含了一些信息,我们后面再来说。
1.3 1.3 1.3 m a s k mask mask表示每个操作的次数的奇偶性。
定义 c o s t ( k , s , f , m a s k ) cost(k,s,f,mask) cost(k,s,f,mask)表示这个子问题的最小代价。
然后看转移。我们只考虑相邻层的转移,换句话说,我们只需要在让 [ 0 , k − 1 ] [0,k-1] [0,k−1]位匹配的基础上让 k k k位匹配就好了。并且我们还有限制,进位有影响只能发生在 k − 1 → k k-1\to k k−1→k。这个转移刚开始令我非常费解,因为它的思路有一点奇怪:用小规模的问题去强行拼凑出大规模问题的解。
1.1 1.1 1.1 如果没有产生 k − 1 → k k-1\to k k−1→k的进位,那么我们根据 m a s k mask mask就能知道第 k k k位是否匹配,如果匹配那么 c o s t ( k , s , 0 , m a s k ) → c o s t ( k + 1 , s , 0 , m a s k ) cost(k,s,0,mask)\to cost(k+1,s,0,mask) cost(k,s,0,mask)→cost(k+1,s,0,mask)。这里我要多解释一句,比如说 s = 1 s=1 s=1,那么就让 S k ← 0 S_k\gets 0 Sk←0,再比较经过 m a s k mask mask掩码后是否与 T k T_k Tk匹配。
1.2 1.2 1.2 如果产生了 k − 1 → k k-1\to k k−1→k的进位,那么 一定 是经过了形如 ( k , s , 1 , m 0 ) (k,s,1,m_0) (k,s,1,m0), ( k , 1 , 1 , m 1 ) , . . . , ( k , 1 , f , m i ) (k,1,1,m_1),...,(k,1,f,m_i) (k,1,1,m1),...,(k,1,f,mi)的操作序列,最后转移到 ( k + 1 , s , f , ⨁ m i ) (k+1,s,f,\bigoplus m_i) (k+1,s,f,⨁mi)。换句话说,先把 S k ← S k / 0 S_k\gets S_k/0 Sk←Sk/0,然后进行若干次 k − 1 → k k-1\to k k−1→k的进位,但是都只影响了 [ 0 , k ] [0,k] [0,k]位;因为 [ 0 , k − 1 ] [0,k-1] [0,k−1]位已经被还原了,就只要考虑 k k k位最后恰好匹配上即可。注意过程中要检验 k k k位的取值。这里还是要多解释一下,如果 f = 0 f=0 f=0那么限制要宽松一些,就是说最后那一段操作没有进位,就检查最后 k k k位是否匹配就好了;如果 f = 1 f=1 f=1那么最后一个操作是作了 k − 1 → k k-1\to k k−1→k的进位的,这个时候应该保证 k k k位是 1 1 1,才能继续进位,这样和状态才合得上。最后要说的就是除了最后一次操作,过程中每一次 k − 1 → k k-1\to k k−1→k进位的时候都要保证 k k k位是 0 0 0才行。
怎么转移不用我多说吧。直接在图上做就好了,因为边权都是非负的。
但是我们还是没有解释为什么只转移到这 4 4 4个特殊状态是对的。这似乎可以用一句万能的套话来说明:最优解一定包含在内。想了想发现确实如此。算了就当我没证吧。乐
总结一下,这道题还是花费了我相当长的时间,感觉过程中一些处理的细节想了很久才想清楚,总体而言这道题比看起来要棘手一些。
时间复杂度 O ( B 4 n ) O(B4^n) O(B4n)。
代码写起来比较不适。细节贼多,建议写之前把细节想清楚。
#include
#define ll long long
#define fi first
#define se second
#define pb push_back
#define db double
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
int n,vis[1<<8][2][2];
ll S,T,A,nums[8],numsets[1<<8],costs[8],now[1<<8][2][2],now2[1<<8][2][2],nxt[1<<8][2][2];
void chmin(ll &x,ll y){x=min(x,y);}
ll get(int s,int j){
if(j==0)return S^numsets[s];
return numsets[s];
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>n>>S>>T>>A;
for(int i=0;i<n;i++)cin>>nums[i]>>costs[i];
for(int s=0;s<1<<n;s++){
for(int i=0;i<n;i++){
if(s>>i&1)numsets[s]^=nums[i];
}
}
for(int s=0;s<1<<n;s++){
for(int i=0;i<2;i++){
for(int j=0;j<2;j++){
if(j==1)now[s][i][j]=A;
for(int k=0;k<n;k++){
if(s>>k&1)now[s][i][j]+=costs[k];
}
}
}
}
for(int i=0;i<=40;i++){
memset(vis,0,sizeof vis);
//fixed
memset(nxt,0x3f,sizeof nxt);
//fixed
memset(now2,0x3f,sizeof now2);
for(int s=0;s<1<<n;s++){
for(int j=0;j<2;j++){
if((get(s,j)>>i&1)==(T>>i&1)){
chmin(nxt[s][j][0],now[s][j][0]);
}
}
}
for(int s=0;s<1<<n;s++){
for(int j=0;j<2;j++){
if((get(s,j)>>i&1)==0){
chmin(now2[s][j][1],now[s][j][1]);
}
else{
chmin(nxt[s][j][1],now[s][j][1]);
}
}
}
//fixed
while(1){
int ts=-1,tj=0;
for(int s=0;s<1<<n;s++){
for(int j=0;j<2;j++){
if(!vis[s][j][1]&&now2[s][j][1]!=inf&&(ts==-1||now2[s][j][1]<now2[ts][tj][1])){
ts=s,tj=j;
}
}
}
if(ts==-1)break;
assert(vis[ts][tj][1]==0);
vis[ts][tj][1]=1;
for(int s2=0;s2<1<<n;s2++){
for(int j=0;j<2;j++){
if(j==0){
if(((numsets[s2]>>i&1)^1)==(T>>i&1)){
chmin(nxt[ts^s2][tj][0],now2[ts][tj][1]+now[s2][1][0]);
}
}
else{
if(((numsets[s2]>>i&1)^1)==0){
chmin(now2[ts^s2][tj][j],now2[ts][tj][1]+now[s2][1][1]);
}
else{
chmin(nxt[ts^s2][tj][j],now2[ts][tj][1]+now[s2][1][1]);
}
}
}
}
}
memcpy(now,nxt,sizeof nxt);
}
ll res=inf;
for(int s=0;s<1<<n;s++){
res=min(res,now[s][0][0]);
}
if(res==inf)cout<<-1;
else cout<<res;
}