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