【学习笔记】[AGC061E] Increment or XOR

看着感觉很吓人 毕竟是 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 k1k的进位,也就是说进位操作只影响后 k k k位; t = 1 t=1 t=1表示有且仅发生一次恰好从 k − 1 → k k-1\to k k1k的进位,这句话也隐含了一些信息,我们后面再来说。
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,k1]位匹配的基础上让 k k k位匹配就好了。并且我们还有限制,进位有影响只能发生在 k − 1 → k k-1\to k k1k。这个转移刚开始令我非常费解,因为它的思路有一点奇怪:用小规模的问题去强行拼凑出大规模问题的解。

1.1 1.1 1.1 如果没有产生 k − 1 → k k-1\to k k1k的进位,那么我们根据 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 Sk0,再比较经过 m a s k mask mask掩码后是否与 T k T_k Tk匹配。

1.2 1.2 1.2 如果产生了 k − 1 → k k-1\to k k1k的进位,那么 一定 是经过了形如 ( 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 SkSk/0,然后进行若干次 k − 1 → k k-1\to k k1k的进位,但是都只影响了 [ 0 , k ] [0,k] [0,k]位;因为 [ 0 , k − 1 ] [0,k-1] [0,k1]位已经被还原了,就只要考虑 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 k1k的进位的,这个时候应该保证 k k k位是 1 1 1,才能继续进位,这样和状态才合得上。最后要说的就是除了最后一次操作,过程中每一次 k − 1 → k k-1\to k k1k进位的时候都要保证 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;
}

你可能感兴趣的:(学习,笔记,算法)