杂题选做
[JOI 2021 Final] ロボット
一个直观的做法,对于每条边\((x,y,c,w)\),如果改颜色只出现一次,连边 \((x,y,0)\),否则有两种策略,第一种是把该颜色直接变,连边 \((x,y,w)\),第二种是改变该颜色的其他边,如果和为 \(sum\),连边 \((x,y,sum-w)\) 。
第二种连边的正确在于:如果被改变的边后续被再次访问到,因为 \(sum-w\leq w\),所以可以直接用第一种策略从这条被再次访问过的边,一定不劣。
但是该做法漏了一种情况,如果我们通过策略一改变了一条原本为 \(c\) 的边进入 \(x\),然后通过策略二从一条为 \(c\) 的边出去,那么进入的边就不需要再次被算入贡献了。
因此对于每个点 \(x\),每个颜色 \(c\),都建立一个虚点 \(o\),对于一条边 \((x,y,c,w)\),连边 \((o,y,sum-w)\),连边 \((y,o,0)\),代表上述情况。
code
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6+7;
typedef long long LL;
int n,m;
int U[N],V[N],C[N],W[N];
struct edge
{
int y,next;
LL v;
}e[2*N];
int flink[N],t=0;
void add(int x,int y,LL v)
{
e[++t].y=y;
e[t].v=v;
e[t].next=flink[x];
flink[x]=t;
}
LL dis[N];
bool vis[N];
#define PII pair<LL,int>
#define mk(x,y) make_pair(x,y)
#define X(x) x.first
#define Y(x) x.second
int tot;
priority_queue<PII> q;
void dijs()
{
for(int i=1;i<=tot;i++)dis[i]=1e18;
dis[1]=0;q.push(mk(0,1));
while(!q.empty())
{
int x=q.top().second;
q.pop();
if(vis[x])continue;
vis[x]=1;
for(int i=flink[x];i;i=e[i].next)
{
int y=e[i].y;
if(dis[y]>dis[x]+e[i].v)
{
dis[y]=dis[x]+e[i].v;
q.push(mk(-dis[y],y));
}
}
}
}
map<int,vector<int> > mp[N];
int main()
{
cin>>n>>m;
for(int i=1;i<=m;i++)
{
scanf("%d %d %d %d",&U[i],&V[i],&C[i],&W[i]);
mp[U[i]][C[i]].push_back(i);
mp[V[i]][C[i]].push_back(i);
}
tot=n;
for(int i=1;i<=n;i++)
{
for(auto c:mp[i])
{
int x=++tot;
add(i,x,0);
LL sum=0;
for(int j:c.second)sum+=W[j];
for(int j:c.second)
{
int y=U[j]+V[j]-i;
if(c.second.size()==1)
{
add(i,y,0);
break;
}
add(x,y,sum-W[j]);
add(y,x,0);
add(i,y,W[j]);
}
}
}
dijs();
if(dis[n]==1e18)cout<<-1;
else cout<<dis[n];
return 0;
}
[JOI Open 2022] 放学路(School Road)
先考虑原图是一个点双的情况,考虑用类似广义串并联图的方式处理:
对于一度点,直接删掉即可。
对于二度点,直接把两条边合起来。
对于重边,如果边权不相等,直接输出 \(1\),否则保留一条即可。
那么最后剩下的图,如果只剩下 \(1\to n\),显然是 \(0\),否则也可以证明是 \(1\) 。
对于不是点双的情况,和上述做法的区别在于,我们只保留存在 \(1\to x\to n\) 的简单路径的点 \(x\) 。
加入一条边 \((1,n,dis(1,n)\),显然不影响答案,同时 \(1,n\) 此时一定在一个点双内,可以发现此时可能的 \(x\) 也一定在该点双内,因此保留该点双,套用上面的做法就好了。
code
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+7;
#define int long long
typedef long long LL;
struct edge
{
int y,next,v;
}e[2*N];
int link[N],t=0;
void add(int x,int y,int v)
{
e[++t].y=y;
e[t].v=v;
e[t].next=link[x];
link[x]=t;
}
int n,m;
int low[N],dfn[N],num;
stack<int> st;
int seq[N],C=0;
bool ins[N];
void manage()
{
bool A=0,B=0;
for(int i=1;i<=C;i++)
{
A|=(seq[i]==1);
B|=(seq[i]==n);
}
if(A&&B)
{
for(int i=1;i<=C;i++)
ins[seq[i]]=1;
}
}
void tarjan(int x)
{
low[x]=dfn[x]=++num;
st.push(x);
for(int i=link[x];i;i=e[i].next)
{
int y=e[i].y;
if(!dfn[y])
{
tarjan(y);
if(low[y]>=dfn[x])
{
int z;
C=0;
do
{
z=st.top();
st.pop();
seq[++C]=z;
}while(z!=y);
seq[++C]=x;
manage();
}
low[x]=min(low[x],low[y]);
}
else low[x]=min(low[x],dfn[y]);
}
}
int U[N],V[N],W[N];
LL dis[N];
bool vis[N];
#define PII pair<LL,int>
#define mk(x,y) make_pair(x,y)
priority_queue<PII> q;
void dijs()
{
for(int i=1;i<=n;i++)dis[i]=1e18;
dis[1]=0;q.push(mk(0,1));
while(!q.empty())
{
int x=q.top().second;
q.pop();
if(vis[x])continue;
vis[x]=1;
for(int i=link[x];i;i=e[i].next)
{
int y=e[i].y;
if(dis[y]>dis[x]+e[i].v)
{
dis[y]=dis[x]+e[i].v;
q.push(mk(-dis[y],y));
}
}
}
}
multiset<PII> G[N];
int deg[N];
queue<int> que;
void ers(int x,int y,int v)
{
deg[x]--;deg[y]--;
G[x].erase(G[x].find(mk(y,v)));
G[y].erase(G[y].find(mk(x,v)));
}
int check(int x,int y)
{
auto u=G[x].lower_bound(mk(y,0));
if(u!=G[x].end()&&(u->first==y)) return u->second;
return -1;
}
void apl(int x,int y,int v)
{
int w=check(x,y);
if(w!=-1&&w!=v)
{
printf("1");
exit(0);
}
if(w!=-1)return;
deg[x]++;deg[y]++;
G[x].insert(mk(y,v));
G[y].insert(mk(x,v));
}
void upd(int x);
void del(int x)
{
if(!ins[x])return;
auto u=(G[x].begin());
int y=u->first,v=u->second;
ers(x,y,v);
ins[x]=0;
upd(y);
}
void upd(int x)
{
if(x!=1&&x!=n)
{
if(deg[x]==1)del(x);
else if(deg[x]==2)que.push(x);
}
}
signed main()
{
cin>>n>>m;
for(int i=1;i<=m;i++)
{
scanf("%lld %lld %lld",&U[i],&V[i],&W[i]);
add(U[i],V[i],W[i]);
add(V[i],U[i],W[i]);
}
dijs();
++m;
U[m]=1;V[m]=n;W[m]=dis[n];
add(1,n,dis[n]);add(n,1,dis[n]);
tarjan(1);
for(int i=1;i<=m;i++)
{
int x=U[i],y=V[i];
if(ins[x]&&ins[y])apl(x,y,W[i]);
}
for(int i=1;i<=n;i++)upd(i);
while(!que.empty())
{
int x=que.front();
que.pop();
if(!ins[x])continue;
ins[x]=0;
auto A=(G[x].begin()),B=(G[x].begin());B++;
int ya=A->first,va=A->second;
int yb=B->first,vb=B->second;
ers(x,ya,va);ers(x,yb,vb);
apl(ya,yb,va+vb);
upd(ya);upd(yb);
}
if(G[1].size()==1&&check(1,n)!=-1)cout<<0;
else cout<<1;
return 0;
}
[UR #25]见贤思齐
首先考虑图是一棵树的情况,考虑对于每个点 \(x\),维护一棵以时间为下标的线段树,第 \(i\) 个位置的值 \(f_{x,i}\) 为第 \(i\) 天的增量(\(\in [0,1]\))。
考虑从 \(p_i\) 推向 \(i\) 时线段树的变化:
若某个时刻 \(t\),\(a_{t,p_x}=a_{t,x}\),且这个情况第一次发生,那么接下来我们会发现: \(f_{t+1,x}=1\),\(f_{t+k,x}=f_{t+k-1,p_x},k\geq 2\) ,也就是说,一定是把整个线段树平移了一个单位。
在发生这种情况之前,可以发现 \(f_{t,x}\) 要么一直是 \(0\) 要么一直是 \(1\) ,我们可以线段树二分找到上面的位置,然后就可以做了。
返回的时候需要撤回。
复杂度 \(O(n\log n)\)
code
#include<bits/stdc++.h>
using namespace std;
const int N = 4e5+7;
template<typename _T> inline void read(_T &x){
x=0; char c=getchar(); bool f=0;
for(; c<'0'||c>'9'; c=getchar()) f|=(c=='-');
for(; c>='0'&&c<='9'; c=getchar()) x=(x<<1)+(x<<3)+(c^48);
x=(f)?(-x):x;
}
struct edge
{
int y,next;
}e[2*N];
int link[N],t=0;
void add(int x,int y)
{
e[++t].y=y;
e[t].next=link[x];
link[x]=t;
}
int p[N],a[N];
int n,m;
int day[N];
vector<int> qry[N];
#define S 1,-n,2e5
bool vis[N];
bool ins[N];
int sum[N*4],cov[N*4],len[N*4];
struct info
{
int k,sum,cov;
};
vector<info> seq;
void pushup(int k)
{
seq.push_back((info){k,sum[k],cov[k]});
sum[k]=sum[k<<1]+sum[k<<1|1];
}
void pushtag(int k,int v)
{
seq.push_back((info){k,sum[k],cov[k]});
sum[k]=v*len[k];
cov[k]=v;
}
void pushdown(int k)
{
if(cov[k]!=-1)
{
seq.push_back((info){k,sum[k],cov[k]});
pushtag(k<<1,cov[k]);
pushtag(k<<1|1,cov[k]);
cov[k]=-1;
}
}
const int Mx = 2e5;
void build(int k,int l,int r)
{
len[k]=r-l+1;
sum[k]=0;
cov[k]=-1;
if(l==r)return;
int mid=(l+r)>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
}
int ask(int k,int l,int r,int L,int R)
{
if(L<=l&&r<=R) return sum[k];
pushdown(k);
int mid=(l+r)>>1;
int res=0;
if(L<=mid)res+=ask(k<<1,l,mid,L,R);
if(R>mid) res+=ask(k<<1|1,mid+1,r,L,R);
return res;
}
void modify(int k,int l,int r,int L,int R,int v)
{
if(L<=l&&r<=R)
{
pushtag(k,v);
return;
}
int mid=(l+r)>>1;
if(L<=mid)modify(k<<1,l,mid,L,R,v);
if(R>mid) modify(k<<1|1,mid+1,r,L,R,v);
pushup(k);
}
int det[N];
void Upd(int x,int l,int r,int v)
{
if(l>r) return;
l-=det[x];r-=det[x];
modify(S,l,r,v);
}
int Sum(int x,int l,int r)
{
if(l>r) return 0;
l-=det[x];r-=det[x];
return ask(S,l,r);
}
int D=0;
const int INF = -1e9;
int query1(int k,int l,int r)
{
if(sum[k]-len[k]+D>0)
{
D+=sum[k]-len[k];
return INF;
}
if(l==r) return l;
pushdown(k);
int mid=(l+r)>>1;
int p=query1(k<<1,l,mid);
if(p!=INF) return p;
return query1(k<<1|1,mid+1,r);
}
int query2(int k,int l,int r,int L,int R)
{
if(L<=l&&r<=R)
{
int p=query1(k,l,r);
return p;
}
pushdown(k);
int mid=(l+r)>>1;
int p=INF;
if(L<=mid)p=query2(k<<1,l,mid,L,R);
if(p!=INF) return p;
if(R>mid) p=query2(k<<1|1,mid+1,r,L,R);
return p;
}
int query3(int k,int l,int r)
{
if(sum[k]+D<0)
{
D+=sum[k];
return INF;
}
if(l==r) return l;
pushdown(k);
int mid=(l+r)>>1;
int p=query3(k<<1,l,mid);
if(p!=INF) return p;
return query3(k<<1|1,mid+1,r);
}
int query4(int k,int l,int r,int L,int R)
{
if(L<=l&&r<=R)
{
int p=query3(k,l,r);
return p;
}
pushdown(k);
int mid=(l+r)>>1;
int p=INF;
if(L<=mid)p=query4(k<<1,l,mid,L,R);
if(p!=INF) return p;
if(R>mid) p=query4(k<<1|1,mid+1,r,L,R);
return p;
}
void put(int x)
{
for(int i=0;i<=10;i++)
printf("f(%d,%d)=%d\n",x,i,Sum(x,1,i));
}
void jump1(int x)
{
//put(p[x]);
D=a[p[x]]-a[x]+1;
int ans=query2(S,-det[p[x]],2e5-det[p[x]]);
if(ans==INF)ans=2e5;
else ans+=det[p[x]];
det[x]=det[p[x]]+1;
Upd(x,1,ans,1);
if(ans<2e5) Upd(x,ans+1,ans+1,1);
}
void jump2(int x)
{
D=a[p[x]]-a[x];
int ans=query4(S,-det[p[x]],2e5-det[p[x]]);
if(ans==INF)ans=2e5;
else ans+=det[p[x]];
det[x]=det[p[x]]+1;
Upd(x,1,ans,0);
if(ans<2e5) Upd(x,ans+1,ans+1,1);
}
int ans[N];
void dfs(int x,int r)
{
vis[x]=1;
int lst=seq.size();
if(x==r)
{
det[x]=0;
Upd(x,1,2e5,1);
}
else
{
if(a[p[x]]>=a[x]) jump1(x);
else jump2(x);
}
//put(x);
//cout<<endl;
for(auto u:qry[x])ans[u]=a[x]+Sum(x,1,day[u]);
for(int i=link[x];i;i=e[i].next)
{
int y=e[i].y;
if(y!=r)dfs(e[i].y,r);
}
//cout<<"clr"<<endl;
while(seq.size()>lst)
{
int k=seq.back().k;
sum[k]=seq.back().sum;
cov[k]=seq.back().cov;
seq.pop_back();
}
}
int main()
{
read(n);read(m);
for(int i=1;i<=n;i++)read(a[i]);
for(int i=1;i<=n;i++)read(p[i]),add(p[i],i);
for(int i=1;i<=m;i++)
{
int x,d;
read(x);read(d);
day[i]=d;
qry[x].push_back(i);
}
build(S);
for(int i=1;i<=n;i++)
if(!vis[i])
{
int x=i;
while(!vis[x])
{
vis[x]=1;
x=p[x];
}
int mn=0;
while(!ins[x])
{
ins[x]=1;
if(!mn||a[x]<a[mn])mn=x;
x=p[x];
}
dfs(mn,mn);
}
for(int i=1;i<=m;i++)printf("%d\n",ans[i]);
return 0;
}

浙公网安备 33010602011771号