例1:洛谷 P1081 开车旅行
链接:https://www.luogu.org/problemnew/show/P1081
题解:dp[i][j][k]三维dp,表示i位为起点,第一步走次近点/最近点(j=0/1),走2^k步,能走到的点,以及这么走a开的路程,b开的路程,对第一个询问暴枚n,第二个则直接查询,总复杂度是(n+m)*log的。注意这里得到dp初始状态还有点难写,找最近与次近需要维护一个有序链表,第i位查完就在链表中删掉即可。
代码:
// luogu-judger-enable-o2
#include<bits/stdc++.h>
#define pii pair<int,int>
#define ll long long
using namespace std;
const int N=1e5+10,inf=2e9+10;
int n,h[N],nxt[N][2][20];
pii hh[N];
ll dpa[N][2][20],dpb[N][2][20];
struct lian{
int fr,to,w,id;
}node[N];
int ps[N],tot=0;
int find(int x,int c)
{
int to,fr,tp1,tp2;
fr=node[x].fr,to=node[x].to;
lb:
tp1=(fr==0)?inf:abs(node[x].w-node[fr].w);
tp2=(to==n+1)?inf:abs(node[x].w-node[to].w);
if(c>1)
{
--c;
if(tp1<tp2||(tp1==tp2&&node[fr].w<node[to].w))fr=node[fr].fr;
else to=node[to].to;
goto lb;
}
if(tp1<tp2||(tp1==tp2&&node[fr].w<node[to].w))return node[fr].id;
return node[to].id;
}
void del(int x)
{
x=ps[x];
node[node[x].fr].to=node[x].to;
node[node[x].to].fr=node[x].fr;
return;
}
void get(int s,int x,ll &A,ll &B)
{
A=B=0;
for(int i=19;i>=0;i--)
{
if(nxt[s][1][i]!=0&&dpa[s][1][i]+dpb[s][1][i]<=x)
{
x-=dpa[s][1][i]+dpb[s][1][i];
A+=dpa[s][1][i],B+=dpb[s][1][i];
s=nxt[s][1][i];
}
}
}
void solve1(int x)
{
ll resa,resb,A,B;int res;
for(int i=1;i<=n;i++)
{
get(i,x,A,B);
if(i==1)resa=A,resb=B,res=i;
else if(resb==0)
{
if(B==0)
{
if(h[i]>res)resa=A,resb=B,res=i;
}
else resa=A,resb=B,res=i;
}
else if(B==0)continue;
else if(A*resb<resa*B)resa=A,resb=B,res=i;
else if(A*resb==resa*B&&h[i]>h[res])resa=A,resb=B,res=i;
}
printf("%d\n",res);
}
int main()
{
int X,S,m;ll A,B;
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&h[i]),hh[i]=pii(h[i],i);
sort(hh+1,hh+n+1);
for(int i=1;i<=n;i++)
{
ps[hh[i].second]=++tot;
node[i].fr=i-1,node[i].to=i+1;
node[i].id=hh[i].second;node[i].w=hh[i].first;
}
for(int i=1;i<=n;i++)
{
if(i+1>n)nxt[i][0][0]=0;
else nxt[i][0][0]=find(ps[i],1),dpb[i][0][0]+=abs(h[i]-h[nxt[i][0][0]]);
if(i+2>n)nxt[i][1][0]=0;
else nxt[i][1][0]=find(ps[i],2),dpa[i][1][0]+=abs(h[i]-h[nxt[i][1][0]]);
del(i);
//cout<<i<<" "<<dpa[i][0][0]<<" "<<dpb[i][1][0]<<'\n';
}
for(int l=1;l<20;l++)
{
for(int i=1;i<=n;i++)
{
nxt[i][0][l]=nxt[nxt[i][0][l-1]][(l==1)?1:0][l-1];
dpa[i][0][l]=dpa[i][0][l-1]+dpa[nxt[i][0][l-1]][(l==1)?1:0][l-1];
dpb[i][0][l]=dpb[i][0][l-1]+dpb[nxt[i][0][l-1]][(l==1)?1:0][l-1];
nxt[i][1][l]=nxt[nxt[i][1][l-1]][(l==1)?0:1][l-1];
dpa[i][1][l]=dpa[i][1][l-1]+dpa[nxt[i][1][l-1]][(l==1)?0:1][l-1];
dpb[i][1][l]=dpb[i][1][l-1]+dpb[nxt[i][1][l-1]][(l==1)?0:1][l-1];
}
}
scanf("%d",&X);
solve1(X);
scanf("%d",&m);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&S,&X);
get(S,X,A,B);
printf("%lld %lld\n",A,B);
}
}
例2:leetcode 466
链接:https://leetcode-cn.com/problems/count-the-repetitions/description/
没想出来.jpg......做法是对于s1串每个位置预处理它得到s2的2的幂次所需步数,最后暴枚起始位置,算S1能最多包含多少s2,除以n2即可。
代码:
#include<bits/stdc++.h>
using namespace std;
class Solution {
public:
#define ll long long
ll dp[110][30],pow2[30];
int getMaxRepetitions(string s1, int n1, string s2, int n2) {
int len1=s1.length(),len2=s2.length();
pow2[0]=1;
for(int i=1;i<30;i++)pow2[i]=2*pow2[i-1];
for(int i=0;i<len1;i++)
{
for(int j=0,ps=0;;j++)
{
if(s1[(i+j)%len1]==s2[ps])++ps;
if(ps==len2){dp[i][0]=j+1;break;}
if(j>10000)return 0;
}
}
for(int l=1;l<30;l++)
{
for(int i=0;i<len1;i++)
{
dp[i][l]=dp[i][l-1]+dp[(i+dp[i][l-1])%len1][l-1];
}
}
ll las,nw,ps,ans=0;
for(int bg=0;bg<len1;bg++)
{
las=n1*len1-bg,ps=bg,nw=0;
for(int j=29;j>=0;j--)
{
if(las>=dp[ps][j])
{
nw+=pow2[j];
las-=dp[ps][j];
ps=(ps+dp[ps][j])%len1;
}
}
ans=max(ans,nw/n2);
}
return ans;
}
#undef ll
}S;
int main()
{
string s1,s2;
int n1,n2;
cin>>s1>>n1>>s2>>n2;
cout<<S.getMaxRepetitions(s1,n1,s2,n2);
}