A. 打表
首先注意这道题数组下标从 \(0\) 开始
可以找规律发现是 \(\displaystyle\frac{\sum |a_i-a _ {ans}|}{2^k}\)
那么严谨证明一下:
由于两个 \(CPU\) 的种类是等概率的,而选择为最优选择,那么每一位的枚举顺序是没有影响的。对于每一位来说,一个选了 \(0\) 的情况,相应地另一个就是 \(1\) 的情况,所以对于所有的情况总和是绝对值之差
对于这类期望题,还是大胆猜结论比较靠谱……
B. 蛇
由于格子特殊的形状,蛇的行动路线一定是往左走一截再从另一行走回来,然后 \(S\) 型盘旋,最后往右走一段再走回来。
那么左右两段可以用哈希直接判断,而中间部分由于横向移动方向单调可以进行 \(dp\)
设 \(f[k][i][j]\) 表示第 \(k\) 行第 \(i\) 列,匹配到模式串第 \(j\) 位的方案数
为避免环形转移,令当前列只能从上一列转移过来
\(hash\) 特别容易出错,需要处理前缀和后缀两种哈希
代码实现
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=4005;
const int mod=1e9+7,base=131;
int n,m,pash[3][maxn],sash[3][maxn],hash1[maxn],bash[maxn],po[maxn],ans,f[3][maxn/2][maxn];
char a[3][maxn/2],b[maxn];
void solve(){
memset(f,0,sizeof f);
memset(pash,0,sizeof pash);
memset(sash,0,sizeof sash);
for(int i=1;i<=2;i++){
for(int j=1;j<=n;j++)pash[i][j]=(pash[i][j-1]*base+a[i][j])%mod;
for(int j=n;j>=1;j--)sash[i][j]=(sash[i][j+1]*base+a[i][j])%mod;
}
// cout<<"ppp "<<pash[1][2]<<" "<<sash[2][3]<<endl;
for(int k=1;k<=2;k++){
for(int i=1;i<=n;i++){
if(a[k][i]==b[1])f[k][i][1]=1;
for(int j=i;j<=n;j++){
int len=j-i+1;
if(len*2>m)continue;
int sum=((pash[k][j]-pash[k][i-1]*po[len]%mod+mod)%mod+(sash[3-k][i]-sash[3-k][j+1]*po[len]%mod+mod)%mod*po[len]%mod)%mod;
sum=(sum+mod)%mod;
int sum1=(bash[1]-bash[len*2+1]*po[len*2]%mod)%mod;
sum1=(sum1+mod)%mod;
// if(k==1&&i==2&&j==2)cout<<sum<<" "<<sum1<<" "<<bash[1]<<" "<<bash[len*2+1]<<" "<<po[m-len*2]<<endl;
if(sum1==sum){
// cout<<"hhhh "<<k<<" "<<i<<" "<<j<<" "<<len*2<<endl;
f[3-k][j][len*2]=1;
}
}
}
}
// cout<<f[1][5][4]<<endl;
for(int i=2;i<=n;i++){
for(int j=2;j<=m;j++){
for(int k=1;k<=2;k++){
if(a[k][i]==b[j]){
f[k][i][j]=(f[k][i][j]+f[k][i-1][j-1])%mod;
// if(k==1&&i==5&&j==4)cout<<f[k][i][j]<<" "<<f[k][i-1][j-1]<<endl;
if(a[3-k][i]==b[j-1]&&j>=2)f[k][i][j]=(f[k][i][j]+f[3-k][i-1][j-2])%mod;
}
}
}
}
// for(int i=1;i<=n;i++){
// for(int j=1;j<=m;j++){
// for(int k=1;k<=2;k++){
// if(f[k][i][j])cout<<k<<" "<<i<<" "<<j<<" "<<f[k][i][j]<<endl;
// }
// }
// }
for(int k=1;k<=2;k++){
for(int i=1;i<=n;i++){
// if(f[k][i][m]){
// cout<<"kkk "<<k<<" "<<i<<" "<<f[k][i][m]<<endl;
// }
ans=(ans+f[k][i][m])%mod;
for(int j=i+1;j<=n;j++){
int len=j-i+1;
if(len*2>m)continue;
int sum=((pash[k][j]-pash[k][i-1]*po[len]%mod+mod)%mod*po[len]%mod+(sash[3-k][i]-sash[3-k][j+1]*po[len]%mod+mod)%mod)%mod;
sum=(sum+mod)%mod;
int sum1=(hash1[m]-hash1[m-len*2]*po[len*2]%mod)%mod;
sum1=(sum1+mod)%mod;
// if(i==4&&j==5&&k==2)cout<<sum<<" "<<sum1<<endl;
if(sum==sum1){
// cout<<k<<" "<<i<<" "<<j<<endl;
// if(f[k][i-1][m-len*2]){
// cout<<3-k<<" "<<i-1<<" "<<m-len*2<<" "<<f[3-k][i-1][m-len*2]<<endl;
// }
ans=(ans+f[k][i-1][m-len*2])%mod;
}
}
}
}
return ;
}
void pre(){
po[0]=1;
for(int i=1;i<=2000;i++){
po[i]=po[i-1]*base%mod;
}
for(int i=1;i<=m;i++)hash1[i]=(hash1[i-1]*base+b[i])%mod;
for(int i=m;i>=1;i--)bash[i]=(bash[i+1]*base+b[i])%mod;//,cout<<bash[i]<<" ";
// cout<<endl;
return ;
}
bool vis[3][maxn];
int movx[5]={0,1,0,-1};
int movy[5]={1,0,-1,0};
bool pd(int x,int y){
return x>=1&&y>=1&&x<=2&&y<=n;
}
void dfs(int x,int y,int len){
// cout<<x<<" "<<y<<" "<<len<<endl;
if(len==m){
ans++;
return ;
}
vis[x][y]=true;
for(int i=0;i<4;i++){
int xx=x+movx[i];
int yy=y+movy[i];
if(vis[xx][yy])continue;
if(pd(xx,yy)){
if(a[xx][yy]==b[len+1]){
dfs(xx,yy,len+1);
}
}
}
vis[x][y]=false;
return ;
}
signed main(){
// freopen("shuju.in","r",stdin);
cin>>n;
scanf("%s",a[1]+1);
scanf("%s",a[2]+1);
n=strlen(a[1]+1);
scanf("%s",b+1);
m=strlen(b+1);
if(m<=2){
for(int i=1;i<=2;i++){
for(int j=1;j<=n;j++){
if(a[i][j]==b[1])dfs(i,j,1);
}
}
cout<<ans;
return 0;
}
pre();
solve();
// cout<<ans<<endl;
reverse(a[1]+1,a[1]+n+1);
reverse(a[2]+1,a[2]+n+1);
solve();
cout<<ans;
return 0;
}
C. 购物
对于一个特定的体积 \(x\),能更新的满足条件的 \(k\) 集合为 \([\left \lceil \frac{x}{2}\right \rceil ,x]\)
那么发现这样的区间大多都是连续的,除非出现这样的情况:
将所有物品从小到大排好序,那么如果 \(a_i>2 * sum_{i-1}\),中间的一段是空下的,因为后面的体积更大,无法补充空隙
所以没加入一个数进行判断即可
代码实现
#include<bits/stdc++.h>
using namespace std;
int read(){
int x=0,f=1;
char ch=getchar();
while(!isdigit(ch)){
x=x*10+ch-48;
ch=getchar();
}
while(isdigit(ch)){
x=x*10+ch-48;
ch=getchar();
}
return x*f;
}
#define int long long
const int maxn=1e5+5;
int n,a[maxn],ans,sum;
signed main(){
n=read();
for(int i=1;i<=n;i++)a[i]=read();
sort(a+1,a+n+1);
int rp=0;
for(int i=1;i<=n;i++){
rp=max((a[i]+1)>>1,sum+1);
sum+=a[i];
ans+=sum-rp+1;
}
cout<<ans;
return 0;
}
D. ants
发现这道题插入很容易但是删除对答案的贡献并不确定
于是引入新科技——回滚莫队
这种莫队方式块内每个询问都直接把左端点拽回块右端点并恢复历史版本,往左移动,这样可以保证只有加操作了
复杂度不变还是根号的
代码实现
#include<bits/stdc++.h>
using namespace std;
const int maxn=3e5+5;
int n,m,size,be[maxn],le[maxn],ri[maxn],l,r,tp,now,len,b[maxn],a[maxn],cnt,mx,ans[maxn];
bool vis[maxn];
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-48;
ch=getchar();
}
return x*f;
}
struct Node{
int l,r,id;
Node(){}
Node(int x,int y,int z):l(x),r(y),id(z){}
}ask[maxn];
struct Rec{
int p,v,l,r;
Rec(){}
Rec(int x,int y,int z,int w):p(x),v(y),l(z),r(w){}
}sta[maxn];
bool cmp(Node a,Node b){
return be[a.l]==be[b.l]?a.r<b.r:a.l<b.l;
}
void push(int x){
sta[++tp]=Rec(x,vis[x],le[x],ri[x]);
return ;
}
void add(int x,bool op){
int l=le[x-1],r=ri[x+1];
if(vis[x-1]){
if(op)push(l);
}
else l=x;
if(vis[x+1]){
if(op)push(r);
}
else r=x;
if(op)push(x);
vis[x]=true,le[x]=l,ri[x]=r,ri[l]=r,le[r]=l;
now=max(now,ri[x]-le[x]+1);
return ;
}
void clear(){
while(tp){
Rec x=sta[tp--];
vis[x.p]=x.v,le[x.p]=x.l,ri[x.p]=x.r;
}
return ;
}
int baoli(int l,int r){
for(int i=l;i<=r;i++)add(a[i],1);
int res=now;
now=0;
clear();
return res;
}
int main(){
n=read();
m=read();
size=sqrt(n);
for(int i=1;i<=n;i++)a[i]=read(),be[i]=(i-1)/size+1,le[i]=ri[i]=i;
for(int i=1;i<=m;i++){
l=read();
r=read();
if(be[l]==be[r])ans[i]=baoli(l,r);
else ask[++cnt]=Node(l,r,i);
}
sort(ask+1,ask+cnt+1,cmp);
for(int i=1;i<=cnt;i++){
if(be[ask[i].l]!=be[ask[i-1].l]){
now=0;
r=size*be[ask[i].l];
for(int j=1;j<=n;j++)vis[j]=0,le[j]=ri[j]=j;
}
while(r<ask[i].r)add(a[++r],0);
int l=size*be[ask[i].l]+1,last=now;
while(l>ask[i].l)add(a[--l],1);
ans[ask[i].id]=now;
now=last,clear();
}
for(int i=1;i<=m;i++)printf("%d\n",ans[i]);
return 0;
}