题意:电梯前有两列队伍,电梯一次可以坐两人,其中
- 有 25% 的可能,电梯是满的,没有办法乘坐电梯;
- 有 25% 的可能,电梯是空的,则两队队首都可以乘坐电梯;
- 有 50% 的可能,电梯里已经有一个人,则两队队首乘坐的概率均等。
求第一列第 i 个人与第二列第 j 个人同时坐上电梯的概率。
分析:概率dp。电梯满的情况相当于没来,这种可能划去。即
- 有 1/3 的可能,两人同时上
- 有 1/3 的可能,左边队伍上一人
- 有 1/3 的可能,右边队伍上一人
因此定义dp[i][j]为左边队伍第i个人与右边队伍第j个人同时在队首的概率。
转移方程为dp[i][j]=1/3*(dp[i-1][j]+dp[i][j-1]+dp[i-1][j-1])
两人同时上的概率为1/3*dp[i][j] 。
另外卡了内存,需要开一个滚动数组。
代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod=998244353;
int dp[2][10005];
signed main()
{
int x,y;
cin>>x>>y;
dp[1][1]=1;
int inv3=332748118;
for(int i=1;i<=x;i++)
{
for(int j=1;j<=y;j++)
{
if(i==1&&j==1) continue;
dp[i&1][j]=(dp[(i-1)&1][j]+dp[i&1][j-1]+dp[(i-1)&1][j-1])*inv3%mod;
}
}
cout<<dp[x&1][y]*inv3%mod<<endl;
}
题意:给定一个由ab组成的字符串,求区间长度大于等于x且ab个数相同的区间是否存在。
分析:时间复杂度上显然只能O(n)扫一遍,令a=1,b=-1,考虑计算序列前缀和{Si},若Si=Sj,说明i到j之间a和b的数量相等。
代码:
#include<bits/stdc++.h>
using namespace std;
char c[10005];
int main()
{
int t;
cin>>t;
while(t--)
{
map<int,int>mp;
mp[0]=0;//
int n,x;
cin>>n>>x;
scanf("%s",c+1);
int cnt=0;
int f=0;
for(int i=1;i<=n;i++)
{
if(c[i]=='a') cnt++;
else cnt--;
if(mp.count(cnt))//又回到了曾经的数目 说明中间这段ab相等
{
if(i-mp[cnt]>=x)
{
f=1;
cout<<mp[cnt]+1<<" "<<i<<endl;
break;
}
}
else mp[cnt]=i;
}
if(f==0) cout<<"impossible"<<endl;
}
}
题意:求字符串中子串的最大循环次数。
分析:O(n^2logn)+break。利用后缀数组可以做到O(nlogn)。
代码:
#include<bits/stdc++.h>
using namespace std;
int main()
{
int t;
cin>>t;
while(t--)
{
string a;
cin>>a;
int s=a.length();
int ans=1;
for(int l=1;l<=s;l++)//长度
{
if(s/l<=ans) break;
for(int i=0;i<s;i++)//起点
{
int j=i+l;//终点
if(j>s) break;
string tmp=a.substr(i,l);
int cnt=1,pos=i;
while(pos+l-1<s&&tmp==a.substr(pos+l,l))
{
pos+=l;
cnt++;
}
ans=max(ans,cnt);
}
}
printf("%d\n",ans);
}
}
后缀数组代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=1e5+5;
int xx[N],yy[N],cnt[N];
int sa[N],rk[N],ht[N];//sa[i]:排名为i的后缀的起始位置;rk[i]:起始位置为i的后缀的排名
char s[N];
int n;
void getsa(int n,int M)
{
int i,j,p,*x=xx,*y=yy;
for(i=0;i<M;i++)cnt[i]=0;
for(i=0;i<n;i++)cnt[x[i]=s[i]]++;
for(i=1;i<M;i++)cnt[i]+=cnt[i-1];
for(i=n-1;i>=0;i--)sa[--cnt[x[i]]]=i;
for(j=1,p=1;p<n;j<<=1,M=p)
{
for(p=0,i=n-j;i<n;i++)y[p++]=i;
for(i=0;i<n;i++)if(sa[i]>=j)y[p++]=sa[i]-j;
for(i=0;i<M;i++)cnt[i]=0;
for(i=0;i<n;i++)cnt[x[y[i]]]++;
for(i=1;i<M;i++)cnt[i]+=cnt[i-1];
for(i=n-1;i>=0;i--)sa[--cnt[x[y[i]]]]=y[i];
for(swap(x,y),p=1,x[sa[0]]=0,i=1;i<n;i++)
x[sa[i]]=(y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+j]==y[sa[i]+j])?p-1:p++;
}
}
void getht(int n)//求完这个才最终求出sa 并求出rk ht
{
int i,j,k=0;
for(i=1;i<=n;i++)rk[sa[i]]=i;
for(i=0;i<n;ht[rk[i++]]=k)
for(k?k--:0,j=sa[rk[i]-1];s[i+k]==s[j+k];k++);
for(i=n;i;i--)rk[i]=rk[i-1],sa[i]++;
}
int dp[N][20];
void RMQ()
{
for(int i=1;i<=n;i++) dp[i][0]=ht[i];
for(int j=1;(1<<j)<=n;j++)
{
for(int i=1;i+(1<<j)-1<=n;i++)
dp[i][j]=min(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
}
}
int query(int l,int r)
{
int k=0;
while((1<<(k+1))<=(r-l+1)) ++k;
return min(dp[l][k],dp[r-(1<<k)+1][k]);
}
int lcp(int i,int j)
{
i=rk[i],j=rk[j];
if(i>j) swap(i,j);
return query(i+1,j);
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%s",s);
n=strlen(s);
getsa(n+1,300);//字符串:n+1,300;数字:n+1,离散化后N
getht(n);
RMQ();
int ans=1;//最少是1
for(int i=1;i<=n;i++)
{
for(int j=1;j+i<=n;j+=i)
{
int now=lcp(j,j+i);
int num=now/i+1;
int k=j-(i-now%i);
if(k>0&&lcp(k,k+i)>=i) num++;
ans=max(ans,num);
}
}
printf("%d\n",ans);
}
}
题意:n个好友,m对关系,初始战斗力全为0。当一个好友战斗力提升x时会带动他的好友战斗力提升x(但是不会带动好友的好友再次提升)。
给定 q个操作:
- 1 p x,代表某人战斗力提升 x;
- 2 p,代表查询某人的战斗力。
分析:根号分治,将度数小于等于根号m的点作为小点,对于度数大于根号m的点作为大点。更新小点时,对周围的点暴力更新;更新大点时,另开一个lazy数组,更新在lazy数组上。查询某个点时我们先预处理把大点周围的点对大点连边,之后只需要遍历该点连向的大点就可以了。这样的话新图大概有sqrt(m)条单向边,复杂度O(q*sqrt(m))。
代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=2e5+5;
vector<int>e[maxn],e2[maxn];
int f[maxn];
int lazy[maxn];
signed main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=m;i++)
{
int u,v;
cin>>u>>v;
e[u].push_back(v);
e[v].push_back(u);
}
int sz=sqrt(m);
for(int i=1;i<=n;i++)
{
if(e[i].size()>sz)
{
for(int j:e[i])
{
e2[j].push_back(i);
}
}
}
int q;
cin>>q;
while(q--)
{
int op;
cin>>op;
int p,x;
cin>>p;
if(op==1)
{
cin>>x;
f[p]+=x;
if(e[p].size()<=sz) for(int j:e[p]) f[j]+=x;
else lazy[p]+=x;
}
else
{
int now=f[p];
for(int i:e2[p]) now+=lazy[i];
cout<<now<<endl;
}
}
}