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;
}