1. 程式人生 > 實用技巧 >7.15

7.15

7.15 dp專題

T1.sum 小Z愛求和

https://www.luogu.com.cn/problem/T138967

無腦暴力騙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 關押罪犯

https://www.luogu.com.cn/problem/T138969

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.luogu.com.cn/problem/T138970

補卡特蘭數:寫到組合數學裡了https://www.cnblogs.com/liukx/p/13323828.html

LGV定理

LGV定理用於解決路徑不相交問題。

詳見賈隊部落格和wiki

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

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