……

模板整理——dp,字符串及其他

- dp

鬼知道我为啥把这么重要的东西放最后面啊。

考虑设计无后效性的状态,减小运算量。

- 背包

#include"iostream"
#include"cstdio"
#include"cmath"
using namespace std;

#define read(x) scanf("%d",&x)
#define MAXN 1005

int n,w[MAXN],v[MAXN],m;
int dp[MAXN];
int ans=0;

int main()
{
    read(m),read(n);   
    for(int i=1;i<=n;i++) read(w[i]),read(v[i]);
    for(int i=1;i<=n;i++)
    {
        for(int j=m;j>=w[i];j--) dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
    }
    for(int i=1;i<=m;i++) ans=max(dp[i],ans);
    return printf("%d\n",ans),0;
}
#include"iostream"
#include"cstdio"
#include"cmath"
using namespace std;

#define read(x) scanf("%d",&x)
#define ll long long

int n,w[10005],v[10005],m;
ll dp[10000005];
ll ans=0;

int main()
{
    read(m),read(n);   
    for(int i=1;i<=n;i++) read(w[i]),read(v[i]);
    for(int i=1;i<=n;i++)
    {
        for(int j=w[i];j<=m;j++) dp[j]=max(dp[j],dp[j-w[i]]+(ll)v[i]);
    }
    for(int i=1;i<=m;i++) ans=max(dp[i],ans);
    return printf("%lld\n",ans),0;
}
#include"iostream"
#include"cstdio"
#include"cmath"
using namespace std;

int dp[1005];
int n,m;
int w[10005],v[10005],c[10005];
int h1,h2,m1,m2;
int ans=0;

int main()
{
    scanf("%d:%d%d:%d%d",&h1,&m1,&h2,&m2,&n);
    if(m2>=m1) m=(m2-m1)+60*(h2-h1);
    else m=(m2-m1+60)+60*(h2-h1-1);
    for(int i=1;i<=n;i++) scanf("%d%d%d",&w[i],&v[i],&c[i]);
    for(int i=1;i<=n;i++)
    {
        if(!c[i]) c[i]=1<<20;
        for(int k=1;c[i]>0;k<<=1)
        {
            int kk=min(k,c[i]);
            for(int j=m;j>=kk*w[i];j--) dp[j]=max(dp[j],dp[j-kk*w[i]]+kk*v[i]);
            c[i]-=kk;//一定要随拆随算
        }
    }
    for(int i=1;i<=m;i++) ans=max(ans,dp[i]);
    printf("%d\n",ans);
}
  • 单调队列优化(\(\mathcal O(nm)\)

字母错一个,调题一万年。

#include"iostream"
#include"cstring"
#include"cstdio"
#include"cmath"
using namespace std;

int dp[1005];
int n,m;
int w[10005],v[10005],c[10005];
int h1,h2,m1,m2;
int ans=0;
int q[1005],loc[1005];
int d[1005];
int tail,head=0;

int main()
{
    scanf("%d:%d%d:%d%d",&h1,&m1,&h2,&m2,&n);
    if(m2>=m1) m=(m2-m1)+60*(h2-h1);
    else m=(m2-m1+60)+60*(h2-h1-1);
    for(int i=1;i<=n;i++) scanf("%d%d%d",&w[i],&v[i],&c[i]);
    for(int i=1;i<=n;i++)
    {
        if(!c[i])
            for(int j=w[i];j<=m;j++) dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
        else
        {
            for(int j=0;j<=m;j++) d[j]=dp[j];//这个地方可能重复更新,而且不容易像01背包一样去倒叙枚举,所以这样改比较好
            for(int k=0;k<w[i];k++)
            {
                head=0,tail=1,q[1]=0,loc[1]=0;
                for(int j=0;j*w[i]+k<=m;j++)
                {
                    d[k+j*w[i]]=max(d[k+j*w[i]],q[tail]+j*v[i]);
                    while(head>=tail&&dp[k+j*w[i]]-j*v[i]>=q[head]) head--;
                    q[++head]=dp[k+j*w[i]]-j*v[i],loc[head]=j;
                    while(loc[tail]<=j-c[i]) tail++;
                }
            }
            for(int j=0;j<=m;j++) dp[j]=d[j];
        }
    }
    for(int i=1;i<=m;i++) ans=max(ans,dp[i]);
    printf("%d\n",ans);
    return 0;
}
  • 分组背包\(\mathcal O((n+c)m)\))(\(c\) 为组数)
#include"algorithm"
#include"iostream"
#include"cstring"
#include"cstdio"
#include"cmath"
using namespace std;

#define MAXN 1005
#define read(x) scanf("%d",&x)

int n,m;
struct node
{
    int w,v,c;
}a[MAXN];
int dp[MAXN],cnt[MAXN],ans=0,cp=0;

bool cmp(node n,node m){return n.c<m.c;}

int main()
{
    read(m),read(n);
    for(int i=1;i<=n;i++) read(a[i].w),read(a[i].v),read(a[i].c);
    sort(a+1,a+n+1,cmp);
    int lst=0;
    for(int i=1;i<=n;i++)
    {
         if(a[i].c!=lst) cp++,lst=a[i].c;
         cnt[cp]++;
    }
    int now=0;
    for(int i=1;i<=cp;i++)
    {
        for(int j=m;j>=0;j--)
        {
            lst=now;
            for(int k=1;k<=cnt[i];k++)
            {
                lst++;
                if(j<a[lst].w) continue;
                dp[j]=max(dp[j],dp[j-a[lst].w]+a[lst].v);
            }
            if(!j) now=lst;
        }
    }
    for(int i=1;i<=m;i++) ans=max(ans,dp[i]);
    printf("%d\n",ans);
    return 0;
}

- 最长上升子序列(LIS)\(\mathcal O(n\log n)\)

这是个套着 \(LCS\) 皮的 \(LIS\)

  • 朴素做法(二分)
#include"iostream"
#include"cstdio"
#include"cmath"
using namespace std;

#define MAXN 100005
#define read(x) scanf("%d",&x)

int n,x;
int a[MAXN],b[MAXN];
int now[MAXN],len=1;

int main()
{
    read(n);
    for(int i=1;i<=n;i++) read(x),b[x]=i;
    for(int i=1;i<=n;i++) read(x),a[i]=b[x];
    now[1]=a[1];
    for(int i=2;i<=n;i++)
    {
        int l=1,r=len,mid;
        if(a[i]>now[len]){now[++len]=a[i];continue;}
        while(l<r)
        {
            mid=(l+r)>>1;
            if(now[mid]<a[i]) l=mid+1;
            else r=mid;
        }
        if(l<len&&now[l]<a[i]) l--;
        now[l]=a[i];
    }
    printf("%d\n",len);
    return 0;
}
  • 大常数解法(线段树)

请参考 P2215

#include"algorithm"
#include"iostream"
#include"cstdio"
#include"cmath"
using namespace std;

#define read(x) scanf("%d",&x)
#define MAXN 10005

struct num
{
    int val,id;
}b[MAXN];
int n,m;
struct node
{
    int l,r,rec;
}a[MAXN<<2];
int re[MAXN],dp[MAXN];
int l,me[MAXN];

bool cmp(num n,num m){if(n.val==m.val) return n.id>m.id;else return n.val<m.val;}

void hash()
{
    sort(b+1,b+n+1,cmp);
    for(int i=1;i<=n;i++) re[b[i].id]=i,dp[i]=-1;
}

void update(int k){a[k].rec=max(a[k<<1].rec,a[k<<1|1].rec);}

void build(int k,int l,int r)
{
    a[k].l=l,a[k].r=r;
    if(l==r){a[k].rec=dp[l];return;}
    int mid=(l+r)>>1;
    build(k<<1,l,mid),build(k<<1|1,mid+1,r);
    update(k);
    return;
}

void modify(int k,int x,int y)
{
    if(a[k].l==a[k].r){a[k].rec=y;return;}
    if(a[k<<1].r>=x) modify(k<<1,x,y);
    else modify(k<<1|1,x,y);
    update(k);
}

int query(int k,int l,int r)
{
    if(a[k].l==l&&a[k].r==r) return a[k].rec;
    int mid=(a[k].l+a[k].r)>>1;
    if(r<=mid) return query(k<<1,l,r);
    else if(l>mid) return query(k<<1|1,l,r);
    else return max(query(k<<1,l,mid),query(k<<1|1,mid+1,r));
}

int main()
{
    read(n);
    for(int i=1;i<=n;i++) read(b[i].val),me[i]=b[i].val,b[i].id=i;
    hash(),build(1,1,n);
    for(int i=n;i>=1;i--)
    {
        int now=query(1,re[i],n);
        if(now==-1) dp[i]=1;
        else dp[i]=now+1;
        modify(1,re[i],dp[i]);
    }
    read(m);
    while(m--)
    {
        read(l);
        int now=l,lst=-0x7fffffff;
        for(int i=1;i<=n;i++)
        {
            if(dp[i]>=now&&me[i]>lst)
            {
                now--,lst=me[i];
                printf("%d ",me[i]);
                if(now==0) break;
            }
        }
        if(now>0) printf("Impossible");
        puts("");
    }
    return 0;
}

- 字符串

这个东西我真的不大会啊/kk。

- 字符串哈希(这里直接使用双模哈希)(\(\mathcal O(nm+n\log n)\)

#include"algorithm"
#include"iostream"
#include"cstring"
#include"cstdio"
#include"cmath"
using namespace std;

#define MOD 1777777777
#define MoD 1000000007

int n;
struct str
{
	int len,op,rt;
	str(){op=rt=0;}
}s[10005];
char c[1505];
int mio[1505],mir[1505];
int ans=0;

int tran(char s)
{
	if(s>='a'&&s<='z') return s-'a';
	else if(s>='A'&&s<='Z') return s-'A'+26;
	else return s-'0'+52;
}

bool cmp(str s,str t)
{
	if(s.len==t.len)
	{
		if(s.op==t.op) return s.rt<t.rt;
		return s.op<t.op;
	}
	return s.len<t.len;
}

int main()
{
	scanf("%d",&n);
	mio[0]=62,mir[0]=62;
	for(int i=1;i<=1501;i++)
	{
		mio[i]=1ll*mio[i-1]*62ll%MOD;
		mir[i]=1ll*mir[i-1]*62ll%MoD;
	}
	for(int i=1;i<=n;i++)
	{
		scanf("%s",c);
		s[i].len=strlen(c);
		for(int j=0;j<s[i].len;j++)
		{
			s[i].op=(s[i].op+1ll*mio[j]*tran(c[j])%MOD)%MOD;
			s[i].rt=(s[i].rt+1ll*mir[j]*tran(c[j])%MoD)%MoD;
		}
	}
	sort(s+1,s+n+1,cmp);
	ans=1;
	for(int i=2;i<=n;i++)
	{
		if(s[i].len==s[i-1].len&&s[i].op==s[i-1].op&&s[i].rt==s[i-1].rt) continue;
		else ans++;
	}
	printf("%d\n",ans);
	return 0;
}

- 字典树(Trie)\(\mathcal O(\sum len)\)

#include"iostream"
#include"cstdio"
#include"cmath"
#include"cstring"
using namespace std;

#define MAXN 500005

struct node
{
    int son[27],t;
}a[MAXN];
int n,m;
char c[55];
int cnt=0;

void add(string s)
{
    int p=0,len=s.size();
    for(int i=0;i<len;i++)
    {
        if(!a[p].son[s[i]-'a']) a[p].son[s[i]-'a']=++cnt;
        p=a[p].son[s[i]-'a'];
    }
    a[p].t++;
}

int find(string s)
{
    int p=0,len=s.size();
    for(int i=0;i<len;i++)
    {
        if(!a[p].son[s[i]-'a']) return -1;
        p=a[p].son[s[i]-'a'];
    }
    if(a[p].t){a[p].t--;return 1;}
    else return 2;
}

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%s",c);
        add(c);
    }
    scanf("%d",&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%s",c);
        int opt=find(c);
        if(opt==-1) puts("WRONG");
        else if(opt==1) puts("OK");
        else puts("REPEAT");
    }
    return 0;
}

- KMP字符串匹配\(\mathcal O(n+m)\)

#include"cstdio"
#include"cstring"
#include"iostream"
using namespace std;

#define MAXN 1000005

char s[MAXN],t[MAXN];
int f[MAXN],nxt[MAXN];
int tl,sl;

void get()
{
    nxt[1]=0;
    for(int i=2;i<=tl;i++)
    {
        int k=nxt[i-1];
        while(k>0&&t[k+1]!=t[i]) k=nxt[k];
        nxt[i]=(t[k+1]==t[i])?k+1:0;
    }
    return;
}

void KMP()
{
    f[1]=(s[1]==t[1])?1:0;
    for(int i=2;i<=sl;i++)
    {
        int k=f[i-1];
        while(t[k+1]!=s[i]&&k>0) k=nxt[k];
        f[i]=(t[k+1]==s[i])?k+1:0;
        if(f[i]==tl) printf("%d\n",i-tl+1);
    }
    return;
}

int main()
{
    scanf("%s%s",s,t);
    tl=strlen(t),sl=strlen(s);
    for(int i=tl;i>=1;i--) t[i]=t[i-1];
    for(int i=sl;i>=1;i--) s[i]=s[i-1];
    get(),KMP();
    for(int i=1;i<=tl;i++) printf("%d ",nxt[i]);
    return puts(""),0;
}

- manacher算法\(\mathcal O(n)\)

应该考不到吧,不复习了qwq。

- AC自动机(简单版)

- AC自动机(加强版)

- AC自动机(二次加强版)(都是 \(\mathcal O(size\sum len)\)

放到这里,表示我曾经会

- 其他

- 二分查找(\(\mathcal O(m\log n)\)

请自动忽略缩进问题/kk。

	//最后一个比他小的数 
	read(n),read(m);
	for(int i=1;i<=n;i++) read(a[i]);
	sort(a+1,a+n+1);
	for(int i=1;i<=m;i++)
	{
		read(x);
		int l=1,r=n,mid;
		while(l<r)
		{
			mid=(l+r)>>1;
			if(a[mid]>=x) r=mid;
			else l=mid+1;
		}
		if(a[l]>=x) l--;
		if(a[1]>=x) puts("NO!");
		else printf("%d\n",a[l]);
		cout<<endl;
	}
	return 0;

	//最后一个比他小或等于的数 
	read(n),read(m);
	for(int i=1;i<=n;i++) read(a[i]);
	sort(a+1,a+n+1);
	for(int i=1;i<=m;i++)
	{
		read(x);
		int l=1,r=n,mid;
		while(l<r)
		{
			mid=(l+r)>>1;
			if(a[mid]>x) r=mid;
			else l=mid+1;	
		}
		if(a[l]>x) l--;
		if(a[1]>x) puts("NO!");
		else printf("%d\n",a[l]); 
	}
	return 0;	
  	
   	//第一个比他大的数 
	read(n),read(m);
	for(int i=1;i<=n;i++) read(a[i]);
	sort(a+1,a+n+1,cmp);
	for(int i=1;i<=m;i++)
	{
		read(x);
		int l=1,r=n,mid;
		while(l<r)
		{
			mid=(l+r)>>1;
			if(a[mid]<=x) r=mid;
			else l=mid+1; 
		}
		if(a[l]<=x) l--;
		if(x>=a[1]) puts("NO!");
		else printf("%d\n",a[l]);
	}
	return 0;			
    
	//第一个比他大或等于的数 
	read(n),read(m);
	for(int i=1;i<=n;i++) read(a[i]);
	sort(a+1,a+n+1,cmp);
	for(int i=1;i<=m;i++)
	{
		read(x);
		int l=1,r=n,mid;
		while(l<r)
		{
			mid=(l+r)>>1;
			if(a[mid]<x) r=mid;
			else l=mid+1;
		}
		if(a[l]<x) l--;
		if(a[1]<x) puts("NO!");
		else printf("%d\n",a[l]);
	}
	return 0;

- 归并排序\(\mathcal O(n\log n)\)

#include"iostream"
#include"cstdio"
#include"cmath"
using namespace std;

#define MAXN 100005 

int n;
int a[MAXN],rt[MAXN];

void qsort(int l,int r)
{
	if(l==r) return;
	int mid=(l+r)>>1;
	qsort(l,mid),qsort(mid+1,r);
	int c=l-1,i=l,j=mid+1;
	while(i<=mid&&j<=r)
	{
		if(a[i]<=a[j]) rt[++c]=a[i++];
		else rt[++c]=a[j++];
	}
	while(i<=mid) rt[++c]=a[i++];
	while(j<=r) rt[++c]=a[j++];
	for(i=l;i<=r;i++) a[i]=rt[i]; 
}

int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	qsort(1,n);
	for(int i=1;i<=n;i++) printf("%d ",a[i]);
	return puts(""),0;
}

- 利用归并排序求逆序对\(\mathcal O(n\log n)\)

#include"iostream"
#include"cstdio"
#include"cmath"
using namespace std;

#define MAXN 500005 

int n;
int a[MAXN],rt[MAXN];
long long ans=0;

void qsort(int l,int r)
{
	if(l==r) return;
	int mid=(l+r)>>1;
	qsort(l,mid),qsort(mid+1,r);
	int c=l-1,i=l,j=mid+1;
	while(i<=mid&&j<=r)
	{
		if(a[i]<=a[j]) rt[++c]=a[i++];
		else rt[++c]=a[j++],ans+=(long long)(mid-i+1);
	}
	while(i<=mid) rt[++c]=a[i++];
	while(j<=r) rt[++c]=a[j++];
	for(i=l;i<=r;i++) a[i]=rt[i]; 
}

int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	qsort(1,n);
	printf("%lld\n",ans);
	return 0;
}

注:逆序对还可以利用树状数组来求,是一种二位偏序的思想,这里就不赘述了。

- 二维差分:

对于矩阵 \(\{a_{i,j}\}\),其查分数组计算方法为 \(\{p_{i,j}=a_{i,j}-a_{i-1,j}-a_{i,j-1}+a_{i-1,j-1}\}\)

同时还原出原数 \(\{a_{i,j}=\sum\limits_{k=1}^i\sum\limits_{l=1}^jp_{k,l}\}\)

可以类比一维差分与前缀和进行记忆。

快速修改。

比如说你要对 \((x_1,y_1):(x_2,y_2)\) 区间 \(+1\)

画图模拟一下就知道应该对差分数组实施:

\[p_{x_1,y_1}+1,p_{x_1,y_2+1}-1,p_{x_2+1,y_1}-1,p_{x_2+1,y_2+1}+1 \]

- 一个常用的结论

如这个题(Link),我们考虑把操作的分成两组考虑。

  1. 获得大于 \(0\)

显然按消耗值从小到大排序。

  1. 获得小于 \(0\)(消耗)

显然这种不能简单的贪心,记住下面这个结论:

按 消耗与需求的和从大到小排序。

总起来,当然先做获得大于 \(0\) 的操作。

- 模拟退火\(\mathcal O(\text{可能能过})\)

#include"iostream"
#include"cstdio"
#include"cmath"
#include"algorithm"
#include"ctime"
using namespace std;

double xo[1005],yo[1005],m[1005];
int n;
double ansx,ansy,x,y,X,Y;
double minx=10000000000.00;

double f(double xx,double yy)
{
    double ans=0;
    for(int i=1;i<=n;i++) ans+=sqrt((xx-xo[i])*(xx-xo[i])+(yy-yo[i])*(yy-yo[i]))*m[i];
    return ans;
}

void SA()
{
    double T=13000,delta=0.993,T0=1e-15;
    while(T>T0)
    {
        X=x+((rand()<<1)-RAND_MAX)*T;
        Y=y+((rand()<<1)-RAND_MAX)*T;
        double op=f(X,Y);
        if(op<minx) minx=op,ansx=x=X,ansy=y=Y;
        else if(exp((minx-op)/T)*RAND_MAX>rand()) x=X,y=Y;
    	T*=delta;
     }
}

void work(){while((double)clock()/CLOCKS_PER_SEC<0.9) SA();}

int main()
{
    srand(19260817),srand(time(0)),srand(rand()),srand(rand());
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%lf%lf%lf",&xo[i],&yo[i],&m[i]),x+=xo[i],y+=yo[i];
    x/=n,y/=n;
    work();
    return printf("%.3lf %.3lf\n",ansx,ansy),0;
}
posted @ 2020-10-29 23:03  童话镇里的星河  阅读(129)  评论(0编辑  收藏  举报