学习笔记-倍增优化dp

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

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值