海亮寄 7.11
前言
业精于勤荒于嬉,行成于思而毁于随
正文(加餐)
连通分量选讲,这是要 \(DAY^{-1}\) 的节奏
浅贴一个课件
关键词:多源最短路、前置转化、临界分割
再浅贴一下题单
机房里有两个人疑似获得了情侣 buff,手速和代码准确率获得 \(2\) 倍加成
T1
先丢进来一个板子
题意
给定一个无向图,问有多少个点满足:删除该点后图变成一棵树
题解
度数 \(m-n+2\) 是容易判断的!
连通性也需要保证,所以要求度数合法的点不可以是割点
corner case 的特判是:一棵树 + 孤立点(然而其实不判也能过?)
代码
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5,M=1e5+5;
int n,m,head[N],tot=1,rt;
struct Edge{int to,nxt;}e[M<<1];
int dfn[N],low[N],tim,deg[N];bool cut[N];
set<int> S;
inline void add(int u,int v){
e[++tot]={v,head[u]};head[u]=tot;
return;
}
inline void tarjan(int u){
dfn[u]=low[u]=++tim;
int ch=0;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(!dfn[v]){
tarjan(v);
low[u]=min(low[u],low[v]);
if(low[v]>=dfn[u]&&u!=rt)cut[u]=true;
if(u==rt)ch++;
}else low[u]=min(low[u],dfn[v]);
}
if(ch>=2&&u==rt)cut[u]=true;
return;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=1;i<=m;i++){
int u,v;cin>>u>>v;
add(u,v),add(v,u);
deg[u]++,deg[v]++;
}
for(int i=1;i<=n;i++)
if(!dfn[i])rt=i,tarjan(i);
for(int i=1;i<=n;i++)
if(!cut[i]&°[i]==m-n+2)S.insert(i);
cout<<S.size()<<'\n';
for(auto x:S)cout<<x<<' ';
return 0;
}
T2
一题三解,收获颇丰
题意
给定有向图,点有点权 \(c_i\),求最大的 \(c_y - c_x\) 使得存在一条 \(1 \to x \to y \to n\) 的路径
题解
法一:缩点起手,DAG 上 DP,随便维护
法二:分层图,层层之间的边权表示买入或卖出的活动,直接 spfa 最长路或者利用小性质转化一下变为 dijkstra 最短路
法三:判可达性后直接按 \(c_i\) 升序排序,均摊复杂度 \(O(n+m)\),可以通过
代码
给一份缩点实现的,毕竟专题是连通分量专题
点击查看代码
#include<bits/stdc++.h>
#define pii pair<int,int>
#define fi first
#define se second
#define mkp make_pair
#define vi vector<int>
#define pb push_back
using namespace std;
const int N=1e5+5,M=5e5+5;
int n,m,c[N],head[N],tot=1;
struct Edge{int to,nxt;}e[M<<1];
int dfn[N],low[N],tim,col[N],cnt;
int stk[N],tp,mx[N],mn[N];bool instk[N];
map<pii,bool> mrk;vi G[N];int f[N],ans[N];
int in[N],dp[N];
inline void add(int u,int v){
e[++tot]={v,head[u]};head[u]=tot;
return;
}
inline void tarjan(int u){
dfn[u]=low[u]=++tim;stk[++tp]=u;instk[u]=true;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(!dfn[v]){tarjan(v);low[u]=min(low[u],low[v]);}
else if(instk[v])low[u]=min(low[u],dfn[v]);
}
if(dfn[u]==low[u]){
cnt++;
int now=-1;
while(now!=u){
now=stk[tp--];instk[now]=false;
col[now]=cnt;
mx[cnt]=max(mx[cnt],c[now]);
mn[cnt]=min(mn[cnt],c[now]);
}
f[cnt]=mn[cnt];
}
return;
}
inline void topo(){
for(int u=1;u<=cnt;u++)for(int v:G[u])in[v]++;
int S=col[1];
queue<int> q;q.push(S);
f[S]=mn[S];ans[S]=mx[S]-mn[S];
while(!q.empty()){
int u=q.front();q.pop();
for(int v:G[u]){
f[v]=min(f[v],f[u]);ans[v]=max(ans[v],ans[u]);
in[v]--;
if(!in[v]){ans[v]=max(ans[v],mx[v]-f[v]);q.push(v);}
}
}
}
int main(){
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>c[i];
for(int i=1;i<=m;i++){
int u,v,opt;cin>>u>>v>>opt;
if(opt==1)add(u,v);
else if(opt==2)add(u,v),add(v,u);
}
memset(mn,0x3f,sizeof(mn));
tarjan(1);
// for(int i=1;i<=cnt;i++)cerr<<i<<' '<<mx[i]<<' '<<mn[i]<<' '<<f[i]<<endl;
// cerr<<'\n';
for(int u=1;u<=n;u++){
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(col[u]==col[v]||!col[u]||!col[v])continue;
if(mrk[mkp(col[u],col[v])])continue;
G[col[u]].pb(col[v]);mrk[mkp(col[u],col[v])]=true;
}
}
topo();
cout<<ans[col[n]]<<'\n';
return 0;
}
T3
当我开 T3 的时候,隔壁墨软二人组都已经干到 T7 力!
题意
给定有向图,判断该图是否满足以下条件:
- 对任意一对点 \(x,y\),均满足:存在一条 \(x \to y\) 的路径或存在一条 \(y \to x\) 的路径
题解
缩点起手,题意的一个充要转化是对于缩点后的 DAG 拓扑序的队列内的元素个数总是不超过 \(1\)
课上还有人提出了另一种转化,即判断最长链是否涵盖所有结点
(目前没有人给出反例,但也没有证明该结论的正确性)
代码
点击查看代码
#include<bits/stdc++.h>
#define vi vector<int>
#define pb push_back
using namespace std;
const int N=1e3+5,M=6e3+5;
int n,m,head[N],tot=1;
struct Edge{int to,nxt;}e[M<<1];
int dfn[N],low[N],tim,col[N],cnt;
int stk[N],tp;bool instk[N];
vi G[N];int in[N];
inline void add(int u,int v){
e[++tot]={v,head[u]};head[u]=tot;
return;
}
inline void tarjan(int u){
dfn[u]=low[u]=++tim;stk[++tp]=u;instk[u]=true;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(!dfn[v]){tarjan(v);low[u]=min(low[u],low[v]);}
else if(instk[v])low[u]=min(low[u],dfn[v]);
}
if(low[u]==dfn[u]){
cnt++;
int now=-1;
while(now!=u){
now=stk[tp--];
instk[now]=false;
col[now]=cnt;
}
}
return;
}
inline bool topo(){
queue<int> q;
for(int i=1;i<=cnt;i++)
if(!in[i])q.push(i);
while(!q.empty()){
if(q.size()>1)return false;
int u=q.front();q.pop();
for(int v:G[u]){
in[v]--;
if(!in[v])q.push(v);
}
}
return true;
}
inline void solve(){
cin>>n>>m;
for(int i=1;i<=m;i++){
int u,v;cin>>u>>v;
add(u,v);
}
for(int i=1;i<=n;i++)
if(!dfn[i])tarjan(i);
for(int i=1;i<=cnt;i++)G[i].clear();
memset(in,0,sizeof(in));
for(int u=1;u<=n;u++){
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(col[u]==col[v])continue;
G[col[u]].pb(col[v]);in[col[v]]++;
}
}
if(topo())cout<<"I love you my love and our love save us!\n";
else cout<<"Light my fire!\n";
return;
}
inline void init(){
memset(head,0,sizeof(head));tot=1;
memset(dfn,0,sizeof(dfn));memset(low,0,sizeof(low));tim=0;
memset(col,0,sizeof(col));cnt=0;
memset(instk,0,sizeof(instk));memset(stk,0,sizeof(stk));tp=0;
return;
}
int main(){
// freopen("in.txt","r",stdin);
// freopen("ans.txt","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
int T;cin>>T;while(T--)init(),solve();
return 0;
}
T4
20 min 切,开心!
题意
给定一个连通无向图,要求选取 \(S,T\) 使得从 \(S\) 到 \(T\) 的必经边尽可能多
题解
边双缩成一棵树后,跑一个树的直径
代码
最水的一集
点击查看代码
#include<bits/stdc++.h>
#define vi vector<int>
#define pb push_back
using namespace std;
const int N=3e5+5,M=3e5+5;
int n,m,head[N],tot=1;
struct Edge{int to,nxt;}e[M<<1];
int dfn[N],low[N],tim,col[N],cnt;
int stk[N],tp;vi G[N];int dp[N],ans;
inline void add(int u,int v){
e[++tot]={v,head[u]};head[u]=tot;
return;
}
inline void tarjan(int u,int pre){
dfn[u]=low[u]=++tim;stk[++tp]=u;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if((i^1)==pre)continue;
if(!dfn[v]){tarjan(v,i);low[u]=min(low[u],low[v]);}
else low[u]=min(low[u],dfn[v]);
}
if(dfn[u]==low[u]){
cnt++;
int now=-1;
while(now!=u){
now=stk[tp--];
col[now]=cnt;
}
}
return;
}
inline void dfs(int u,int fa){
for(int v:G[u]){
if(v==fa)continue;
dfs(v,u);
ans=max(ans,dp[u]+dp[v]+1);
dp[u]=max(dp[u],dp[v]+1);
}
return;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=1;i<=m;i++){
int u,v;cin>>u>>v;
add(u,v),add(v,u);
}
for(int i=1;i<=n;i++)
if(!dfn[i])tarjan(i,-1);
for(int u=1;u<=n;u++){
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(col[u]==col[v])continue;
G[col[u]].pb(col[v]);
}
}
dfs(1,-1);
cout<<ans<<'\n';
return 0;
}
T5
疑似简单题
题意
给定一个无向连通图,要求对所有的点求出:如果删除这个点,将会有多少对点不互相连通
题解
点双是显然的
答案与割点在 DFS 生成树上裂开的子树 \(siz\) 有关,具体是一个 \(\prod siz_v \times (n-siz_v-1)\) 的形式
Warning:
-
割点的父辈们不要忘记加入贡献
-
割点本身不是被删除,而是被“封锁”,所以也有贡献!
代码
就是割点板子上多加了几句贡献计算
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+5,M=5e5+5;
int n,m,head[N],tot=1,rt;
struct Edge{int to,nxt;}e[M<<1];
int dfn[N],low[N],tim,siz[N];int ans[N];
inline void add(int u,int v){
e[++tot]={v,head[u]};head[u]=tot;
return;
}
inline void tarjan(int u){
dfn[u]=low[u]=++tim;siz[u]=1;
int sum=0;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(!dfn[v]){
tarjan(v);siz[u]+=siz[v];
low[u]=min(low[u],low[v]);
if(low[v]>=dfn[u]){ans[u]+=(siz[v]*sum);sum+=siz[v];}
}else low[u]=min(low[u],dfn[v]);
}
ans[u]+=(n-sum-1)*sum;ans[u]+=n-1;
return;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=1;i<=m;i++){
int u,v;cin>>u>>v;
add(u,v),add(v,u);
}
for(int i=1;i<=n;i++)
if(!dfn[i])tarjan(i);
for(int i=1;i<=n;i++)cout<<ans[i]*2<<'\n';
return 0;
}
T6
第一眼,好难,不会做;第二眼,还是不会做;第三眼,嘶——金牌点双第一题;第四眼,rz 题
题意
给定一个无向图,问至少要设置多少个关键点使得,删除任意一个点后,所有点都能走到其中一个关键点,并求方案数
题解
求点双很显然,发现对于每一个点双:
-
如果其包含 \(>1\) 个割点,那么对答案无贡献
-
恰好包含一个割点,意味着点双内需要一个关键点
-
不包含割点,则需要两个关键点
关键点数目出来了,方案数自然好算
代码
不开 long long 见祖宗!
还有喜闻乐见的多测清空环节!
点击查看代码
#include<bits/stdc++.h>
#define int long long
#define vi vector<int>
#define pb push_back
using namespace std;
const int N=1e3+5,M=505;
int n,m,head[N],tot=1,rt;
struct Edge{int to,nxt;}e[M<<1];
int dfn[N],low[N],tim,cnt;
int stk[N],tp;vi dcc[N];bool cut[N];
inline void add(int u,int v){
e[++tot]={v,head[u]};head[u]=tot;
return;
}
inline void tarjan(int u){
dfn[u]=low[u]=++tim;stk[++tp]=u;
if(u==1&&head[u]==0){dcc[++cnt].pb(u);return;}
int fa=0;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(!dfn[v]){
tarjan(v);
low[u]=min(low[u],low[v]);
if(low[v]>=dfn[u]){
fa++;
if(u!=1||fa>1)cut[u]=true;
cnt++;
int now=-1;
while(now!=v){
now=stk[tp--];
dcc[cnt].pb(now);
}
dcc[cnt].pb(u);
}
}else low[u]=min(low[u],dfn[v]);
}
return;
}
inline void clr(){
memset(head,0,sizeof(head));tot=1;
memset(dfn,0,sizeof(dfn));tim=0;
memset(low,0,sizeof(low));
memset(cut,0,sizeof(cut));
memset(stk,0,sizeof(stk));tp=0;
for(int i=1;i<=cnt;i++)dcc[i].clear();
n=cnt=0;
return;
}
signed main(){
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
int T=0;
while(cin>>m){
if(m==0)break;
for(int i=1;i<=m;i++){
int u,v;cin>>u>>v;
add(u,v),add(v,u);
n=max(n,max(u,v));
}
tarjan(1);
int ans1=0,ans2=1;
for(int i=1;i<=cnt;i++){
int sum=dcc[i].size(),tol=0;
for(auto x:dcc[i])tol+=cut[x];
if(tol==1)ans1++,ans2*=(sum-1);
else if(tol==0)ans1+=2,ans2*=(sum*(sum-1)/2);
}
cout<<"Case "<<(++T)<<": "<<ans1<<" "<<ans2<<"\n";
clr();
}
return 0;
}
T7
铁打的 shr,流水的 luogu 题
题意
直接去翻原题吧,难以概括……
题解
强连通分量内贡献好算
DAG 上发现选取度数为 \(0\) 的点一定最优
得到了一个近似正解的做法
然后发现一些 corner case
对于一些大小为 \(1\) 且入度为 \(0\) 的强连通分量,如果其领域内不存在入度为 \(1\) 的点,则可以不用选中该强连通分量,即对答案贡献减一
代码
隔壁火腿肠开 long double 莫名其妙 94pts,云落瞪了一下没瞪出来
(但其实云落的代码实现用 long double 可过,比较神秘)
点击查看代码
#include<bits/stdc++.h>
#define int long long
#define db long double
#define vi vector<int>
#define pb push_back
#define pii pair<int,int>
#define fi first
#define se second
#define mkp make_pair
using namespace std;
const int N=1e5+5,M=3e5+5;
int n,m,head[N],tot;
struct Edge{int to,nxt;}e[M<<1];
int dfn[N],low[N],tim,col[N],cnt,in[N];
int stk[N],tp;bool instk[N];vi scc[N];
map<pii,bool> mp;
inline void add(int u,int v){
e[++tot]={v,head[u]};head[u]=tot;
return;
}
inline void tarjan(int u){
dfn[u]=low[u]=++tim;stk[++tp]=u;instk[u]=true;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(!dfn[v]){tarjan(v);low[u]=min(low[u],low[v]);}
else if(instk[v])low[u]=min(low[u],dfn[v]);
}
if(low[u]==dfn[u]){
cnt++;
int now=-1;
while(now!=u){
now=stk[tp--];instk[now]=false;
col[now]=cnt;scc[cnt].pb(now);
}
}
return;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=1,u,v;i<=m;i++)cin>>u>>v,add(u,v);
for(int i=1;i<=n;i++)
if(!dfn[i])tarjan(i);
for(int u=1;u<=n;u++){
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(col[u]==col[v]||mp[mkp(col[u],col[v])])continue;
in[col[v]]++;mp[mkp(col[u],col[v])]=true;
}
}
int sum=0;
for(int i=1;i<=cnt;i++)
if(!in[i])sum++;
for(int k=1;k<=cnt;k++){
if((int)(scc[k].size())>1||in[k])continue;
int u=scc[k][0];bool flag=false;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(in[col[v]]==1)flag=true;
}
if(!flag){sum--;break;}
}
db ans=1.0-1.0*sum/n;
cout<<fixed<<setprecision(6)<<ans<<'\n';
return 0;
}
T8
锦鲤抄……
题意
给你一张有向图,每个点有一个点权。任意时刻你可以任意选择一个有入度的点,获得
它的点权并把它和它的出边从图上删去。最多能选择 \(k\) 个点,求最多能获得多少点权
题解
缩点起手,发现一个 SCC 内可以选择至少 \(siz-1\) 个点。特别地,如果存在入度或者自环,则可以都选
对于 DAG 的部分,按反向拓扑序取,可以取走所有的点
把所有可以扔进去的点扔到一个 vector 里,排序取前 \(k\) 大即可
代码
(因为按编号排序只 WA 两个点,\(FeSO_4\) 的同时还硬控自己半个点)
点击查看代码
#include<bits/stdc++.h>
#define int long long
#define vi vector<int>
#define pb push_back
using namespace std;
const int N=5e5+5,M=2e6+5;
int n,m,k,a[N],head[N],tot;
struct Edge{int to,nxt;}e[M<<1];
int dfn[N],low[N],tim,col[N],cnt,in[N];
int stk[N],tp;bool instk[N];vi scc[N],vec;
inline void add(int u,int v){
e[++tot]={v,head[u]};head[u]=tot;
return;
}
inline void tarjan(int u){
dfn[u]=low[u]=++tim;stk[++tp]=u;instk[u]=true;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(!dfn[v]){tarjan(v);low[u]=min(low[u],low[v]);}
else if(instk[v])low[u]=min(low[u],dfn[v]);
}
if(low[u]==dfn[u]){
cnt++;
int now=-1;
while(now!=u){
now=stk[tp--];instk[now]=false;
col[now]=cnt;scc[cnt].pb(now);
}
}
return;
}
inline bool cmp(int x,int y){return a[x]>a[y];}
inline bool cmp1(int x,int y){return x>y;}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>m>>k;
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1,u,v;i<=m;i++)cin>>u>>v,add(u,v);
for(int i=1;i<=n;i++)
if(!dfn[i])tarjan(i);
for(int u=1;u<=n;u++){
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(u==v||col[u]!=col[v])in[col[v]]++;
}
}
for(int i=1;i<=cnt;i++){
if(scc[i].size()==1){
if(in[i])vec.pb(a[scc[i][0]]);
continue;
}
sort(scc[i].begin(),scc[i].end(),cmp);
for(auto x:scc[i])vec.pb(a[x]);
if(!in[i])vec.pop_back();
}
sort(vec.begin(),vec.end(),cmp1);
int ans=0;
for(int i=0;i<min(k,(int)(vec.size()));i++)ans+=vec[i];
cout<<ans<<'\n';
return 0;
}
T9
神秘结论题
题意
有 \(n\) 个人,给出 \(m\) 个关系表示两个人不互相憎恶,其中 \(k\) 个人能进行一次会议当且仅当:
-
\(k\) 是 \(>1\) 的奇数
-
这 \(k\) 个人坐一圈能满足任意相邻两人不相互憎恶
问有多少个人不能参加任何一场会议
题解
一次会议等价于一个奇环,问题等价于求有多少个点不在任意一个奇环内
结论:若一个点双内包含奇环,则该点双内所有点总是存在于至少一个简单奇环上
结论证明的核心:奇环的另一种表述——对于奇环上的任意两个结点,总存在两条不同的路径,使得其经过的边数奇偶性不同!
有了上面的结论,就可以直接把点双拎出来
判断奇环经典二分图染色即可
代码
结论题还是太需要脑子了,好在不怎么费手
点击查看代码
#include<bits/stdc++.h>
#define vi vector<int>
#define pb push_back
using namespace std;
const int N=1e5+5,M=1e6+5;
int n,m,head[N],tot=1,rt;
struct Edge{int to,nxt;}e[M<<1];
int dfn[N],low[N],tim,col[N],cnt;
int stk[N],tp;vi dcc[N];
bool flag,mrk[N];int idx[N],c[N],now;
inline void add(int u,int v){
e[++tot]={v,head[u]};head[u]=tot;
return;
}
inline void tarjan(int u){
dfn[u]=low[u]=++tim;
if(u==rt&&head[u]==0)dcc[++cnt].pb(u);
stk[++tp]=u;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(!dfn[v]){
tarjan(v);
low[u]=min(low[u],low[v]);
if(low[v]>=dfn[u]){
cnt++;
int now=-1;
while(now!=v){
now=stk[tp--];
dcc[cnt].pb(now);
}
dcc[cnt].pb(u);
}
}else low[u]=min(low[u],dfn[v]);
}
return;
}
inline void dfs(int u,int col){
if(flag)return;
c[u]=col;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(idx[v]!=now)continue;
if(c[v]==col){flag=true;return;}
if(!c[v])dfs(v,3-col);
}
return;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=1;i<=m;i++){
int u,v;cin>>u>>v;
if(u==v)continue;
add(u,v),add(v,u);
}
for(int i=1;i<=n;i++)
if(!dfn[i])rt=i,tarjan(i);
for(int i=1;i<=cnt;i++){
now=i;flag=false;
for(auto x:dcc[i])idx[x]=now,c[x]=0;
dfs(dcc[i][0],1);
if(!flag)continue;
for(auto x:dcc[i])mrk[x]=true;
}
int ans=0;
for(int i=1;i<=n;i++)
if(!mrk[i])ans++;
cout<<ans<<'\n';
return 0;
}
T10
居然自己灵光一闪想对了,开心!
题意
给定一个无向图,其中每个点的度数 \(\le 3\)。若每条边的容量为 \(1\),设 \(f_{x,y}\) 表示 \(x \to y\) 的最大流(或最小割),求 \(\sum \limits_{x<y} f(x,y)\)
题解
观测到 \(f(x,y) \le 3\),考虑对 \(f(x,y) = 0/1/2/3\) 计数
\(f(x,y)=0\) 等价于判连通
\(f(x,y)=1\) 等价于边双缩点
\(f(x,y)=2/3\) 没有直接思路,观测到 \(n,m\) 并非 \(10^5\),可以搞一个 \(O(nm)\) 量级的算法
枚举割边,对 \(f(x,y)=2\) 转化为 \(f(x,y)=1\),跑边双
对于每一个结点 \(u\),维护 \(O(m)\) 次边双编号
容易发现,\(f(x,y)=3\) 当且仅当,\(x,y\) 的由边双编号构成的序列是相同的,直接序列哈希维护
代码
(时间紧迫,没有代码)
T11
这能是黑题?思维难度顶天上位蓝
题意
给定一个推箱子游戏的局面,和 \(Q\) 次询问:是否能将箱子推到指定位置
题解
条件反射地,搞一个状态 \(f_{x,y,d}=0/1\) 表示箱子在 \((x,y)\),人在 \(d\) 方向的情况 是 / 否 存在
转移是需要判断箱子不动的时候,人是否可以移动到相应方向的
容易发现这个东西很点双,预处理即可
代码
(时间紧迫……)
T12
不会咋办?咕咕咕——
后记
世界孤立我任它奚落
完结撒花!

浙公网安备 33010602011771号