T1:
题解:
不难发现是个状压dp,其实我们只需要维护这四个格子的值进行转移就好了
k为状压,值为1的时候代表这个数字还没有取
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int map[2010][2010];
char s[2010];
int h,w;
int f[2010][2010][2][2][2][2];
int main()
{
int i,j,k,l,ans=0x3f3f3f3f;
scanf("%d%d",&h,&w);
for(i=1;i<=h;i++){
scanf("%s",s+1);
for(j=1;j<=w;j++)
if(s[j]!='.')
map[i][j]=s[j]-'0';
}
memset(f,0x3f,sizeof(f));
if(map[1][2]&&map[2][1]){
f[1][1][1][0][1][1]=map[1][1]+map[2][1];
f[1][1][1][1][0][1]=map[1][1]+map[1][2];
}
if(!map[1][2]||!map[2][1])
f[1][1][1][1][1][1]=map[1][1];
for(i=1;i<=h;i++)
for(j=1;j<=w;j++)
for(k=0;k<16;k++)
if(f[i][j][k&1][(k>>1)&1][(k>>2)&1][(k>>3)&1]<0x3f3f3f3f){
l=(map[i+1][j+1]!=0)+(map[i+2][j]!=0)+((k&1)&&(map[i+1][j-1]!=0));
if(l==3){
f[i+1][j][1][0][0][(k>>2)&1]=min(f[i+1][j][1][0][0][(k>>2)&1],f[i][j][k&1][(k>>1)&1][(k>>2)&1][(k>>3)&1]+map[i+1][j]*((k>>1)&1)+map[i+1][j+1]+map[i+2][j]);
f[i+1][j][1][1][0][(k>>2)&1]=min(f[i+1][j][1][1][0][(k>>2)&1],f[i][j][k&1][(k>>1)&1][(k>>2)&1][(k>>3)&1]+map[i+1][j]*((k>>1)&1)+map[i+1][j+1]+map[i+1][j-1]);
f[i+1][j][1][0][1][(k>>2)&1]=min(f[i+1][j][1][0][1][(k>>2)&1],f[i][j][k&1][(k>>1)&1][(k>>2)&1][(k>>3)&1]+map[i+1][j]*((k>>1)&1)+map[i+2][j]+map[i+1][j-1]);
}
if(l==2){
if(map[i+1][j-1]&&(k&1))
f[i+1][j][1][1][1][(k>>2)&1]=min(f[i+1][j][1][1][1][(k>>2)&1],f[i][j][k&1][(k>>1)&1][(k>>2)&1][(k>>3)&1]+map[i+1][j]*((k>>1)&1)+map[i+1][j-1]);
if(map[i+2][j])
f[i+1][j][1][0][1][(k>>2)&1]=min(f[i+1][j][1][0][1][(k>>2)&1],f[i][j][k&1][(k>>1)&1][(k>>2)&1][(k>>3)&1]+map[i+1][j]*((k>>1)&1)+map[i+2][j]);
if(map[i+1][j+1])
f[i+1][j][1][1][0][(k>>2)&1]=min(f[i+1][j][1][1][0][(k>>2)&1],f[i][j][k&1][(k>>1)&1][(k>>2)&1][(k>>3)&1]+map[i+1][j]*((k>>1)&1)+map[i+1][j+1]);
}
if(l<=1)
f[i+1][j][1][1][1][(k>>2)&1]=min(f[i+1][j][1][1][1][(k>>2)&1],f[i][j][k&1][(k>>1)&1][(k>>2)&1][(k>>3)&1]+map[i+1][j]*((k>>1)&1));
l=(map[i+1][j+1]!=0)+(map[i][j+2]!=0)+(((k>>3)&1)&&(map[i-1][j+1]!=0));
if(l==3){
f[i][j+1][(k>>1)&1][0][0][1]=min(f[i][j+1][(k>>1)&1][0][0][1],f[i][j][k&1][(k>>1)&1][(k>>2)&1][(k>>3)&1]+map[i][j+1]*((k>>2)&1)+map[i+1][j+1]+map[i][j+2]);
f[i][j+1][(k>>1)&1][0][1][1]=min(f[i][j+1][(k>>1)&1][0][1][1],f[i][j][k&1][(k>>1)&1][(k>>2)&1][(k>>3)&1]+map[i][j+1]*((k>>2)&1)+map[i+1][j+1]+map[i-1][j+1]);
f[i][j+1][(k>>1)&1][1][0][1]=min(f[i][j+1][(k>>1)&1][1][0][1],f[i][j][k&1][(k>>1)&1][(k>>2)&1][(k>>3)&1]+map[i][j+1]*((k>>2)&1)+map[i][j+2]+map[i-1][j+1]);
}
if(l==2){
if(map[i-1][j+1]&&((k>>3)&1))
f[i][j+1][(k>>1)&1][1][1][1]=min(f[i][j+1][(k>>1)&1][1][1][1],f[i][j][k&1][(k>>1)&1][(k>>2)&1][(k>>3)&1]+map[i][j+1]*((k>>2)&1)+map[i-1][j+1]);
if(map[i][j+2])
f[i][j+1][(k>>1)&1][1][0][1]=min(f[i][j+1][(k>>1)&1][1][0][1],f[i][j][k&1][(k>>1)&1][(k>>2)&1][(k>>3)&1]+map[i][j+1]*((k>>2)&1)+map[i][j+2]);
if(map[i+1][j+1])
f[i][j+1][(k>>1)&1][0][1][1]=min(f[i][j+1][(k>>1)&1][0][1][1],f[i][j][k&1][(k>>1)&1][(k>>2)&1][(k>>3)&1]+map[i][j+1]*((k>>2)&1)+map[i+1][j+1]);
}
if(l<=1)
f[i][j+1][(k>>1)&1][1][1][1]=min(f[i][j+1][(k>>1)&1][1][1][1],f[i][j][k&1][(k>>1)&1][(k>>2)&1][(k>>3)&1]+map[i][j+1]*((k>>2)&1));
}
for(k=0;k<16;k++)
if(ans>f[h][w][k&1][(k>>1)&1][(k>>2)&1][(k>>3)&1])
ans=f[h][w][k&1][(k>>1)&1][(k>>2)&1][(k>>3)&1];
printf("%d\n",ans);
return 0;
}
T2:
题解:
n个珠子用一条线穿起来,手玩一下不难发现是2^n种
那么f[i][j]表示前i个珠子分成j组的方案数,那么
f[i][j]=∑i−1k=1f[k][j−1]∗2i−k+1
f
[
i
]
[
j
]
=
∑
k
=
1
i
−
1
f
[
k
]
[
j
−
1
]
∗
2
i
−
k
+
1
然后我们用超群的智慧打出一张表,up上午已经把表删了现在直接粘别人的了
联系一下f[i][j]会和f[i-1][j]/f[i-1][j-1]/f[i][j-1]发生奇奇怪怪的关系
我们找到了!
f[i][j]=f[i−1][j−1]+2∗f[i−1][j]
f
[
i
]
[
j
]
=
f
[
i
−
1
]
[
j
−
1
]
+
2
∗
f
[
i
−
1
]
[
j
]
然后可以O(n^2)预处理+O(1)回答询问可以完美解决这个问题。
代码:
#include<cstdio>
#define LL long long
using namespace std;
const int N=5000;
const int mod=104857601;
LL f[N+5][N+5];
int main()
{
freopen("b.in","r",stdin);
freopen("b.out","w",stdout);
f[1][1]=1; f[2][1]=2;
LL all=4;
for (int i=3;i<=N;i++) f[i][1]=all,all=(all+f[i][1])%mod;
for (int i=2;i<=N;i++)
for (int j=i;j<=N;j++)
f[j][i]=(f[j-1][i-1]+2LL*f[j-1][i]%mod)%mod;
int T;scanf("%d",&T);
while(T--)
{
int n,m;scanf("%d%d",&n,&m);
if(n==0) {printf("0\n");continue;}
if(m>n) {printf("0\n");continue;}
if(m==n) {printf("1\n");continue;}
printf("%lld\n",f[n][m]);
}
}
T3:
题解:
为什么我不截数据范围呢?因为这是一道结论规律题啊
规律就是答案是区间内的最大值*2
我们有科学严谨的证明
首先,加入别的数没有我们找到的数的最高位“1”,那么在&上我们可以领先一个最高位,在|上即使后面的全都是1也无法弥补
然后,假如最高位上有“1”,(这个有1只能是全是1,如果有一个0的话可以去看证明第一步。),我们肯定会在&上舍弃一个相对的高位,这样后面全|回来也没用
相等?相等不就是本身*2吗。
证毕
我们可以用RMQ实现O(1)回答询问
代码:
#include <cmath>
#include <cstdio>
#include <iostream>
#define sz 19
#define LL long long
using namespace std;
const int N=36005;
LL maxx[N][sz];int n,m;
void rmq()
{
for (int j=1;j<sz;j++)
for (int i=1;i<=n;i++)
if (i+(1<<j)-1<=n)
maxx[i][j]=max(maxx[i][j-1],maxx[i+(1<<j-1)][j-1]);
}
int main()
{
freopen("c.in","r",stdin);
freopen("c.out","w",stdout);
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++) scanf("%lld",&maxx[i][0]);
rmq();LL ans=0;
while (m--)
{
LL x,y;scanf("%lld%lld",&x,&y);
LL l=(x+ans)%n+1,r=(y+ans)%n+1;
if (l>r) swap(l,r); ans=0;
int k=log2(r-l+1);
ans=2LL*max(maxx[l][k],maxx[r-(1<<k)+1][k]);
printf("%lld\n",ans);
}
}