【题解】day1

A:doughnut

来源:CF407B Long PathCF407B\ Long\ PathCF407B Long Path

时间限制:2s2s2s 空间限制:256MB256 MB256MB

【题目描述】

AAA 有很多甜甜圈。有一天,她在地上画了 n+1n+1n+1 个格子,她想从第 111 个格子跳到第 n+1n+1n+1 个格子。

规则是,AAA 每到一个格子,都会放一个甜甜圈在里面。在第 iii 个格子时,如果当前甜甜圈的数量是偶数,那么她会跳到第 i+1i+1i+1 个格子;否则,她会跳到第 PiP_iPi 的格子(1≤Pi≤i)(1\le P_i\le i)1Pii 。当到达第 n+1n+1n+1 个格子,结束。

AAA 想知道,需要跳多少次才能到达终点。答案对 100000000710000000071000000007 取模。

【输入格式】

输入第一行包含一个整数 nnn ,表示一共有 n+1n+1n+1 个格子。

第二行包含 nnn 个正整数 PiP_iPi ,含义如题目所描述。

【输出格式】

输出一个整数,表示跳的次数,答案对 100000000710000000071000000007 取模。

【样例输入1】

2
1 2

【样例输出1】

4

【样例输入2】

4
1 1 2 3

【样例输出2】

20

【样例输入1】

5
1 1 1 1 1

【样例输出1】

62

【数据范围】

对于 20%20\%20% 的数据,1≤n≤201\le n\le 201n20

对于 40%40\%40% 的数据,1≤n≤2001\le n\le 2001n200

对于 100%100\%100% 的数据,1≤n≤10000001\le n\le 10000001n1000000

【算法分析】

点走到第 i+1i+1i+1 个格子时,前 iii 个格子里的甜甜圈数量一定是偶数个。

f[i]f[i]f[i] 表示从起点第一次走到第 iii 个格子需要跳的次数

那么如何从第 iii 个点跳到第 i+1i+1i+1 个点呢?

先跳到第 iii 个点,放了一个甜甜圈,然后跳到 p[i]p[i]p[i] ,此时 p[i]p[i]p[i] 为奇数,经过一系列的跳跃又回到 iii ,而这一个过程可以看做第一次跳到 p[i]p[i]p[i] ,再跳到 iii ,跳跃次数为 f[i]−f[p[i]]f[i]-f[p[i]]f[i]f[p[i]]最后跳到第 i+1i+1i+1 个点,故递推公式为:

f[i+1]=f[i]+1+f[i]−f[p[i]]+1f[i+1]=f[i]+1+f[i]-f[p[i]]+1f[i+1]=f[i]+1+f[i]f[p[i]]+1

【参考程序】

#include<bits/stdc++.h>
using namespace std;
const int N=1000010;
const int P=1000000007;
int n,p[N],f[N];	//p[i]表示第一次跳到i的次数 
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%d",&p[i]);
	for(int i=1;i<=n;i++)
	{
		f[i+1]=(f[i]+1+f[i]-f[p[i]]+1)%P;
		if(f[i+1]<0) f[i+1]+=P;				//有减法,可能为负数
	}
	cout<<f[n+1];	 
	return 0;
} 

B:pudding

时间限制:2s2s2s 空间限制:128MB128 MB128MB

【题目描述】

HHH 得到了一个字符串。他想让 AAA 找出其中包含 (0w0)(0w0)(0w0)子序列(可以不连续)有多少个。(注意 000 是数字,不是字母 ooo)为了提高难度,HHH 提出他可以随时修改一个或一段字符,询问区间内的 (0w0)(0w0)(0w0) 的子序列个数。答案对 429496729642949672964294967296 取模。

【输入格式】

第一行包含两个整数 n,mn,mn,m ,分别表示字符串的长度和操作的数量。

第二行包含一个长度为 nnn 的字符串,表示一开始的字符串;

加下来 mmm 行,每行一个操作,操作的格式如下:

(1)A x y:(1)A\ x\ y:1A x y表示把字符的第 xxx 位改为字符 yyy 。字符串元素从 111 开始标号。

(2)B x y z:(2)B\ x\ y \ z:2B x y z表示把字符的第 xxx 位到第 yyy 位都改成字符 zzz

(3)C x y:(3)C\ x\ y:3C x y表示询问字符串区间 [x,y][x,y][x,y] 内有多少个子序列等于 (0w0)(0w0)(0w0)

【输出格式】

对于每个 CCC 操作,输出一行,宝行一个整数,表示该询问的答案,对 429496729642949672964294967296 取模。

【样例输入】

7 4
(0w0#))
C 1 6
B 5 6 w
A 6 0
C 1 7

【样例输出】

1
4

【数据范围】

对于 20%20\%20% 的数据,1≤n,m≤501\le n,m\le 501n,m50

对于 40%40\%40% 的数据,1≤n,m≤2001\le n,m\le 2001n,m200

对于 60%60\%60% 的数据,1≤n,m≤10001\le n,m\le 10001n,m1000

对于 100%100\%100% 的数据,1≤n≤500001\le n\le 500001n50000B,CB,CB,C 操作中的 x,yx,yx,y 满足 x≤yx\le yxy,原字符串和修改操作中的字符 ASCIIASCIIASCII 码范围为 [33,126][33,126][33,126],每种操作数量各占总数的约 13\frac 1 331

【算法分析】

202020

暴力,修改时直接修改,询问时枚举 555 个字符,555forforfor 循环,判断是否是子序列。

时间复杂度 O(m×n5)O(m\times n^5)O(m×n5)

404040

202020 分到基础上优化一下。对于询问时,找到字符 www ,然后统计左边有多少个"(0(0(0",右边有多少个“0)0)0)” ,然后通过乘法原理,就可以得到当前 www 为中心的子序列数量,将所有数量相加就是最终答案。

时间复杂度 O(m×n3)O(m\times n^3)O(m×n3)

606060

DPDPDP

f[i][j]f[i][j]f[i][j] 表示字符串的前 iii 个字符里有多少子序列等于 (0w0)(0w0)(0w0) 的前 jjj(j=0,1,2,3,4,5)(j=0,1,2,3,4,5)(j=0,1,2,3,4,5)

状态转移方程:

如果第 i+1i+1i+1 位和第 j+1j+1j+1 位不相等:f[i+1][j]=f[i][j]f[i+1][j]=f[i][j]f[i+1][j]=f[i][j]

如果第 i+1i+1i+1 位和第 j+1j+1j+1 位相等:f[i+1][j+1]=f[i][j]f[i+1][j+1]=f[i][j]f[i+1][j+1]=f[i][j]

时间复杂度 O(nm)O(nm)O(nm)

100100100

线段树

用线段树维护区间内有多少个 (0w0)(0w0)(0w0)

对于一个区间,分两个情况:

  • (0w0)(0w0)(0w0) 在区间内
  • (0w0)(0w0)(0w0) 横跨两个区间
    (0w0(0w0(0w0 在左侧,))) 在右侧
    (0w(0w(0w 在左侧,0)0)0) 在右侧
    (0(0(0 在左侧,w0)w0)w0) 在右侧
    ((( 在左侧,0w0)0w0)0w0) 在右侧

因此,还需要维护区间有多少 ((((0(0(0(0w(0w(0w(0w0(0w0(0w00w0)0w0)0w0)w0)w0)w0)0)0)0))))

因此,对于线段树每个节点,定义一个数组 f[i][j]f[i][j]f[i][j] ,来维护上诉的字符串,表示子序列 (0w0)(0w0)(0w0)i∼ji\sim jij 位构成的子串出现的次数。

对于区间更新:fl,frf_l,f_rfl,fr 表示左右子区间,则

f[i][j]=fl[i][j]+fr[i][j]+∑k=ij−1(fl[i][k]×fr[k+1][j])f[i][j]=f_l[i][j]+f_r[i][j]+\sum_{k=i}^{j-1}(f_l[i][k]\times f_r[k+1][j])f[i][j]=fl[i][j]+fr[i][j]+k=ij1(fl[i][k]×fr[k+1][j])

fl[i][k]×fr[k+1][j]f_l[i][k]\times f_r[k+1][j]fl[i][k]×fr[k+1][j] 就是 (0w0)(0w0)(0w0) 横跨两个区间的情况。

对于区间修改,可以打 lazylazylazy 标记。

时间复杂度 O(nlog⁡n×53)O(n\log n\times 5^3)O(nlogn×53)

【参考程序】
来自于yxc20220744的贡献

#include<bits/stdc++.h>
using namespace std;
#define full(a,b) memset(a,b,sizeof a)
#define ll long long
const ll INF=(1LL<<60);
const ll N=5e4+5;
const ll M=4294967296;
ll n,m;char arr[N];
ll ans[5][5];
struct node
{
    ll f[7][7],len,lazy;
} tree[4*N];
void build(ll l,ll r,ll k);
void update(ll l,ll r,ll k,ll x,ll y,ll v);
void ask(ll l,ll r,ll k,ll x,ll y);
void merge(ll a,ll b,ll c);
void pushdown(ll k);
int main()
{
//  freopen("pudding.in","r",stdin);
//  freopen("pudding.out","w",stdout);
    scanf("%lld%lld",&n,&m);
    scanf("\n%s",arr+1);
    build(1,n,1);
    while(m--)
    {
        char opt;scanf("\n%c",&opt);
        if(opt=='A')
        {
            ll x;char y;
            scanf("%lld %c",&x,&y);
            update(1,n,1,x,x,y);
        }
        else
        if(opt=='B')
        {
            ll x,y;char z;
            scanf("%lld%lld %c",&x,&y,&z);
            update(1,n,1,x,y,z);
        }
        else
        if(opt=='C')
        {
            ll x,y;
            scanf("%lld%lld",&x,&y);
            full(ans,0);
            ask(1,n,1,x,y);
            printf("%lld\n",ans[0][4]);
        }
    }
}
void build(ll l,ll r,ll k)
{
    tree[k].len=r-l+1;
    if(l==r)
    {
        if(arr[l]=='(') tree[k].f[0][0]=1;
        else if(arr[l]=='0') tree[k].f[1][1]=tree[k].f[3][3]=1;
        else if(arr[l]=='w') tree[k].f[2][2]=1;
        else if(arr[l]==')') tree[k].f[4][4]=1;
        return;
    }
    ll mid=(l+r)>>1;
    build(l,mid,k*2);
    build(mid+1,r,k*2+1);
    merge(k,k*2,k*2+1);
}
void update(ll l,ll r,ll k,ll x,ll y,ll v)
{
    if(x>r||y<l) return;
    if(x<=l&&r<=y)
    {
        tree[k].lazy=v;
        pushdown(k);
        return ;
    }
    pushdown(k*2);pushdown(k*2+1);
    ll mid=(l+r)>>1;
    update(l,mid,k*2,x,y,v);
    update(mid+1,r,k*2+1,x,y,v);
    merge(k,k*2,k*2+1);
}
void ask(ll l,ll r,ll k,ll x,ll y)
{
    if(x>r||y<l) return;
    if(x<=l&&r<=y)
    {
        ll tmp[5][5];
        for(ll i=0; i<=4; i++)
            for(ll j=i; j<=4; j++)
            {
                tmp[i][j]=ans[i][j]+tree[k].f[i][j];
                for(ll r=i; r<j; r++)
                    tmp[i][j]+=ans[i][r]*tree[k].f[r+1][j],
                    tmp[i][j]%=M;
            }
        memcpy(ans,tmp,sizeof tmp);
        return ;
    }
    pushdown(k*2);pushdown(k*2+1);
    ll mid=(l+r)>>1;
    ask(l,mid,k*2,x,y);
    ask(mid+1,r,k*2+1,x,y);
}
void merge(ll a,ll b,ll c)
{
    for(ll i=0; i<=4; i++)
        for(ll j=i; j<=4; j++)
        {
            tree[a].f[i][j]=tree[b].f[i][j]+tree[c].f[i][j];
            for(ll k=i; k<j; k++)
                tree[a].f[i][j]+=tree[b].f[i][k]*tree[c].f[k+1][j],
                tree[a].f[i][j]%=M;
        }
}
void pushdown(ll k)
{
    if(!tree[k].lazy) return;
    full(tree[k].f,0);
    if(tree[k].lazy=='(') tree[k].f[0][0]=tree[k].len;
    else if(tree[k].lazy=='0') tree[k].f[1][1]=tree[k].f[3][3]=tree[k].len;
    else if(tree[k].lazy=='w') tree[k].f[2][2]=tree[k].len;
    else if(tree[k].lazy==')') tree[k].f[4][4]=tree[k].len;
    if(k*2<4*N) tree[k*2].lazy=tree[k*2+1].lazy=tree[k].lazy;
    tree[k].lazy=0;
    return; 
}

C:tiramisu

来源:「雅礼集训 2018 Day2」操作

时间限制:2s2s2s 空间限制:256MB256 MB256MB

【题目描述】

AAA 买了一份提拉米苏,在吃之前,她决定思考一个问题来增加仪式感。她随机出一个 010101 串和一个正整数 kkk,然后询问自己 mmm 次:每次给出一个区间,可以进行若干次操作,选出区间任意一个长度为 kkk 的子区间取反,求最少多少次操作可以把区间全变为 000 。每次询问独立。

【输入格式】

输入的第一行包含三个整数 n,k,mn,k,mn,k,m ,分别表示 010101 串的长度,每次可操作的区间长度,以及询问的数量;

第二行包含一个长度为 nnn010101 串;

接下来 mmm 行,每行两个正整数 l,r(l≤r)l,r(l\le r)l,r(lr),表示每次询问的区间 [l,r][l,r][l,r]

【输出格式】

输出 mmm 行,对于每个询问,输出最少需要的操作次数。若该询问不可能完成,则输出 −1-11

【样例输入】

10 2 5
0100011010
1 7
2 10
4 8
5 9
4 10

【样例输出】

-1
6
1
-1
-1

【数据范围】

对于 10%10\%10% 的数据,1≤n,m≤5001\le n,m\le 5001n,m500

对于 30%30\%30% 的数据,1≤n,m≤50001\le n,m\le 50001n,m5000

对于 70%70\%70% 的数据,1≤n,m≤1000001\le n,m\le 1000001n,m100000

对于 100%100\%100% 的数据,1≤n≤2000000,1≤m≤5000001\le n\le 2000000,1\le m\le 5000001n20000001m500000

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值