题意比较迷惑,最后转化下来是,
确定一个树的点与父亲的排列,使得所求式总代价最小,
即求一棵最小生成树,点i和点j连接的代价是popcount(a[i]^a[j])
即ai和aj异或的值的二进制位的个数,
其中n<=2e5,0<=ai<2^18,ai两两不同
考虑将n个出现的值ai,把i视为祖先,多源bfs
对于每个[0,2^18)的值确定其祖先,然后考虑枚举扰动边,
枚举j从0到18,两个popcount之差1的相邻点,
如果祖先不同,就将这两个祖先连一条边,
考虑到a变到b的变化过程,
一定是从a,变到最远的离a最近的点,再变一次变到最远的离b最近的点,再变到b的
最后边的数量是(2^18)*18级别的,作Kruskal最小生成树即可
求出最小生成树后建树,dfs一遍即可确定点和父亲的对应关系的排列
由于根节点没有父亲,在父亲对应的排列里给父亲赋一个[1,n]的值即可
#include
using namespace std;
#define pb push_back
typedef pair P;
const int N=2e5+10,M=(1<<18)+5,INF=0x3f3f3f3f;
int t,n,m,a[N],bit[M],dis[M],par[M],fa[M];
int b[N],p[N],c;
bool vis[M];
vectore[N];
queueq;
struct edge{
int u,v,w;
}g[M*18];
bool operator<(edge a,edge b){
return a.w>1]+(i&1);
}
scanf("%d",&t);
while(t--){
scanf("%d",&n);
c=m=0;
for(int i=0;i
n*m(n<=1e3,m<=1e3,sum n*m<=7e6)的矩形,
点(i,j)上有一个值a(i,j)(-1e9<=a(i,j)<=1e9),表示这个点可以提供的氧气量,
如果为正,代表安全地区,
人的肺内氧气量在这里会加a(i,j),这里认为肺的容氧量是无穷的
如果为负数,则表示这个点不能提供氧气量,为毒气地区,
一个人想从(1,1)走到(n,m),保证a(1,1)>0,a(n,m)>0
每一秒只能向右或向下或向右下走一格,
即可以从(x,y)走到(x,y+1)/(x+1,y)/(x+1,y+1),
特别地,一个人可以选择在安全地区憋气,并耗掉u(u<=1e9)的代价,
憋气时间可以长达k(k<=1e9)秒,
在憋气时间内可以穿越毒气地区,并最终在安全地区松气,
求能安全到达(n,m)所剩下的最大氧气量
考虑到某个点的dp值需要从左上角长度为k的正方形转移而来,
于是需要用到二维单调队列,
赛中夏老师想出二维单调队列的想法的时候,
距比赛结束还有18min,于是写不完了gg
而且这个二维单调队列不是静态的,需要一边扫一边维护
静态的则可以先横着扫一遍扫完再竖着扫一遍扫完
首先由于氧气量是1e9级别的,就放在dp数组里进行维护,
dp[i][j]表示到(i,j)这个点能剩余的最大氧气量,初始化-1e18
ans[i][j]维护[dp[i][j-k],dp[i][j]]这一段长度为k的滑动窗口的最大dp值
分三种情况讨论,
第一种是左上角的格子,dp[1][1]=a[1][1]
第二种是大于0的格子,这个时候可以更新这个点的dp值,
dp值可以直接从dp[i-1][j-1]、dp[i-1][j]、dp[i][j-1]三个正dp值处直接转移而来
也可以左上角一个k*k的正方形处转移而来,
即横向的ans[i][j],和纵向的单调队列维护的窗口为k的ans队列
注意先用不带dp[i][j]的ans[i][j]更新dp[i][j],再用dp[i][j]更新ans[i][j]
第三种是小于0的格子,
这个时候仍然需要维护这个点左边长为k的区间的最大dp值,即ans[i][j]
样例很良心,本来一开始脑补的时候,忽略了第三种情况
#include
using namespace std;
typedef long long ll;
const int N=1e3+10;
int n,m,k,u,a[N][N];
ll dp[N][N],ans[N][N];
dequerow[N],col[N];
int main(){
while(~scanf("%d%d%d%d",&n,&m,&k,&u)){
for(int i=1;i<=n;++i){
dp[i][0]=-1e18;
while(!row[i].empty())row[i].pop_front();
}
for(int j=1;j<=m;++j){
dp[0][j]=-1e18;
while(!col[j].empty())col[j].pop_front();
}
for(int i=1;i<=n;++i){
for(int j=1;j<=m;++j){
scanf("%d",&a[i][j]);
dp[i][j]=-1e18;ans[i][j]=-1e18;
while(!row[i].empty() && row[i].front()0){
if(!row[i].empty()){
dp[i][j]=max(dp[i][j],dp[i][row[i].front()]-u+a[i][j]);
ans[i][j]=max(ans[i][j],dp[i][row[i].front()]);
}
if(!col[j].empty()){
dp[i][j]=max(dp[i][j],ans[col[j].front()][j]-u+a[i][j]);
}
dp[i][j]=max(dp[i][j],dp[i-1][j-1]+a[i][j]);
dp[i][j]=max(dp[i][j],dp[i-1][j]+a[i][j]);
dp[i][j]=max(dp[i][j],dp[i][j-1]+a[i][j]);
ans[i][j]=max(ans[i][j],dp[i][j]);
}
else{
if(!row[i].empty()){
ans[i][j]=max(ans[i][j],dp[i][row[i].front()]);
}
}
if(dp[i][j]>=u){
while(!row[i].empty() && dp[i][j]>=dp[i][row[i].back()])row[i].pop_back();
row[i].push_back(j);
}
if(ans[i][j]>=u){
while(!col[j].empty() && ans[i][j]>=ans[col[j].back()][j])col[j].pop_back();
col[j].push_back(i);
}
//printf("i:%d j:%d dp:%lld\n",i,j,dp[i][j]);
}
}
printf("%lld\n",dp[n][m]>0?dp[n][m]:-1);
}
return 0;
}
z*x*y(z<=100,x<=100,y<=100),多组样例,sum z*x*y<=6e6,
一个三维的结构,每一层是一个矩阵,矩阵里有三种字符,@.*,
@代表邮局,.代表空地,*代表用户,每个用户会选择到自己家最近的邮局
距离定义为曼哈顿距离,
现在需要确定一个新邮局,建在空地上,
使得每个用户到最近的邮局的最大距离最小,输出最大距离
保证输入里至少有一个邮局和一个空地,
最大距离最小,考虑二分答案距离x,然后对邮局进行多源bfs,
找到所有用户,忽略到邮局距离不超过x的用户,然后考虑剩下的用户,
问题转化成,是否存在一个空地,对给定的用户的曼哈顿距离均不超过x
即最大n维曼哈顿距离问题
最大n维曼哈顿距离是一个经典问题,可以参考poj2926
利用了dp的松弛思想,即abs(x0-x1)=max(x0-x1,x1-x0)
abs(x0-x1)+abs(y0-y1)=max(x0-x1,x1-x0)+max(y0-y1,y1-y0)
=max(x0-x1+y0-y1,x0-x1+y1-y0,x1-x0+y0-y1,x1-x0-y1-y0)
=max(x0+y0-(x1+y1),x0-y0-(x1-y1),-x0+y0-(-x1+y1),-x0-y0-(-x1-y1)
三维同理,是八个值的最大值,
且如果枚举的(x0,y0)的当前符号固定,减去的值的符号(x1,y1)也与其相同
所以考虑所有可能的(x1,y1),用数组维护八维(x1,y1)的最小值,
对于所有的(x0,y0)插进去询问即可
这里,(x1,y1)先插入所有当前剩下的邮局,(x0,y0)则枚举所有的空地,
可以将sum=6e6认为是1e6的样例6组,
复杂度O(6*1e6*log300*8),实际跑不满,2s跑了1.3s
plus:如果是最小曼哈顿距离的话,则需用到三维树状数组,
这个时候偏序关系就必须发挥作用了,维护八棵树,分别询问,
可以参考2019年牛客多校第八场D.distance
#include
using namespace std;
const int N=105,INF=0x3f3f3f3f;
int z,x,y,dis[N][N][N],mn[8];
struct node{
int a,b,c;
};
vectorsta,usr,spa;
char s[N][N][N];
queueq;
bool leg(int a,int b,int c){
return a>=0 && a=0 && b=0 && cmid && s[a][b][c]=='*'){
usr.push_back({a,b,c});
}
if(leg(a+1,b,c)){
dis[a+1][b][c]=dis[a][b][c]+1;
q.push({a+1,b,c});
}
if(leg(a-1,b,c)){
dis[a-1][b][c]=dis[a][b][c]+1;
q.push({a-1,b,c});
}
if(leg(a,b+1,c)){
dis[a][b+1][c]=dis[a][b][c]+1;
q.push({a,b+1,c});
}
if(leg(a,b-1,c)){
dis[a][b-1][c]=dis[a][b][c]+1;
q.push({a,b-1,c});
}
if(leg(a,b,c+1)){
dis[a][b][c+1]=dis[a][b][c]+1;
q.push({a,b,c+1});
}
if(leg(a,b,c-1)){
dis[a][b][c-1]=dis[a][b][c]+1;
q.push({a,b,c-1});
}
}
if(!usr.size()){
return 1;
}
memset(mn,INF,sizeof mn);
for(int i=0;i
在队友的carry下9/13,以为很勇,
赛后被放到gym重现了,还删了一个签到题,这样就是8/12,
然而,神仙群友纷纷9/12,10/12,11/12异常神勇,radewoosh甚至单人3.5hAKorz
F题好像是一个O(n^5)的直接模拟,不想补了gg