省流:\(40 + 85 + 48 + 0\)。逆天绿紫黑黑。不能再挂分了,t1 \(100 \to 40\),t2 \(100 \to 85\),t3 \(84 \to 48\)。
T1
给一个 \(n \times m\) 的网格图,每个点只能是 #
或 .
或 S
或 T
,若这个点为 #
则这个点是障碍,不能到达,若是 .
则是空地,可以到达,S
是起点,T
是终点。每次你可以走四联通花费 \(1\) 的代价或者到达任意一个曼哈顿距离至多 \(k\) 的点,花费 \(t\) 的代价,求最小代价从起点走到终点。
\(n,m \leq 2000,k \leq 8\)。
由于正解的 \(\Theta(nmk^2)\) 非常的不牛且被赛时的我认为过不了,所以写一下我的赛时爆标做法,虽然赛时写挂了。
我们可以给每个点赋予一个势能,势能的大小代表我这个点可以无代价的走几步。那么一个无势能的点可以花费 \(t\) 的代价使它的势能变为 \([1,k]\) 的任意一个(因为至多 \(k\) 不是恰好 \(k\)),还可以不消耗势能花费 \(1\) 的代价走到四联通且是空地的点。一个势能为 \(1\) 的点则可以无代价的走到四联通的是空地的点,消耗 \(1\) 的势能。若势能大于 \(1\),则可以走到四联通的任意一个点,消耗 \(1\) 的势能。发现边权只有 \(0,1,t\) 三种,所以可以用三个队列模拟优先队列,以优化掉 dij 的 \(\log\),时间复杂度 \(\Theta(nmk)\),由于常数较大导致它跑的没有 \(\Theta(nmk^2)\) 快。
代码:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2005;
char ch[N][N];
int n,m,k,t,xs,ys,xt,yt,dis[N][N][10],dx[]={0,0,0,1,-1},dy[]={0,1,-1,0,0};
struct node {
int x,y,z,w;
bool operator<(const node &o) const {return w<o.w;}
};queue<node> q[3];
bool check() {
for(int i=1; i<=n; i++) for(int j=1; j<=m; j++) if(ch[i][j]=='#') return false;
return true;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr),cout.tie(nullptr);
cin>>n>>m>>k>>t;
for(int i=1; i<=n; i++) {
for(int j=1; j<=m; j++) {
cin>>ch[i][j];
if(ch[i][j]=='S') xs=i,ys=j;
if(ch[i][j]=='T') xt=i,yt=j;
}
}
if(check()) {
int dist=abs(xs-xt)+abs(ys-yt);
cout<<min({(int)floor(1.0*dist/k)*(t-k)+dist,(int)ceil(1.0*dist/k)*t,dist});
return 0;
}
memset(dis,0x3f,sizeof(dis));
dis[xs][ys][0]=0;
q[0].push((node){xs,ys,0,0});
while(!q[0].empty()||!q[1].empty()||!q[2].empty()) {
int pos;
node u=(node){0,0,0,0x3f3f3f3f3f3f3f3f};
for(int i=0; i<3; i++) {
if(q[i].empty()) continue;
if(q[i].front()<u) u=q[i].front(),pos=i;
}
q[pos].pop();
int x=u.x,y=u.y,z=u.z;
if(z==0) {
for(int i=1; i<=k; i++) {
if(dis[x][y][i]>dis[x][y][z]+t) {
dis[x][y][i]=dis[x][y][z]+t;
q[2].push((node){x,y,i,dis[x][y][i]});
}
}
for(int i=1; i<=4; i++) {
int tx=x+dx[i],ty=y+dy[i];
if(tx<1||tx>n||ty<1||ty>m||ch[tx][ty]=='#') continue;
if(dis[tx][ty][0]>dis[x][y][0]+1) {
dis[tx][ty][0]=dis[x][y][0]+1;
q[1].push((node){tx,ty,0,dis[tx][ty][0]});
}
}
} else if(z==1) {
for(int i=1; i<=4; i++) {
int tx=x+dx[i],ty=y+dy[i];
if(tx<1||tx>n||ty<1||ty>m||ch[tx][ty]=='#') continue;
if(dis[tx][ty][z-1]>dis[x][y][z]) {
dis[tx][ty][z-1]=dis[x][y][z];
q[0].push((node){tx,ty,z-1,dis[tx][ty][z-1]});
}
}
} else {
for(int i=1; i<=4; i++) {
int tx=x+dx[i],ty=y+dy[i];
if(tx<1||tx>n||ty<1||ty>m) continue;
if(dis[tx][ty][z-1]>dis[x][y][z]) {
dis[tx][ty][z-1]=dis[x][y][z];
q[0].push((node){tx,ty,z-1,dis[tx][ty][z-1]});
}
}
}
}
int ans=0x3f3f3f3f3f3f3f3f;
for(int i=0; i<=k; i++) ans=min(ans,dis[xt][yt][i]);
if(ans<0x3f3f3f3f3f3f3f3f) cout<<ans;
else cout<<-1;
return 0;
}
闲话:如果这题只让 \(nmk\) 过应该有个上位蓝到下位紫。
T2
题意:给定一个 \(n\) 个点的树,点有点权,定义一个点 \(u\) 的价值沉为在树上选择一个点 \(v\),\(u\) 到 \(v\) 的简单路径上的点权异或和。每个点选择 \(v\) 是随机的,求存在一个点的价值沉 \(\geq t\) 的概率乘以 \(n^n\) 模 \(998244353\) 的结果。
\(n \leq 3 \times 10^5\)。
容易把题意转换为求每个点的价值 \(< t\) 的方案数的乘积,两个点之间的简单路径点权异或和可以被表示为两个点到根的异或再异或上 lca 的点权,这启发我们在 lca 上统计。
假设在 dfs 过程中当前这个点为 \(u\),那么我们就要统计所有经过 \(u\) 且 \(< t\) 的路径 \(s \to t\),将 \(s,t\) 的方案数 \(+ 1\)。异或 \(< t\) 可以用 01 trie 实现,使用 dsu on tree 保留重儿子后,轻儿子暴力加入 01 trie,同时统计有多少个点能使得加入的这个点方案数 \(+ 1\),然后在 01 trie 上打 tag,表示当前插入的这个点对这个 01 trie 上的节点的方案数贡献。最后如果当前 dfs 的是轻儿子,暴力将 01 trie 上的 tag 加回到每个点,然后删除。
时间复杂度 \(\Theta(n \log n \log V)\),常数较大,需要卡常。
代码:
#include<bits/stdc++.h>
#pragma GCC optimize("Ofast,unroll-loops")
using namespace std;
const int N=3e5+5,p=998244353;
long long t,w[N],dis[N];
vector<int> ve,id[N<<5];
int n,Sz[N],son[N],tot=0,ch[N<<5][2],sz[N<<5],tag[N<<5],cnt[N],head[N],ecnt=0;
struct edge {int to,nxt;}e[N<<1];
inline void addedge(int u,int v) {e[++ecnt]=(edge){v,head[u]};head[u]=ecnt;return;}
void dfs1(int u,int pre) {
int mx=0;
Sz[u]=1,dis[u]=dis[pre]^w[u];
for(int i=head[u]; i; i=e[i].nxt) {
int v=e[i].to;
if(v==pre) continue;
dfs1(v,u);
Sz[u]+=Sz[v];
if(Sz[v]>mx) {
mx=Sz[v];
son[u]=v;
}
}
return;
}
inline void add(int ii,long long x) {
int cur=0;
for(int i=31; i>=0; --i) {
bool b=(x>>i)&1;
if(!ch[cur][b]) ch[cur][b]=++tot;
cur=ch[cur][b];
sz[cur]++;
cnt[ii]-=tag[cur];
}
id[cur].emplace_back(ii);
return;
}
inline int query(long long x) {
int cur=0,ans=0;
for(int i=31; i>=0; --i) {
bool b1=(x>>i)&1,b2=(t>>i)&1;
if(b2) {
if(ch[cur][b1^1]) tag[ch[cur][b1]]++,ans+=sz[ch[cur][b1]],cur=ch[cur][b1^1];
else {
tag[ch[cur][b1]]++,ans+=sz[ch[cur][b1]];
break;
}
} else if(ch[cur][b1]) cur=ch[cur][b1];
else break;
}
return ans;
}
void collect(int u,int pre) {
ve.emplace_back(u);
for(int i=head[u]; i; i=e[i].nxt) {
int v=e[i].to;
if(v==pre) continue;
collect(v,u);
}
return;
}
void dfstrie(int u,int sum) {
if(u) sum+=tag[u];
for(int i=0; i<id[u].size(); ++i) cnt[id[u][i]]+=sum;
if(ch[u][0]) dfstrie(ch[u][0],sum);
if(ch[u][1]) dfstrie(ch[u][1],sum);
return;
}
void dfs2(int u,int pre,bool hv) {
for(int i=head[u]; i; i=e[i].nxt) {
int v=e[i].to;
if(v==pre||v==son[u]) continue;
dfs2(v,u,0);
}
if(son[u]) dfs2(son[u],u,1);
for(int i=head[u]; i; i=e[i].nxt) {
int v=e[i].to;
if(v==pre||v==son[u]) continue;
ve.clear();collect(v,u);
for(int j=0; j<ve.size(); j++) cnt[ve[j]]+=query(dis[ve[j]]^w[u]);
for(int j=0; j<ve.size(); j++) add(ve[j],dis[ve[j]]);
}
cnt[u]+=query(dis[u]^w[u]);
add(u,dis[u]);
if(!hv) {
dfstrie(0,0);
for(int i=0; i<=tot; i++) sz[i]=tag[i]=ch[i][0]=ch[i][1]=0,id[i].clear();
tot=0;
}
return;
}
inline int qpow(int a,int b) {
int ans=1;
while(b) {
if(b&1) ans=1ll*ans*a%p;
a=1ll*a*a%p;
b>>=1;
}
return ans;
}
inline long long read() {
int f=1;
long long x=0;
char ch=getchar();
while(ch<'0'||ch>'9') {
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x*f;
}
signed main() {
n=read();
for(int i=1; i<n; ++i) {
int u,v;
u=read(),v=read();
addedge(u,v),addedge(v,u);
}
for(int i=1; i<=n; ++i) w[i]=read();
t=read();
dfs1(1,0);dfs2(1,0,0);
int ans=1;
for(int i=1; i<=n; ++i) ans=1ll*ans*(cnt[i]+(w[i]<t))%p;
cout<<(qpow(n,n)-ans+p)%p;
return 0;
}
T3
原题:AT_arc063_f。
还不会。
T4
AT_arc070_f 加强版。
还不会。
标签:ch,noi,tx,ty,int,cur,plus,201117,dis From: https://www.cnblogs.com/System-Error/p/18551087