QZS8.17

T1

啊?60改成20???啊啊啊

60

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int N=1000005;
const int mod=1000000007;
int n,p[N];
long long sum[N],ans,dp[N];
int main() {
    scanf("%d",&n);
    for(int i=1;i<=n;i++)  
        scanf("%d",&p[i]);
    dp[1]=2;sum[1]=2;
    for(int i=2;i<=n;i++) {
        dp[i]=(2+sum[i-1]-sum[p[i]-1])%mod;
        sum[i]=(dp[i]+sum[i-1])%mod;
    }
    for(int i=1;i<=n;i++)
        ans=(ans+dp[i])%mod;
    printf("%lld\n",ans);
    return 0;
}

想假了???

正解

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int N=1000005;
const int mod=1000000007;
int n,p[N];
long long ans,dp[N];
int main() {
    scanf("%d",&n);
    for(int i=1;i<=n;i++)  
        scanf("%d",&p[i]);
    for(int i=1;i<=n;i++)
        dp[i+1]=(dp[i]+dp[i]-dp[p[i]]+2)%mod;
    for(int i=1;i<=n;i++)
        ans=(ans+dp[i])%mod;
    printf("%lld\n",(dp[n+1]+mod)%mod);
    return 0;
}

T2

赤裸裸的暴力40分

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const long long mod=4294967296;
int n,m;
char op,z,ch,s[50005];
long long query(int l,int r) {
    long long res=0;
    for(int i=l;i<=r;i++) {
        if(s[i]=='0') {
            for(int j=i+1;j<=r;j++) {
                if(s[j]=='w') {
                    for(int k=j+1;k<=r;k++)
                        if(s[k]=='0') res++;
                }
            }
        }
    }
    return res;
}
int main() {
    scanf("%d%d",&n,&m);
    scanf("%s",s+1);
    for(int i=1,x,y;i<=m;i++) {
        cin>>op;scanf("%d",&x);
        if(op=='A') {
            cin>>z;
            s[x]=z;
        } else if(op=='B') {
            scanf("%d",&y);cin>>z;
            for(int j=x;j<=y;j++)
                s[j]=z;
        } else {
            scanf("%d",&y);
            long long ans=0;
            int L=0;
            for(int j=x;j<=y;j++) {
                if(s[j]=='(') {
                	L=j;
                	for(int k=j+1;k<=y;k++) 
	                    if(s[k]==')') 
	                        ans+=query(L,k); 
				}
            }
            printf("%lld\n",ans%mod);
        }
    }
    return 0;
}

线段树维护字符串+合并

思路大致对的,but代码只有60分

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define ls (p<<1)
#define rs (p<<1|1)
const long long mod=4294967296;
#define int long long
const int N=200005;
int n,m;
char op,z,ch,s[N];
int root,len[N];
char tag[N];
int f[N][5][5];
int ans[5][5],tmp[5][5];
void merge(int g1[5][5],int g2[5][5],int g[5][5]) {
    for(int i=0;i<=4;i++)
        for(int j=i;j<=4;j++) {
            g[i][j]=(g1[i][j]+g2[i][j])%mod;
            for(int k=i;k<j;k++)
                g[i][j]=(g[i][j]+g1[i][k]*g2[k+1][j]%mod)%mod;
        }
}
void build(int l,int r,int p) {
    len[p]=r-l+1;
    if(l==r) {
        if(s[l]=='(') f[p][0][0]=1;
        else if(s[l]=='0') f[p][1][1]=f[p][3][3]=1;
        else if(s[l]=='w') f[p][2][2]=1;
        else if(s[l]==')') f[p][4][4]=1;
        return;
    }
    int mid=(l+r)>>1;
    build(l,mid,ls);
    build(mid+1,r,rs);
    merge(f[ls],f[rs],f[p]);
}
void pushdown(int p) {
    if(!tag[p]) return;
    tag[ls]=tag[rs]=tag[p];
    memset(f[p],0,sizeof(f[p]));
    if(tag[p]=='(') f[p][0][0]=len[p];
    else if(tag[p]=='0') f[p][1][1]=f[p][3][3]=len[p];
    else if(tag[p]=='w') f[p][2][2]=len[p];
    else if(tag[p]==')') f[p][4][4]=len[p];
    tag[p]=0;
}
void modify(int l,int r,int L,int R,int p) {
    if(L<=l&&r<=R) {
        tag[p]=z;
        pushdown(p);
        return;
    }
    pushdown(ls);pushdown(rs);
    int mid=(l+r)>>1;
    if(L<=mid) modify(l,mid,L,R,ls);
    if(R>mid) modify(mid+1,r,L,R,rs);
    merge(f[ls],f[rs],f[p]);
}
void query(int l,int r,int L,int R,int p) {
    if(L<=l&&r<=R) {
        merge(ans,f[p],tmp);
        memcpy(ans,tmp,sizeof(tmp));
        return;
    }
    pushdown(ls);pushdown(rs);
    int mid=(l+r)>>1;
    if(L<=mid) query(l,mid,L,R,ls);
    if(R>mid) query(mid+1,r,L,R,rs);   
}
signed main() {
    scanf("%d%d",&n,&m);
    scanf("%s",s+1);
    build(1,n,1);
    for(int i=1,x,y;i<=m;i++) {
        cin>>op;
        scanf("%d",&x);
        if(op=='A') {
            cin>>z;
            modify(1,n,x,x,1);
        } else if(op=='B') {
            scanf("%d",&y);cin>>z;
            modify(1,n,x,y,1);
        } else {
            scanf("%d",&y);
            memset(ans,0,sizeof(ans));
            query(1,n,x,y,1);
            printf("%lld\n",ans[0][4]%mod);
        }
    }
    return 0;
}

T3

https://www.luogu.com.cn/blog/52243/ti-xie-qi-zhi-shu-day1t3-tiramisu

首先01序列有可差分性质

运用差分(异或意义下)可以将区间取反 降到 \(O(1)\)

观察差分序列,我们暴力中每次会把距离为 k 的两个点取反。

问题转化成在这样的差分序列中,每次将距离为 k 的两个点取反,需要的最小操作次数。

举个栗子:

原序列:0100011010
询问[7, 9]
提取+前导和后导零 0 101 0
差分:01111

这里具体说一下加前导和后导零的必要

前导零比较好说,一是为了保证差分序列长度=原序列长度

二是如果提取出的序列为 101 ,我们最后的目标是把这段都变成0, 如果加的是前导1,那么这差分序列就成了010,显然与原序列不符。

后导零呢再举个栗子

提取 011 ,设k=2,显然我们取一次即可

but 他的差分序列是 (0)010 ,不加后导零是搞不出那一对的

加了之后 是 (0)010(1) 我们就可以快乐取反了

判断有解

注意到,由于每次取反的距离为k,所以只有在编号模 k 值相同的位置中,(即处于%k同余系相同的位置)1 的个数为偶数个,问题有解

可以用哈希 O(1) 判断是否有解:

对编号模 k 值相同的位置赋一个 hash 值,然后对所有 1 做前缀异或和。

因为异或的性质,某个 hash 值有偶数个,异或起来为 0。

那么提取区间异或和即可判断是否有解。

统计答案

对于询问区间[L,R] ,由上面这个(0)010(1)例子,我们可以发现其实询问的是差分数组的[L,R+1] ,

现在要计算最小操作次数,易得我们需要将编号模 k 值相同的位置中,所有的 1 相邻两两配对。

答案就是每对位置差除以 k。

我们还是对这些答案做前缀和,以便 O(1)查询。

需要注意的是,两两配对的点中,做前缀和的话,如果有偶数个点那么恰好为答案;如果有奇数个点,要考虑到两个前缀和做差形成偶数个点的情况。

刚才的前导零和后导零是我们自己把原序列那个位置强制假设为0,如果其实际位置是1 呢?这显然对答案统计是有影响的。

具体的,我们先不考虑 L , R+1

答案区间转化为 [L+1,R]

ans = sum[R]-sum[L]

然后考虑强制假设的影响,我们可以记录个nxt[i] , 表示 i 之前包括 i 在内的%k同余系下相同的位置 产生的ans,bef [i] , 表示 i 之前不包括 i 在内的%k同余系下相同的位置 产生的ans,然后就判断如果 R是1 ,则证明 R+1 一定是1,( 后导0 ^ 1 = 1),然后处理其贡献

//假设我们之前的前缀和是p3 - p2 + p1,现在我们要变成p4 - p3 + p2 - p1
// 所以应该先把p3 - p2 + p1 ①减去
// 之后需要再加上p4 - (p3 - p2 + p1) ②
// ①+②= r + 1 (p4)- 2 * p3 - p2 +p1
// 而bef数组就是存的"p3 - p2 + p1"的值
if(c[r] == '1')
    res += r + 1 - 2 * bef[r + 1];
if(c[l] == '1')
    res -= l - 2 * nex[l];

At last

代码

#include <ctime>
#include <cstdio>
#include <cctype>
#include <cstdlib>
#include <iostream>
using namespace std;
#define int long long
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
	return f*x;
}
const int N = 2e6 + 5;
int n,m,k,l,r,res;
char c[N];
int dif[N],has[N],pre[N];
int sum[N],b[N],bef[N],nxt[N];
signed main() {
    srand(time(0));
    n=read();k=read();m=read();
    scanf("%s",c+1);
    c[0]='0',c[n+1]='0';
    for(int i=1;i<=n+1;i++)//差分
        dif[i]=(c[i-1]=='1')^(c[i]=='1');
    for(int i=0;i<k;i++) //k的余数域hash值
        has[i]=rand()*rand()+rand();
    for(int i=1;i<=n+1;i++) //差分前缀和hash——一会判奇偶
        if(dif[i]) pre[i]=pre[i-1]^has[i%k];
        else pre[i]=pre[i-1];
    for(int i=1;i<=n+1;i++) {
        sum[i]=sum[i-1];//所有位置的前缀答案
        bef[i]=b[i%k];//b[]模k值相同的位置的前缀答案
        if(dif[i]) {
            sum[i]-=b[i%k];
            b[i%k]=i-b[i%k];
            sum[i]+=b[i%k];
        // 如果有偶数个点,b[i%k]值为两两距离差除以k,即答案(/k最后统一除)
        // 如果有奇数个点,b[i%k]值为 i-原b[i%k],即该位置减去之前的答案 例:pre=pos2-pos1,now=pos3-pre=pos3-pos2+pos1
        }
        nxt[i]=b[i%k];
    }
    while(m--) {
        l=read();r=read();
        if(pre[l]^pre[r]^((c[l]!='0')*has[l%k]^((c[r]!='0')*has[(r+1)%k]))) {
            puts("-1");continue;
        }
        res=sum[r]-sum[l];//暂不考虑r + 1 , l 
        // 所以应该先把p3 - p2 + p1 ①减去
        // 之后需要再加上p4 - (p3 - p2 + p1) ②
        // ①+②= r + 1 (p4)- 2 * p3 - p2 +p1
        // 而bef数组就是存的"p3 - p2 + p1"的值        
        if(c[r]=='1') 
            res+=r+1-2*bef[r+1];
        if(c[l]=='1')
            res-=l-2*nxt[l];
        printf("%lld\n",res/k);
    }
    return 0;
}
posted @ 2020-08-22 09:03  ke_xin  阅读(35)  评论(0编辑  收藏  举报