【学习笔记】CF1835C Twin Clusters

首先,区间不交的限制几乎可以不考虑,所以只要任意两个左右端点不重合,且异或和相等的区间即可。

其次,可以发现异或这个东西也没啥用。问题的症结在于值域太大,因此考虑将值域拆成两个部分,即二进制的前 k k k位和后 k k k位。

我们考虑找到若干对 ( i , j ) (i,j) (i,j),使得只考虑二进制前 k k k位时,其对应的异或前缀和相同。这样,我们至少能找到 2 k + 1 2^k+1 2k+1组这样的数对(也就对应一段区间),显然存在两个数对满足其对应的二进制后 k k k位的异或和相同。

现在回过头看,因为记录的是相同的前缀异或和的最大位置,所以端点之间一定不交,因此我们的构造是合法的。

这样,我们只从 2 2 k + 1 2^{2k+1} 22k+1个区间中取出 2 k + 1 2^{k+1} 2k+1个区间就完成了构造。这也是折半枚举在构造题中的应用:估计解的范围,从而减少枚举量。

类似的题目:小蓝的构造。

但是不知道为啥这道题直接暴搜就能过,难道有什么神奇的剪枝?

其实CF这道题乱搞一下也能过

#include
#define ll long long
#define fi first
#define se second
#define pb push_back
#define inf 0x3f3f3f3f
using namespace std;
int T,K,n,maxpos[1<<18];
pair<int,int>ps[1<<18];
ll a[(1<<18)+5],sm[(1<<18)+5];
void solve(){
    cin>>K,n=1<<K+1;
    for(int i=1;i<=n;i++)cin>>a[i],sm[i]=sm[i-1]^a[i];
    for(int i=0;i<1<<K;i++)ps[i]={-1,-1},maxpos[i]=-1;
    maxpos[0]=0;
    for(int i=1;i<=n;i++){
        if(~maxpos[sm[i]>>K]){
            int j=maxpos[sm[i]>>K];
            ll x=sm[i]^sm[j];
            if(ps[x].fi!=-1){
                int a=ps[x].fi,b=ps[x].se,c=j+1,d=i;
                if(b<c)cout<<a<<" "<<b<<" "<<c<<" "<<d<<"\n";
                else cout<<min(a,c)<<" "<<max(a,c)-1<<" "<<b+1<<" "<<d<<"\n";
                return;
            }
            ps[x]={j+1,i};
        }maxpos[sm[i]>>K]=i;
    }
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    cin>>T;
    while(T--){
        solve();
    }
}

你可能感兴趣的:(构造题,学习,笔记)