7.15

7.15 dp专题

T1.sum小Z爱求和

无脑暴力骗20...

正解

$ n^2 $ 个元素,挨个统计是 O($ n^2 $) ,所以统计每个点的贡献 复杂度降到O(n)

对于每个点,他可能贡献的区间是 L= pre(k-1) 前跳 k-1 个前驱 R=nxt ———— L=pre R=nxt(k-1) 后跳 k-1 个后继

然后链表维护——链表比较好维护删除,不好维护插入,所以我们先按把原序列搞成链表,然后从小到大(大到小同理,reverse一下即可) 考虑

总复杂度 O(n*k)

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
inline 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-'0';ch=getchar();}
    return x*f; 
}
typedef long long LL;
const LL mod = 998244353;
const int N = 1e6+10;
int n,k,a[N];
int pre[N],nxt[N],pos[N];
LL ans;
bool cmp(int i,int j){
    return a[i]<a[j];
}
void calc(){
    for(int i=1;i<=n;i++) pre[i]=i-1,nxt[i]=i+1;
    for(int i=1;i<=n;i++){
        int c=1,x=pos[i],L=pos[i],R=pos[i];
        for(;c<k;c++) 
            if(pre[L]) L=pre[L]; 
            else break;
        for(;c<k;c++)
            if(nxt[R]!=n+1) R=nxt[R];
            else break;
        if(c==k){
            while(L<=x){
                if(R==n+1)break;
                ans+=a[x]*1LL*(nxt[R]-R)%mod*1LL*(L-pre[L])%mod;
                ans%=mod;
                L=nxt[L];R=nxt[R];
            }
        }
        pre[nxt[x]]=pre[x];
        nxt[pre[x]]=nxt[x];
    }
}

int main(){
    n=read();k=read();
    for(int i=1;i<=n;i++) a[i]=read();
    for(int i=1;i<=n;i++) pos[i]=i;
    sort(pos+1,pos+1+n,cmp);
    calc();
    reverse(pos+1,pos+1+n);
    calc();
    printf("%lld\n",ans);
    return 0;
}

T2.prison 关押罪犯

myself

这个当时只能想到贪心。。。然后没搞出反例,就...

好吧骗了70 so lucky

然后这个显然贪心是不正确的,可能有的人在前面的组不是最优

人生第一次打出状压不容易。。。没满也值得纪念,放这待着吧

    for(int i=1,x,y;i<=m;i++) {
        x=read(),y=read();
        s[x]|=(1<<(y-1));
        s[y]|=(1<<(x-1));
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=ans;j++){
            if((f[j]&(1<<(i-1)))==0){
                vis[i]=1;
                f[j]|=s[i];
                break;
            }else{
                if(num[j]<k){
                    vis[i]=1;f[j]|=s[i];num[j]++;
                    break;
                }
            }
        }
        if(!vis[i]){
            ans++;f[ans]|=s[i];vis[i]=1;
        }
    }
    printf("%d\n",ans);
    return 0;
}

正解!!!

先预处理 ok[ ]表示此状态有矛盾的能不能小于 k 个人

然后状压枚举子集,转移也很好想 $ f[s]=min(f[s],f[s-ss]+1); $ 把子集分出来一组,然后组数 +1 ,原来状态减去这个子集

#include <cstdio>
#include <iostream>
using namespace std;
inline 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-'0';ch=getchar();}
    return x*f; 
}
bool ok[1<<17],g[17][17];
int f[1<<17],n,m,k,tmp;
int main(){
    n=read();m=read();k=read();
    for(int i=1,x,y;i<=m;i++){
        x=read()-1;y=read()-1;
        g[x][y]=g[y][x]=1;
    }
    for(int s=0;s<(1<<n);s++){
        for(int i=0;i<n;i++){
            if((s>>i)&1){
                for(int j=i+1;j<n;j++){
                    if((s>>j)&1)
                        if(g[i][j])
                            tmp++;
                }
            }
        }
        if(tmp<=k)ok[s]=1;
        tmp=0;
    }
    for(int s=1;s<(1<<n);s++){
        f[s]=n;//取min用的,先赋个最大值
        for(int ss=s;ss>0;ss=(ss-1)&s){
            if(ok[ss])
                f[s]=min(f[s],f[s-ss]+1);
        }
    }    
    printf("%d\n",f[(1<<n)-1]);
    return 0;
}

T3maze

补卡特兰数:写到组合数学里了https://www.cnblogs.com/liukx/p/13323828.html

LGV定理

LGV定理用于解决路径不相交问题。

详见贾队博客和wiki

https://www.luogu.com.cn/blog/jzp1115/lgv-ding-li

image-20200716184743199

$ ans=Calculate(2,1,n,m-1)Calculate(1,2,n-1,m)-Calculate(2,1,n-1,m)Calculate(1,2,n,m-1); $

​ 绿到绿 * 红到红 (同时走显然没相交) - 绿到红 * 红到绿

#include <cstdio>
#include <cstring>
#include <iostream>
#define M 2020
#define p 1000000007
using namespace std;
int n,m;
char map[M][M];
long long f[M][M];
long long Calculate(int x1,int y1,int x2,int y2){
	int i,j;
	if(map[x1][y1]=='1')return 0;
	memset(f,0,sizeof f);
	f[x1][y1]=1;
	for(i=x1;i<=x2;i++)
		for(j=y1;j<=y2;j++){
			if(map[i][j]=='1')continue;
			(f[i][j]+=f[i-1][j]+f[i][j-1])%=p;
		}
	return f[x2][y2];
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
		scanf("%s",map[i]+1);
	long long ans=Calculate(2,1,n,m-1)*Calculate(1,2,n-1,m)-Calculate(2,1,n-1,m)*Calculate(1,2,n,m-1);
    printf("%lld\n",(ans%p+p)%p);
	return 0;
}
posted @ 2020-08-14 00:18  ke_xin  阅读(117)  评论(0编辑  收藏  举报