8/21 牛客补题+cf思维+tarjan
Posted 钟钟终
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了8/21 牛客补题+cf思维+tarjan相关的知识,希望对你有一定的参考价值。
P3398 仓鼠找 sugar
题意:在一棵树上,给定四个点,在a和b的最短路径中能否在c和d的最短路径中相遇。
思路:可看出题目给了伪标签,不是tarjan的题。一道倍增lca的题。
1.若要满足相遇,则需要a和b的lca在c和d的最短路径上;或者c和d的lca在a和b的最短路径上。
2.在树中,判断一个点是否在一条路径上,可采用距离公式:dep[x]+dep[y]-2*dep[lca(x,y)]
3.满足条件为:DIS(c,x)+DIS(d,x)==DIS(c,d)
,x为a和b的lca。
代码:
#include<bits/stdc++.h>
#define int long long
#define endl '\\n'
#define For(i,a,b) for(i=(a);i<=(b);++i)
#define ios (ios::sync_with_stdio(false),cin.tie(0),cout.tie(0))
using namespace std;
const int N=7e5+7;
const int inf=0x3f3f3f3f;
const int mod=1e9+7;
int n,q;
int dep[N]; //存u点的深度
int fa[N][20]; //存从u点向上跳2^i层的祖先节点
vector<int>e[N];
void dfs(int u,int pare)
dep[u]=dep[pare]+1;
fa[u][0]=pare;
for(int i=1;i<=19;i++)
fa[u][i]=fa[fa[u][i-1]][i-1];
for(int v:e[u])
if(v!=pare) dfs(v,u);
//利用st表求lca
int lca(int u,int v)
if(dep[u]<dep[v]) swap(u,v);
for(int i=19;i>=0;i--)
//先跳到同一层
if(dep[fa[u][i]]>=dep[v])
u=fa[u][i];
if(u==v) return v;
//跳到lca的下一层
for(int i=19;i>=0;i--)
if(fa[u][i]!=fa[v][i])
u=fa[u][i],v=fa[v][i];
return fa[u][0];
int DIS(int x,int y)
return dep[x]+dep[y]-2*dep[lca(x,y)];
bool check(int a,int b,int c,int d)
int x=lca(a,b);
if(DIS(c,x)+DIS(d,x)==DIS(c,d))
return 1;
return 0;
void solve()
cin>>n>>q;
for(int i=1;i<n;i++)
int u,v;cin>>u>>v;
e[u].push_back(v);
e[v].push_back(u);
dfs(1,0);
while(q--)
int a,b,c,d;
cin>>a>>b>>c>>d;
if(check(a,b,c,d)||check(c,d,a,b))
cout<<"Y"<<endl;
else
cout<<"N"<<endl;
signed main()
//ios;
//int T;cin>>T;
//while(T--)
solve();
return 0;
P2746 [USACO5.3]校园网Network of Schools
题意:为了使所有学校都用上软件,需要给几个学校发送软件;若是给任意一个学校发送了软件,就会分发搭配各个学校,则最少需要添加几条通信线路
思路:和之前做过的一道题类似。
1.先将各个强连通分量中的点看作一个点,进行tarjan的缩点操作。
2.根据原图建立缩点后的拓补图。
3.对于问题二,是将该拓扑图变成一个环,最少需要添加多少条边,取入读为0和出度为0中的较大值。
代码:
#include<bits/stdc++.h>
#define int long long
#define endl '\\n'
#define For(i,a,b) for(i=(a);i<=(b);++i)
#define ios (ios::sync_with_stdio(false),cin.tie(0),cout.tie(0))
using namespace std;
const int N=7e5+7;
const int inf=0x3f3f3f3f;
const int mod=1e9+7;
vector<int>e[N],ne[N];
int dfn[N],low[N],tot;
int stk[N],instk[N],top;
int scc[N],siz[N],cnt;
int n,m,a[N],din[N],dout[N];
void tarjan(int x)
//盖戳、入栈
dfn[x]=low[x]=++tot;
stk[++top]=x,instk[x]=1;
for(int y:e[x])
if(!dfn[y])
tarjan(y);
//回x时及时更新low
low[x]=min(low[x],low[y]);
else if(instk[y]) //若x已访问且在栈中
low[x]=min(low[x],dfn[y]);
if(dfn[x]==low[x]) //若x时SCC的根
int y;++cnt;
do
y=stk[top--];instk[y]=0; //出栈
scc[y]=cnt; //SCC编号
++siz[cnt];
while(y!=x);
void solve()
cin>>n;
for(int i=1,x;i<=n;i++)
while(cin>>x&&x)
e[i].push_back(x);
for(int i=1;i<=n;i++)
if(!dfn[i]) tarjan(i);
for(int i=1;i<=n;i++)
for(int j:e[i])
if(scc[i]!=scc[j])
din[scc[j]]++,dout[scc[i]]++;
int ans1=0,ans2=0;
for(int i=1;i<=cnt;i++)
if(!din[i]) ans1++;
if(!dout[i]) ans2++;
cout<<ans1<<endl;
if(cnt==1)
cout<<0<<endl;
else
cout<<max(ans1,ans2)<<endl;
signed main()
//ios;
//int T;cin>>T;
//while(T--)
solve();
return 0;
C. 3SUM Closure
题意:给定一个数组中,是否存在任意三个数相加都为数组中元素,数据范围是2e5。
思维:
1.首先暴力肯定被排除。此外可看出若出现三个以上正数或者负数,则一定会出现三个数相加结果
2.此外若出现元素0,出现两个在意义上即可以代表所有的0.
3.因此可筛选元素进行暴力。
#include<bits/stdc++.h>
#define int long long
#define endl '\\n'
#define For(i,a,b) for(i=(a);i<=(b);++i)
#define ios (ios::sync_with_stdio(false),cin.tie(0),cout.tie(0))
using namespace std;
const int N=7e5+7;
const int inf=0x3f3f3f3f;
const int mod=1e9+7;
int n,a[N];
vector<int>v1,v2,v3;
void solve()
v1.clear();v2.clear();v3.clear();
cin>>n;
int a1=0,a2=0;
for(int i=1;i<=n;i++)
cin>>a[i];
if(a[i]>0) a1++,v1.push_back(a[i]);
if(a[i]<0) a2++,v2.push_back(a[i]);
if(a[i]==0&&v3.size()<2) v3.push_back(a[i]);
if(a1>2||a2>2)
cout<<"NO"<<endl;return;
for(int i:v1) v3.push_back(i);
for(int i:v2) v3.push_back(i);
int g=v3.size();
for(int i=0;i<g;i++)
for(int j=i+1;j<g;j++)
for(int k=j+1;k<g;k++)
int flag=0;
for(int c=0;c<g;c++)
if(v3[i]+v3[j]+v3[k]==v3[c])
flag=1;
if(!flag)
cout<<"NO"<<endl;return;
cout<<"YES"<<endl;
signed main()
//ios;
int T;cin>>T;
while(T--)
solve();
return 0;
C. Monoblock
题意:数值相同的块可合并,将任意段区间的块数累加,输出结果。每次可修改一个位置的值。
思路:算最近做的最难的一道思维题了。
1.数字的值仅和相邻位置的值有关,因此每次修改都要考虑相邻的值即可。
2.若a[x]==y,则直接输出答案;若不相等,需要和相邻两个元素值进行讨论。
3.对于两边的值的比较,在于影响到的区间数目。具体见代码:
#include<bits/stdc++.h>
#define int long long
#define endl '\\n'
#define For(i,a,b) for(i=(a);i<=(b);++i)
#define ios (ios::sync_with_stdio(false),cin.tie(0),cout.tie(0))
using namespace std;
const int N=7e5+7;
const int inf=0x3f3f3f3f;
const int mod=1e9+7;
int n,m,a[N];
void solve()
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>a[i];
a[0]=a[n+1]=-1;
int suf=1,ans=1,ls=0;
for(int i=2;i<=n;i++)
if(a[i]!=a[i-1]) suf+=i,ans+=suf;
else suf++,ans+=suf;
while(m--)
int x,y;cin>>x>>y;
if(a[x]==y)
cout<<ans<<endl;continue;
if(y==a[x-1]&&a[x]!=a[x-1]) ans-=(n-x+1)*(x-1);
if(y!=a[x-1]&&a[x]==a[x-1]) ans+=(n-x+1)*(x-1);
if(y==a[x+1]&&a[x]!=a[x+1]) ans-=(n-x)*x;
if(y!=a[x+1]19香港补题(G)+cf思维