やっぱり雨は降るんだね | 题解:P10433 [JOISC 2024 Day2] 棋盘游戏
Petit_Souris · · 题解
这个题太难了。思考时间至少得有 1.5h,代码和 debug 难度也很高。放到正式比赛场上我不可能通过。
最初的观察
我们现在需要最小化棋子
由于这是一张无向图,我们容易发现可以有一种很好的策略:先走到一个终止节点,从第二次开始每次走出一步再走回来,这样除了第一回合以外,每个回合对答案的贡献为固定的
但是我们发现这样不一定是最优的:我们可能可以走到两个相邻的终止节点中的一个,并在两个终止节点之间反复横跳,这样除了第一回合以外每个回合只贡献
巧妙的补丁
我们分析一下走到一组相邻终止节点的路径情况:假设路径上经过了
你可能会好奇,这样为什么是对的?难道不会出现
问题的开端
现在
现在我们的问题清晰许多了:对于一条
在繁琐的分析之后我们之后得出了多项式时间复杂度的做法:记录当前的
最终的一击
考虑优化。我们发现当
既然除以
容易发现
平衡两边复杂度,取
代码细节相当繁琐。
#include<bits/stdc++.h>
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
#define pii pair<ll,ll>
#define rep(i,a,b) for(ll i=(a);i<=(b);++i)
#define per(i,a,b) for(ll i=(a);i>=(b);--i)
using namespace std;
bool Mbe;
ll read(){
ll x=0,f=1;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;
}
void write(ll x){
if(x<0)putchar('-'),x=-x;
if(x>9)write(x/10);
putchar(x%10+'0');
}
const ll N=5e4+9,INF=1e12;
ll n,m,K,X[N],tod[2][N],tag[N],md[N],cst[N];
vector<ll>to[N];
char s[N];
void run1(){
queue<ll>q;
rep(i,0,n)tod[0][i]=-1;
rep(i,1,n){
if(s[i]=='1'){
for(ll j:to[i])q.push(j),tod[0][j]=1;
}
}
rep(i,1,n){
if(~tod[0][i])q.push(i);
}
while(!q.empty()){
ll u=q.front();q.pop();
for(ll v:to[u]){
if(!~tod[0][v]){
tod[0][v]=tod[0][u]+1;
q.push(v);
}
}
}
}
void run2(){
deque<ll>q;
rep(i,0,n)tod[1][i]=-1;
rep(i,1,n){
if(tag[i]){
for(ll j:to[i])tod[1][j]=1;
}
}
rep(i,1,n){
if(~tod[1][i])q.push_back(i);
}
while(!q.empty()){
ll u=q.front();q.pop_front();
for(ll v:to[u]){
if(s[u]=='1'){
if(!~tod[1][v]||tod[1][v]>tod[1][u])tod[1][v]=tod[1][u],q.push_front(v);
}
else {
if(!~tod[1][v]||tod[1][v]>tod[1][u]+1)tod[1][v]=tod[1][u]+1,q.push_back(v);
}
}
}
}
void run3(){
rep(i,0,n)md[i]=INF;
md[X[1]]=0;
queue<ll>q;
q.push(X[1]);
while(!q.empty()){
ll u=q.front();q.pop();
if(u!=X[1]&&s[u]=='1')continue;
for(ll v:to[u]){
if(md[v]>md[u]+1){
md[v]=md[u]+1;
q.push(v);
}
}
}
}
namespace S1{
ll minc[N],f[N][505];
void solve(){
rep(i,1,n)minc[i]=INF;
deque<ll>q;
q.push_back(X[1]);
minc[X[1]]=0;
while(!q.empty()){
ll u=q.front();q.pop_front();
for(ll v:to[u]){
ll w=(s[u]=='1'&&u!=X[1]);
if(minc[v]>minc[u]+w){
minc[v]=minc[u]+w;
if(!w)q.push_front(v);
else q.push_back(v);
}
}
}
rep(i,0,n){
rep(j,0,min(500ll,n/K))f[i][j]=INF;
}
queue<pii>q1;
f[X[1]][0]=0;
q1.push({X[1],0});
while(!q1.empty()){
pii now=q1.front();q1.pop();
ll u=now.first,i=now.second;
for(ll v:to[u]){
ll j=minc[u]+i-minc[v]+(u!=X[1]&&s[u]=='1');
if(j<=min(500ll,n/K)){
if(f[v][j]>f[u][i]+1)f[v][j]=f[u][i]+1,q1.push({v,j});
}
}
}
rep(i,1,n){
ll ans=md[i];
rep(j,0,min(500ll,n/K))ans=min(ans,f[i][j]+cst[minc[i]+j]);
write(ans),putchar('\n');
}
}
}
namespace S2{
ll dis[N][2],ans[N];
bool ok[N][2];
struct Node{
ll u,tp,d;
bool operator<(const Node&a)const{return d>a.d;}
};
void dijk(ll k,ll sp){
priority_queue<Node>q;
rep(i,0,n+1){
rep(j,0,1)dis[i][j]=INF,ok[i][j]=0;
}
dis[X[1]][0]=0;
q.push({X[1],0,0});
while(!q.empty()){
Node now=q.top();q.pop();
ll u=now.u,tp=now.tp;
if(ok[u][tp])continue;
ok[u][tp]=1;
for(ll v:to[u]){
ll w=1+k*(u!=X[1]&&s[u]=='1');
ll ntp=tp||(u!=X[1]&&s[u]=='1');
if(dis[v][ntp]>dis[u][tp]+w){
dis[v][ntp]=dis[u][tp]+w;
q.push({v,ntp,dis[v][ntp]});
}
}
}
rep(i,1,n)ans[i]=min(ans[i],sp+dis[i][1]);
}
void solve(){
rep(i,1,n)ans[i]=md[i];
rep(i,2,n){
if(i==n||cst[i]*2!=cst[i-1]+cst[i+1])dijk(cst[i]-cst[i-1],cst[i]-(cst[i]-cst[i-1])*i);
}
rep(i,1,n)write(ans[i]),putchar('\n');
}
}
bool Med;
int main(){
cerr<<fabs(&Med-&Mbe)/1048576.0<<"MB\n";
n=read(),m=read(),K=read();
rep(i,1,m){
ll x=read(),y=read();
to[x].push_back(y);
to[y].push_back(x);
}
scanf("%s",s+1);
rep(i,1,K)X[i]=read();
rep(i,1,n){
if(s[i]=='1'){
for(ll j:to[i]){
if(s[j]=='1')tag[i]=1;
}
}
}
run1(),run2(),run3();
if(!count(s+1,s+n+1,'1')){
rep(i,1,n)write(md[i]),putchar('\n');
return 0;
}
rep(i,2,K){
cst[2]+=2;
if(~tod[1][X[i]])cst[tod[1][X[i]]-tod[0][X[i]]+2]--;
}
rep(i,2,n)cst[i]+=cst[i-1];
rep(i,2,K)cst[1]+=tod[0][X[i]];
rep(i,2,n)cst[i]+=cst[i-1];
if(K>=100)S1::solve();
else S2::solve();
cerr<<"\n"<<clock()*1.0/CLOCKS_PER_SEC*1000<<"ms\n";
return 0;
}