• 「LOJ #6500」「雅礼集训 2018 Day2」操作


    description

    LOJ 6500

    solution

    根据常有套路,容易想到将区间差分转化为异或数组上的单点修改,即令\(b_i=a_i \ xor\ a_{i-1}\)
    那么将\([l,l+k-1]\)取反,就相当于将\(b[l]\)\(b[l+k]\)取反,若\(b[l]\)\(b[l+k]\)都是1,等于是二者消掉了
    于是发现一次操作只会对\(mod k\)余数相同的位置造成影响,并且每次操作只能消去两个1,
    故区间\([l,r]\)\(b\)数组能全部变成0当且仅当这段区间内的所有位置按\(mod k\)的余数分组后,每组中都有偶数个1
    处理这样的情况有一个常见套路,那就是给\(mod k\)的每个余数分配一个随机数哈希值,那么一段区间中若\(mod k=r\)的位置中有偶数个1,那么异或起来就会变成0
    故区间\([l,r]\)\(b\)数组能全部变成0当且仅当这段区间内的异或和为0

    考虑如何求操作次数,容易想到将最优方案是对于每个剩余系,将相邻的2个1配对
    于是对于一个\(mod k=r\)的剩余系,设剩余系内所有位置从小到大分别为\(a_1,a_2,⋯,a_{2k−1},a_{2k}\),那么答案就是\(\frac{(a_2−a_1)+(a_4−a_3)+⋯+(a_{2k}−a_{2k−1})}{k}\)
    我们可以预处理这个式子的前缀和,因为从左至右依次处理时,每一个剩余系内从右至左的第奇数个位置有正的贡献,第偶数个有负的贡献,于是新加入一个\(i\)就会导致之前的贡献全部取反再加上\(i\)的贡献

    于是再特殊处理一下边界就行了

    code

    #include<bits/stdc++.h>
    using namespace std;
    typedef unsigned long long ull;
    const int N=2e6+10;
    int n,k,m,a[N],dis[N],d[N],L[N],R[N];
    ull hsh[N],sum[N];
    char s[N];
    int main(){
    	scanf("%d%d%d",&n,&k,&m);
    	scanf("%s",s+1);
    	for(int i=0;i<k;++i) hsh[i]=rand()*rand();
    	for(int i=1;i<=n;++i){
    		a[i]=s[i]-'0';
    		sum[i]=sum[i-1];dis[i]=dis[i-1];
    		if(a[i]^a[i-1]){
    			sum[i]^=hsh[i%k];
    			dis[i]+=-(d[i%k]<<1)+i;
    			d[i%k]=i-d[i%k];
    		}
    		L[i]=d[i%k];
    		R[i]=d[(i+1)%k];
    	}
    	for(int i=1,l,r;i<=m;++i){
    		scanf("%d%d",&l,&r);
    		ull hsht=sum[l]^sum[r]^(a[l]*hsh[l%k])^(a[r]*hsh[(r+1)%k]);
    		if(hsht!=0) puts("-1");
    		else{
    			int ret=dis[r]-dis[l];
    			if(a[l]==1) ret-=l-(L[l]<<1);
    			if(a[r]==1) ret+=r+1-(R[r]<<1);
    			printf("%d\n",ret/k);
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    【微积分】 02
    【微积分】 01
    【线性代数】 09
    云南国庆八日游策划书
    Kubectl工具常用命令
    Linux 常用命令缩写及对应的
    kubectl工具的windows安装方法
    Intellij IDEA工具的常用快捷键
    如何理解docker镜像build中的上下文
    【转】在服务器上排除问题的头五分钟&常用命令
  • 原文地址:https://www.cnblogs.com/tqxboomzero/p/13905668.html
Copyright © 2020-2023  润新知