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