T1
有一个 n∗nn*nn∗n 的矩阵,每个格子有一个数,最开始在格子 (1,1)(1,1)(1,1),最终想要到达 (n,n)(n,n)(n,n),每一步只能往右或者往下走一格。
第 iii 行,第 jjj 列的数是 Ai∗Bjmod CA_i*B_j\mod CAi∗BjmodC,一条路线的价值是路线上所有数的和,我们想要求出最大的价值以及这条路线,如果有价值相同的路线,输出任意一组解。
n≤104,0≤Ai,Bi,C≤2∗104n\leq 10^4,0\leq A_i,B_i,C\leq 2*10^4n≤104,0≤Ai,Bi,C≤2∗104
2s2s2s ,2MB2MB2MB。
一开始想到直接用 bitsetbitsetbitset 存储整个图的状态,然后直接倒退,发现只能通过 60%60\%60% 的数据点,仔细思考一下发现可以用时间换空间,即倒着将行分成若干块,每一块的大小设为 BBB ,从后往前倒退,每次相当于只用处理行上一个前缀的答案,那么时间复杂度就变为 O(n3B)O(\frac {n^3} {B})O(Bn3),带一个 12\frac 1 221 的常数。
发现最大数据点还是超时,做了后面的题回来发现可以将行列同时分块,这样就省去了很多的时间复杂度,即将行列都按 BBB 分块,从最后一个块开始往前倒退,如果上一步在上面一块,那么下面的一整块在下一次Dp中都不需要管了,在左边的块同理。
最大的时候按照 B=3333B=3333B=3333 来划分可以做到大约 73n2\frac 7 3n^237n2 ,十分优秀。
正解直接考虑分治,令 solve(x,y,l,r)solve(x,y,l,r)solve(x,y,l,r) 表示从 (x,l)(x,l)(x,l) 到 (y,r)(y,r)(y,r) 的答案。
用 O((y−x)(r−l))O((y-x)(r-l))O((y−x)(r−l)) 的时间可以处理出来经过第 mid=(x+y)/2mid=(x+y)/2mid=(x+y)/2 行时最佳经过点 pospospos 。
然后划分成 solve(x,mid,l,pos)solve(x,mid,l,pos)solve(x,mid,l,pos) 和 solve(mid+1,y,pos,r)solve(mid+1,y,pos,r)solve(mid+1,y,pos,r) 发现这样的面积恰好是原来的一半,所以时间复杂度就是 O(n2)O(n^2)O(n2) 空间复杂度是 O(n)O(n)O(n) 。
#include<cstdio>
#include<bitset>
#include<algorithm>
using namespace std;
const int N=10010,T=3334;
int n,C,a[N],b[N],p[N],f[N];
char s[N<<1];
bitset<T> w[T];
int main(){
scanf("%d %d",&n,&C);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++) scanf("%d",&b[i]);
int t=0;
if(n<=T){
for(int i=1;i<=n;i++){
f[1]=f[1]+a[i]*b[1]%C;w[i-1][0]=0;
for(int j=2;j<=n;j++)
f[j]=(f[j]>f[j-1]?f[j]:(w[i-1][j-1]=1,f[j-1]))+a[i]*b[j]%C;
}
int x=n,y=n;
while(x!=1 || y!=1) s[++t]=(w[x-1][y-1]?(y--,'R'):(x--,'D'));
}
else{
int B=(n+T-1)/T;
for(int i=1;i<=B;i++) p[i]=n/B+(i<=n%B)+p[i-1];
int X=B,Y=B,x=n,y=n;
while(1){
for(int j=1;j<=y;j++) f[j]=0;
for(int i=1;i<=p[X-1];i++){
f[1]=f[1]+a[i]*b[1]%C;
for(int j=2;j<=y;j++)
f[j]=(f[j]>f[j-1]?f[j]:f[j-1])+a[i]*b[j]%C;
}
for(int i=p[X-1]+1;i<=x;i++) {
f[1]=f[1]+a[i]*b[1]%C;
if(p[Y-1]==0) w[i-p[X-1]-1][0]=0;
for(int j=2;j<=p[Y-1];j++) f[j]=(f[j]>f[j-1]?f[j]:f[j-1])+a[i]*b[j]%C;
for(int j=max(2,p[Y-1]+1);j<=y;j++)
f[j]=((f[j]>f[j-1])?(w[i-p[X-1]-1][j-p[Y-1]-1]=0,f[j]):(w[i-p[X-1]-1][j-p[Y-1]-1]=1,f[j-1]))+a[i]*b[j]%C;
}
while(x>p[X-1] && y>p[Y-1]){
if(x==1 && y==1) break;
if(w[x-p[X-1]-1][y-p[Y-1]-1]) y--,s[++t]='R';
else x--,s[++t]='D';
}
if(x==1 && y==1) break;
if(x<=p[X-1]) X--;
if(y<=p[Y-1]) Y--;
}
}
reverse(s+1,s+1+t);s[t+1]='\0';
printf("%s\n",s+1);
}
T2
给出一个 W∗HW*HW∗H 的网格,坐标从 (0,0)(0,0)(0,0) 到 (W−1,H−1)(W−1,H−1)(W−1,H−1)。有公共边的网格之间有 111 的边权。
有 nnn 个网格上有障碍,求所有非障碍点对间的最短路之和,保证非障碍点联通。
答案可能很大,对 109+710^9+7109+7 取模。
对于 100%100\%100% 的数据,保证N≤40,W,H≤106,0≤xi≤W−1,0≤yi≤H−1,N\leq 40,W,H\leq 10^6,0\leq x_i\leq W−1,0\leq y_i\leq H−1,N≤40,W,H≤106,0≤xi≤W−1,0≤yi≤H−1, ,障碍点之间两两不同且非障碍点两两联通。
直接考虑划分成 (2n+1)∗(2n+1)(2n+1)*(2n+1)(2n+1)∗(2n+1) 个块,块内的最短路肯定没错,块间的最短路跑一下 bfsbfsbfs 。设块 aaa 到块 bbb 的最短路是 ddd ,但是曼哈顿距离为 p≤dp\leq dp≤d,那么块 aaa 的每一个点到块 bbb 的每一个点的最短路都比曼哈顿距离多了 d−pd-pd−p 。
证明还是挺简单的,因为障碍点都是 1∗11*11∗1 的。
#include<bits/stdc++.h>
#define pii pair<int,int>
#define fi first
#define se second
#define mp make_pair
using namespace std;
const int N=100,mod=1000000007;
int n,w,h;
bool tf[N][N];
map<pair<int,int>,bool> p;
int a[N<<1],b[N<<1],xx[N],yy[N],ta,tb;
bool vis[N][N];
int fx[4]={-1,1,0,0};
int fy[4]={0,0,-1,1},ans,st,ed;
struct node{
int x,y,d;
}qs[N*N];
void bfs(int X,int Y){
qs[st=ed=1]=(node){X,Y,0};
for(int i=1;i<=ta;i++) for(int j=1;j<=tb;j++) vis[i][j]=false;
vis[X][Y]=true;
while(st<=ed){
int x=qs[st].x,y=qs[st].y,d=qs[st].d;st++;
for(int i=0;i<4;i++){
int xx=x+fx[i],yy=y+fy[i],dd=d+1;
if(xx>=1 && xx<=ta && yy>=1 && yy<=tb && !tf[xx][yy] && !vis[xx][yy]);
else continue;
vis[xx][yy]=true;
qs[++ed]=(node){xx,yy,dd};
if(X<xx || X==xx && Y<yy) ans=(ans+1ll*(dd-abs(X-xx)-abs(Y-yy))*(a[X]-a[X-1])%mod*(b[Y]-b[Y-1])%mod*(a[xx]-a[xx-1])%mod*(b[yy]-b[yy-1]))%mod;
}
}
}
int main(){
scanf("%d %d",&w,&h);
scanf("%d",&n);
for(int i=1;i<w;i++) ans=(ans+1ll*h*h%mod*(w-i)%mod*i)%mod;
for(int i=1;i<h;i++) ans=(ans+1ll*w*w%mod*(h-i)%mod*i)%mod;
for(int i=1;i<=n;i++) {
scanf("%d %d",&xx[i],&yy[i]);
xx[i]++;yy[i]++;
p[mp(xx[i],yy[i])]=true;
}
int tot=0;
for(int i=1;i<=n;i++) for(int j=1;j<i;j++) ans=(ans+abs(xx[i]-xx[j])+abs(yy[i]-yy[j]))%mod;
sort(xx+1,xx+1+n);
sort(yy+1,yy+1+n);
xx[n+1]=w;yy[n+1]=h;
for(int i=1;i<=n+1;i++) if(xx[i]!=xx[i-1]){
if(xx[i]>xx[i-1]+1) a[++ta]=xx[i]-1;
a[++ta]=xx[i];
}
for(int i=1;i<=n+1;i++) if(yy[i]!=yy[i-1]){
if(yy[i]>yy[i-1]+1) b[++tb]=yy[i]-1;
b[++tb]=yy[i];
}
for(int i=1;i<=ta;i++)
for(int j=1;j<=tb;j++) if(a[i]-a[i-1]==1 && b[j]-b[j-1]==1 && p[mp(a[i],b[j])]){
tf[i][j]=true;
ans=(ans+mod-(1ll*(a[i]-1)*a[i]/2+1ll*(w-a[i]+1)*(w-a[i])/2)%mod*h%mod+mod-(1ll*(b[j]-1)*b[j]/2+1ll*(h-b[j])*(h-b[j]+1)/2)%mod*w%mod)%mod;
}
for(int i=1;i<=ta;i++)
for(int j=1;j<=tb;j++) if(!tf[i][j])
bfs(i,j);
printf("%d\n",ans);
}
T3
有一个 nnn 个点的有向图,对于所有节点(除了 nnn),都恰好有两条出边,一条红色,一条蓝色。这些点在任意时刻都有恰好一条边是活动的,最开始蓝色边是活动的。开始节点 111 有一个棋子,每次棋子将当前点的活动边切换,并沿着活动边走向下一个节点,直到棋子走到 nnn 节点为止。本题保证所有点可以通过边走到 nnn(即将这个图视为一般有向图能到达)。
有 qqq 个查询,每次查询给出一个状态,状态形如一个二元组 (v,s)(v,s)(v,s),其中 vvv 是个一个数表示当前所在位置,sss 是一个长度为 n−1n−1n−1 的字符串表示当前活动边的颜色(BBB 表示蓝色,RRR 表示红色)。你需要回答这个状态第一次出现的时间,或者判定这个状态不可能出现,请注意,棋子切换活动边之后,再沿着边走(当然操作仍然是同时发生的)。
对于 100%100\%100% 的数据,n≤58,q≤5000,1≤v≤n−1n\leq 58,q\leq 5000,1\leq v\leq n−1n≤58,q≤5000,1≤v≤n−1.
神仙线性代数。
首先要学会构造未知数 xi,yix_i,y_ixi,yi ,表示 iii 点走了 xix_ixi 次红边,yiy_iyi次蓝边。
根据状态 sss 我们可以知道 yi=xi−[si=R]y_i=x_i-[s_i=R]yi=xi−[si=R] ,因此可以用 xix_ixi 表示 yiy_iyi。同时根据所在点 vvv 我们可以知道每个点出度和入度的关系,因此可以列出 n−1n-1n−1 条方程,写成矩阵的形式可以得到 Ax=wAx=wAx=w ,其中 AAA 矩阵在每一次询问中其实是不会改变的,那么可以得到 x=A−1wx=A^{-1}wx=A−1w,所以先将 AAA 的逆矩阵求出来,在询问中就能通过 wiw_iwi 快速计算 xix_ixi 。
注意当计算矩阵的逆时,初等行(列)变换只有三种:
1.给一行(列)乘上某个非零数 kkk 。
2.给一行(列)减去另一行(列)的 k≠0k\not =0k=0 倍。
3.交换两行(列)。
可以证明这个 AAA 必定有逆。

然后将所有的 xi,yix_i,y_ixi,yi 加起来就是答案了,由于答案可能很大,所以模两个大质数然后用中国剩余定理合并即可。
考虑如何判无解?首先 xi,yi≥0x_i,y_i\geq 0xi,yi≥0,其次对于 xi≠0x_i\not = 0xi=0的点(除了vvv),最后一条活动边必定为以 vvv 为根的内向树,必要性显然。
充分性可以分成两种情况:
1.vvv 的 xi,yix_i,y_ixi,yi 均为 000,那么入活动边只有恰好一条(由出入度关系保证,没有入活动边当且仅当就是第一步,无需证明),直接回退即可。
2.vvv 有上一条活动边,由于所有经过的点都指向了 vvv ,vvv 的这条活动边指向的一定是一个曾经经过的点,否则出入度条件矛盾,那么回退到那个点所在的子树即可,状态唯一,其次满足归纳。
本文探讨了一种动态规划方法解决矩阵路径问题,利用分治和矩阵逆运算优化求解最大价值路线。还涉及了二维网格上的最短路径问题,通过划分区域并结合BFS求解,以及有向图中棋子移动状态的判断。关键点包括时间空间优化、矩阵逆和状态转移方程的应用。
896

被折叠的 条评论
为什么被折叠?



