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)(1≤Pi≤i) 。当到达第 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 201≤n≤20;
对于 40%40\%40% 的数据,1≤n≤2001\le n\le 2001≤n≤200;
对于 100%100\%100% 的数据,1≤n≤10000001\le n\le 10000001≤n≤1000000 。
【算法分析】
点走到第 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:(1)A x y:表示把字符的第 xxx 位改为字符 yyy 。字符串元素从 111 开始标号。
(2)B x y z:(2)B\ x\ y \ z:(2)B x y z:表示把字符的第 xxx 位到第 yyy 位都改成字符 zzz 。
(3)C x y:(3)C\ x\ y:(3)C 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 501≤n,m≤50;
对于 40%40\%40% 的数据,1≤n,m≤2001\le n,m\le 2001≤n,m≤200;
对于 60%60\%60% 的数据,1≤n,m≤10001\le n,m\le 10001≤n,m≤1000;
对于 100%100\%100% 的数据,1≤n≤500001\le n\le 500001≤n≤50000 ,B,CB,CB,C 操作中的 x,yx,yx,y 满足 x≤yx\le yx≤y,原字符串和修改操作中的字符 ASCIIASCIIASCII 码范围为 [33,126][33,126][33,126],每种操作数量各占总数的约 13\frac 1 331。
【算法分析】
202020 分
暴力,修改时直接修改,询问时枚举 555 个字符,555 个 forforfor 循环,判断是否是子序列。
时间复杂度 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(0w0, 0w0)0w0)0w0), w0)w0)w0), 0)0)0), ))),
因此,对于线段树每个节点,定义一个数组 f[i][j]f[i][j]f[i][j] ,来维护上诉的字符串,表示子序列 (0w0)(0w0)(0w0) 第 i∼ji\sim ji∼j 位构成的子串出现的次数。
对于区间更新: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=ij−1(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(nlogn×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 串的长度,每次可操作的区间长度,以及询问的数量;
第二行包含一个长度为 nnn 的 010101 串;
接下来 mmm 行,每行两个正整数 l,r(l≤r)l,r(l\le r)l,r(l≤r),表示每次询问的区间 [l,r][l,r][l,r] 。
【输出格式】
输出 mmm 行,对于每个询问,输出最少需要的操作次数。若该询问不可能完成,则输出 −1-1−1。
【样例输入】
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 5001≤n,m≤500;
对于 30%30\%30% 的数据,1≤n,m≤50001\le n,m\le 50001≤n,m≤5000;
对于 70%70\%70% 的数据,1≤n,m≤1000001\le n,m\le 1000001≤n,m≤100000;
对于 100%100\%100% 的数据,1≤n≤2000000,1≤m≤5000001\le n\le 2000000,1\le m\le 5000001≤n≤2000000,1≤m≤500000;
286

被折叠的 条评论
为什么被折叠?



