【做题记录】图论相关问题
A. 牛场围栏
首先判断 -1
的情况。
- 如果可用的长度中有 \(1\),那么所有长度都能拼出来。
- 如果所有可用长度的 \(gcd\) 不为 \(1\),那一定没有最大值。
- 证明:设 \(gcd\) 为 \(q\),则 \(q\mid x_1a_1+x_2a_2+\dots+x_na_n,x_1,x_2,\dots,x_n\in\mathbb{Z}\)。那么对于不能被 \(q\) 整除的数,就一定拼不出来。
然后考虑最小的能拼出来的数,显然就是最小的可用长度,记为 \(mina\),考虑它的每个剩余系 \(K_i,i=0,1,\dots mina-1\),因为有解,所以一定存在一个最小的能拼出来的 \(t_i=mina\times p+i\),因为 \(mina\) 是最小的所以 \(p\) 一定大于 \(0\)。则对于 \(K_i\) 最大的拼不出来的数就是 \(t_i-mina\)。\(t_i\) 可用同余最短路来求,就是说每个 \(K_i\) 都是一个点,每个可用长度都是一条边。使用朴素 Dijkstra,时间复杂度为 \(O(mina^2)\)。
Code
复制代码
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
using namespace std;
namespace asbt{
namespace cplx{bool begin;}
const int maxn=3e3+5,inf=0x3f3f3f3f;
int n,m,a[maxn],mina=maxn,dis[maxn];
bool vis[maxn];
vector<pii> e[maxn];
il void wuj(){
int tmp=0;
for(int i=1;i<=3e3;i++){
if(vis[i]){
tmp=gcd(tmp,i);
}
}
if(tmp>1||vis[1]){
puts("-1");
exit(0);
}
}
namespace cplx{
bool end;
il double usdmem(){return (&begin-&end)/1048576.0;}
}
int main(){
read(n)read(m);
for(int i=1;i<=n;i++){
read(a[i]);
for(int j=0;j<=min(m,a[i]);j++){
vis[a[i]-j]=1;
}
}
for(int i=1;i<3e3;i++){
if(vis[i]){
mina=i;
break;
}
}
wuj();
memset(dis,0x3f,sizeof dis);
for(int i=1;i<3e3;i++){
if(!vis[i]){
continue;
}
dis[i%mina]=min(dis[i%mina],i);
for(int j=0;j<mina;j++){
e[j].pb(mp((j+i)%mina,i));
}
}
memset(vis,0,sizeof vis);
for(int i=1,u,v,w,mind;i<=mina;i++){
mind=inf;
for(int j=0;j<mina;j++){
if(mind>dis[j]&&!vis[j]){
mind=dis[j],u=j;
}
}
vis[u]=1;
for(pii j:e[u]){
v=j.fir,w=j.sec;
if(!vis[v]&&dis[v]>dis[u]+w){
dis[v]=dis[u]+w;
}
}
}
int ans=0;
for(int i=0;i<mina;i++){
ans=max(ans,dis[i]-mina);
}
printf("%d",ans);
return 0;
}
}
int main(){return asbt::main();}
B. 「LNOI2014」LCA
将深度转化,其实就是这个点到根的路径上的点的个数。
对于每个询问 \(l,r,u\),将 \(\forall i\in[l,r]\) 到根都权值加一,再求 \(u\) 到根的权值和。树剖 \(+\) 线段树。
考虑离线,每个询问在 \(l-1\) 处减,在 \(r\) 处加,从 \(1\) 到 \(n\) 扫一遍就行了。
Code
复制代码
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
using namespace std;
namespace asbt{
namespace cplx{bool begin;}
const int maxn=5e4+5,mod=201314;
int n,m,fa[maxn],ans[maxn];
int dfn[maxn],cnt;
int top[maxn],sz[maxn],hes[maxn];
vector<int> e[maxn];
vector<pii> hao[maxn];
il void dfs1(int u){
sz[u]=1;
int mxs=0;
for(int v:e[u]){
dfs1(v);
sz[u]+=sz[v];
if(mxs<sz[v]){
mxs=sz[v];
hes[u]=v;
}
}
}
il void dfs2(int u){
dfn[u]=++cnt;
if(!top[u]){
top[u]=u;
}
if(hes[u]){
top[hes[u]]=top[u];
dfs2(hes[u]);
}
for(int v:e[u]){
if(v!=hes[u]){
dfs2(v);
}
}
}
int sum[maxn<<2],tag[maxn<<2];
il void pushup(int id){
sum[id]=(sum[lid]+sum[rid])%mod;
}
il void pushtag(int id,int l,int r,int val){
(tag[id]+=val)%=mod;
(sum[id]+=(r-l+1)*val)%=mod;
}
il void pushdown(int id,int l,int r){
if(tag[id]){
int mid=(l+r)>>1;
pushtag(lid,l,mid,tag[id]);
pushtag(rid,mid+1,r,tag[id]);
tag[id]=0;
}
}
il void upd(int id,int L,int R,int l,int r,int val){
if(L>=l&&R<=r){
pushtag(id,L,R,val);
return ;
}
pushdown(id,L,R);
int mid=(L+R)>>1;
if(l<=mid){
upd(lid,L,mid,l,r,val);
}
if(r>mid){
upd(rid,mid+1,R,l,r,val);
}
pushup(id);
}
il int query(int id,int L,int R,int l,int r){
if(L>=l&&R<=r){
return sum[id];
}
pushdown(id,L,R);
int mid=(L+R)>>1,res=0;
if(l<=mid){
(res+=query(lid,L,mid,l,r))%=mod;
}
if(r>mid){
(res+=query(rid,mid+1,R,l,r))%=mod;
}
return res;
}
il void upd(int u,int val){
while(top[u]!=1){
upd(1,1,n,dfn[top[u]],dfn[u],val);
u=fa[top[u]];
}
upd(1,1,n,dfn[1],dfn[u],val);
}
il int query(int u){
int res=0;
while(top[u]!=1){
(res+=query(1,1,n,dfn[top[u]],dfn[u]))%=mod;
u=fa[top[u]];
}
return (res+query(1,1,n,dfn[1],dfn[u]))%mod;
}
namespace cplx{
bool end;
il double usdmem(){return (&begin-&end)/1048576.0;}
}
signed main(){
read(n)read(m);
for(int i=2;i<=n;i++){
read(fa[i]);
e[++fa[i]].pb(i);
}
dfs1(1),dfs2(1);
// for(int i=1;i<=n;i++){
// cout<<dfn[i]<<" ";
// }
// puts("");
for(int i=1,l,r,u;i<=m;i++){
read(l)read(r)read(u);
u++;
hao[l].pb(mp(-i,u)),hao[r+1].pb(mp(i,u));
}
for(int i=1;i<=n;i++){
upd(i,1);
for(pii j:hao[i]){
if(j.fir<0){
(ans[-j.fir]+=mod-query(j.sec))%=mod;
}
else{
(ans[j.fir]+=query(j.sec))%=mod;
}
}
}
for(int i=1;i<=m;i++){
printf("%lld\n",ans[i]);
}
return 0;
}
}
signed main(){return asbt::main();}
C. 跳跳棋
考虑每个状态 \((x,y,z),x<y<z\) 的转移:
- \(y\) 移到左边:\((2x-y,x,z)\)
- \(y\) 移到右边:\((x,z,2z-y)\)
- \(x\) 移到中间:\((y,2y-x,z)\)
- \(z\) 移到中间:\((x,2y-z,y)\)
第 \(3\)、\(4\) 个转移是矛盾的。具体地,设 \(d1=y-x\),\(d2=z-y\),若 \(d1<d2\) 则可以进行 第 \(3\) 个转移,若 \(d1>d2\) 则可以进行第 \(4\) 个转移,否则不可转移。
我们令 \((x,y,z)\) 进行了 \(3\) 或 \(4\) 号操作后的状态为它的父亲。则它的父亲可以通过第 \(1\) 或 \(2\) 个转移到达它。则对于每一个状态,可以找到它所属的二叉树。二叉树的根就是那个 \(d1=d2\) 的状态。而我们要求的就是两个状态在树上的距离(如果所在的树不一样则无解)。
求树上的距离的方式为求 \(lca\)。考虑如何去求。首先让两个状态走到同一深度,然后二分 \(lca\) 的高度,判断这个高度的祖先是否相同。
那么问题就在于如何快速求出某个状态的深度与 \(k\) 级祖先了。我们考虑压缩转移。以第 \(3\) 个转移为例,最多可以连续进行 \(\lfloor\frac{d2-1}{d1}\rfloor\) 次。设 \(tmp=\lfloor\frac{d2-1}{d1}\rfloor\),\(tmp\) 次转移后的状态即为 \((x+tmp\times d1,y+tmp\times d2,z)\)。这样我们最多跳 \(\log\max(d1,d2)\) 次。于是这两个操作的时间复杂度就都是 \(\log\) 级别的了。因为还有个二分,最终的复杂度是 \(\log^2\) 的。
Code
复制代码
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
using namespace std;
namespace asbt{
namespace cplx{bool begin;}
struct node{
int a[3];
il void init(){
sort(a,a+3);
}
il int&operator[](int x){
return a[x];
}
il bool operator==(node x)const{
return a[0]==x[0]&&a[1]==x[1]&&a[2]==x[2];
}
il bool operator!=(node x)const{
return !(*this==x);
}
};
il int dep(node x){
int d1=x[1]-x[0],d2=x[2]-x[1];
int res=0;
while(d1!=d2){
if(d1>d2){
int tmp=(d1-1)/d2;
x[1]-=tmp*d2,x[2]-=tmp*d2;
res+=tmp;
}
else{
int tmp=(d2-1)/d1;
x[0]+=tmp*d1,x[1]+=tmp*d1;
res+=tmp;
}
d1=x[1]-x[0],d2=x[2]-x[1];
}
return res;
}
il node ganc(node x,int k){
int d1=x[1]-x[0],d2=x[2]-x[1];
while(k){
if(d1>d2){
int tmp=min((d1-1)/d2,k);
x[1]-=tmp*d2,x[2]-=tmp*d2;
k-=tmp;
}
else{
int tmp=min((d2-1)/d1,k);
x[0]+=tmp*d1,x[1]+=tmp*d1;
k-=tmp;
}
d1=x[1]-x[0],d2=x[2]-x[1];
}
return x;
}
namespace cplx{
bool end;
il double usdmem(){return (&begin-&end)/1048576.0;}
}
signed main(){
node p,q;
read(p[0])read(p[1])read(p[2]);
read(q[0])read(q[1])read(q[2]);
p.init(),q.init();
int dp=dep(p),dq=dep(q);
// cout<<dp<<" "<<dq<<"\n";
if(ganc(p,dp)!=ganc(q,dq)){
puts("NO");
return 0;
}
if(dp<dq){
swap(dp,dq),swap(p,q);
}
p=ganc(p,dp-dq);
int l=0,r=dq;
while(l<r){
int mid=(l+r)>>1;
if(ganc(p,mid)==ganc(q,mid)){
r=mid;
}
else{
l=mid+1;
}
}
puts("YES");
printf("%lld",dp-dq+l*2);
return 0;
}
}
signed main(){return asbt::main();}
D. Berland and the Shortest Paths
对图进行 bfs,显然这样生成的树是满足要求的。
因此我们只需计算对于每个点,哪些边是从上一层连下来的。所有点这个边数的乘积就是总方案数。
输出方案就对每一个点枚举选哪条边即可。
Code
复制代码
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
using namespace std;
namespace asbt{
namespace cplx{bool begin;}
const int maxn=2e5+5;
int n,m,tot,num[maxn];
int vis[maxn],dep[maxn];
vector<int> hao[maxn];
vector<pii> e[maxn];
queue<int> q;
il void dfs(int u){
if(u>n){
for(int i=1;i<=m;i++){
printf("%d",vis[i]);
}
puts("");
if(--tot==0){
exit(0);
}
return ;
}
for(int x:hao[u]){
vis[x]=1;
dfs(u+1);
vis[x]=0;
}
}
namespace cplx{
bool end;
il double usdmem(){return (&begin-&end)/1048576.0;}
}
int main(){
read(n)read(m)read(tot);
for(int i=1,u,v;i<=m;i++){
read(u)read(v);
e[u].pb(mp(v,i));
e[v].pb(mp(u,i));
}
dep[1]=1,q.push(1);
while(q.size()){
int u=q.front();
q.pop();
for(pii i:e[u]){
int v=i.fir,w=i.sec;
if(!dep[v]){
dep[v]=dep[u]+1;
num[v]++,hao[v].pb(w);
q.push(v);
}
else if(dep[u]+1==dep[v]){
num[v]++,hao[v].pb(w);
}
}
}
ll res=1;
for(int i=2;i<=n;i++){
res*=num[i];
if(res>=tot){
break;
}
}
tot=min(tot*1ll,res);
printf("%d\n",tot);
dfs(2);
return 0;
}
}
int main(){return asbt::main();}
E. Breaking Good
首先 bfs 搞出最短路。
设最短路为 \(dep\),本来完好的边数为 \(sum\),则对于一条最短路径 \(i\),设其中本来完好的边数为 \(cnt\),则答案为 \((dep-cnt)+(sum-cnt)\)。所以只需让 \(cnt\) 最大。在 bfs 时转移即可。题目还要求输出方案,因此需要记录路径。
Code
复制代码
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
using namespace std;
namespace asbt{
namespace cplx{bool begin;}
const int maxn=1e5+5;
int n,m,enm,dep[maxn];
int cnt[maxn],hd[maxn],pre[maxn];
bool vis[maxn],zai[maxn],hp[maxn];
queue<int> q;
struct edge{
int v,w,id,nxt;
}e[maxn<<1];
il void addedge(int u,int v,int w,int id){
e[++enm]=(edge){v,w,id,hd[u]};
hd[u]=enm;
}
namespace cplx{
bool end;
il double usdmem(){return (&begin-&end)/1048576.0;}
}
int main(){
read(n)read(m);
int sum=0;
for(int i=1,u,v,w;i<=m;i++){
read(u)read(v)read(w);
addedge(u,v,w,i);
addedge(v,u,w,i);
sum+=w;
}
memset(cnt,-1,sizeof cnt);
cnt[1]=0;
vis[1]=1,q.push(1);
while(q.size()){
int u=q.front();
q.pop();
for(int i=hd[u],v,w;i;i=e[i].nxt){
v=e[i].v,w=e[i].w;
if(vis[v]&&dep[v]<=dep[u]){
continue;
}
if(!vis[v]){
dep[v]=dep[u]+1;
vis[v]=1,q.push(v);
}
if(cnt[v]<cnt[u]+w){
cnt[v]=cnt[u]+w;
pre[v]=u;
}
}
}
printf("%d\n",dep[n]+sum-2*cnt[n]);
// for(int i=1;i<=n;i++){
// cout<<pre[i]<<"\n";
// }
int u=n;
while(u!=1){
for(int i=hd[pre[u]],v,w,id;i;i=e[i].nxt){
v=e[i].v,w=e[i].w,id=e[i].id;
if(v==u){
zai[id]=1;
if(!w){
printf("%d %d %d\n",pre[u],u,1);
}
break;
}
}
u=pre[u];
}
for(u=1;u<=n;u++){
for(int i=hd[u],v,w,id;i;i=e[i].nxt){
v=e[i].v,w=e[i].w,id=e[i].id;
if(hp[id]){
continue;
}
hp[id]=1;
if(!zai[id]&&w){
printf("%d %d %d\n",u,v,0);
}
}
}
return 0;
}
}
int main(){return asbt::main();}
F. Turtle and Intersected Segments
对于三个能互相连边的点 \((l_i,r_i,a_i),(l_j,r_j,a_j),(l_k,r_k,a_k)\),其中 \(a_i\le a_j\le a_k\),我们显然只需要将 \(i\) 和 \(j\) 连边,\(j\) 和 \(k\) 连边就够了。
于是进行扫描线,加入每个点时去找它的 \(a\) 值的前驱和后继连边。用 set
维护。
set::insert
的返回值为一个 pair
,其中 first
为插入的这个值的迭代器,second
是一个 bool
,表示有没有插入成功(因为 set
去重)。类似地 multiset
只返回一个迭代器。
可以用 prev(it)
返回 \(it\) 的前一个迭代器,next
返回后一个。
Code
复制代码
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
using namespace std;
namespace asbt{
namespace cplx{bool begin;}
const int maxn=5e5+5;
int n,lsh[maxn<<1];
int fa[maxn],sz[maxn];
struct node{
int l,r,a;
}p[maxn];
vector<int> hao[maxn<<1];
struct cmp{
il bool operator()(const int &x,const int &y)const{
if(p[x].a==p[y].a){
return x>y;
}
return p[x].a<p[y].a;
}
};
set<int,cmp> hp;
struct edge{
int u,v,w;
il bool operator<(const edge &x)const{
return w<x.w;
}
}e[maxn<<1];
il int find(int x){
return x!=fa[x]?fa[x]=find(fa[x]):x;
}
il void merge(int u,int v){
u=find(u),v=find(v);
if(u==v){
return ;
}
if(sz[u]>sz[v]){
sz[u]+=sz[v],fa[v]=u;
}
else{
sz[v]+=sz[u],fa[u]=v;
}
}
il void solve(){
read(n);
int tot=0;
for(int i=1;i<=n;i++){
read(p[i].l)read(p[i].r)read(p[i].a);
lsh[++tot]=p[i].l;
lsh[++tot]=p[i].r;
}
sort(lsh+1,lsh+tot+1);
tot=unique(lsh+1,lsh+tot+1)-lsh-1;
for(int i=1;i<=n;i++){
hao[lwrb(lsh+1,lsh+tot+1,p[i].l)-lsh].pb(i);
hao[lwrb(lsh+1,lsh+tot+1,p[i].r)-lsh].pb(-i);
}
for(int i=1;i<=tot;i++){
sort(hao[i].begin(),hao[i].end(),greater<int>());
}
hp.clear();
int num=0;
for(int i=1;i<=tot;i++){
for(int u:hao[i]){
if(u<0){
hp.erase(-u);
continue;
}
auto tmp=hp.insert(u).first;
if(tmp!=hp.begin()){
e[++num]=(edge){*prev(tmp),u,p[u].a-p[*prev(tmp)].a};
}
if(next(tmp)!=hp.end()){
e[++num]=(edge){u,*next(tmp),p[*next(tmp)].a-p[u].a};
}
}
}
for(int i=1;i<=n;i++){
fa[i]=i,sz[i]=1;
}
sort(e+1,e+num+1);
int cnt=0,ans=0;
for(int i=1;i<=num;i++){
if(find(e[i].u)!=find(e[i].v)){
merge(e[i].u,e[i].v);
cnt++,ans+=e[i].w;
}
}
printf("%lld\n",cnt==n-1?ans:-1);
for(int i=1;i<=tot;i++){
hao[i].clear();
}
}
namespace cplx{
bool end;
il double usdmem(){return (&begin-&end)/1048576.0;}
}
signed main(){
int T;
read(T);
while(T--){
solve();
}
return 0;
}
}
signed main(){return asbt::main();}
G. MST Company
首先将所有与 \(1\) 相连的边去掉,求一个最小生成森林。然后对于每个连通块,从 \(1\) 向它连一条最小的边。此时如果边不够或需要连的边超出限制就无解。
然后去满足 \(k\) 的限制。从没有被选中的从 \(1\) 连出的边中选一条连上,则要从形成的这个环上删去一条边(端点不能为 \(1\))。显然删去最大的一条。设新连的边为 \(w1\),最大的边为 \(w2\),则要求 \(w1-w2\) 最小。每次 \(O(n)\) 扫一遍即可。总复杂度 \(O(nk)\)。
Code
复制代码
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
using namespace std;
namespace asbt{
namespace cplx{bool begin;}
const int maxn=2e5+5;
const int inf=0x3f3f3f3f;
int n,m,num,enm=1,hd[maxn];
int fa[maxn],sz[maxn];
vector<int> vec,ans;
struct edge{
int u,v,w,id,nxt;
il bool operator<(const edge &x)const{
return w<x.w;
}
}a[maxn],e[maxn];
il void addedge(int u,int v,int w,int id){
e[++enm].v=v;
e[enm].w=w;
e[enm].id=id;
e[enm].nxt=hd[u];
hd[u]=enm;
}
il int find(int x){
return x!=fa[x]?fa[x]=find(fa[x]):x;
}
il void merge(int u,int v){
u=find(u),v=find(v);
if(u==v){
return ;
}
if(sz[u]>sz[v]){
sz[u]+=sz[v],fa[v]=u;
}
else{
sz[v]+=sz[u],fa[u]=v;
}
}
int mx[maxn];
bool ban[maxn],no[maxn];
il void dfs(int u,int fa){
// cout<<u<<"\n";
for(int i=hd[u],v;i;i=e[i].nxt){
v=e[i].v;
if(ban[i]||v==fa||v==1){
continue;
}
mx[v]=e[mx[u]].w>e[i].w?mx[u]:i;
dfs(v,u);
}
}
namespace cplx{
bool end;
il double usdmem(){return (&begin-&end)/1048576.0;}
}
int main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>n>>m>>num;
for(int i=1;i<=m;i++){
cin>>a[i].u>>a[i].v>>a[i].w;
if(a[i].u>a[i].v){
swap(a[i].u,a[i].v);
}
a[i].id=i;
}
sort(a+1,a+m+1);
for(int i=1;i<=n;i++){
fa[i]=i,sz[i]=1;
}
for(int i=1;i<=m;i++){
if(a[i].u==1){
continue;
}
if(find(a[i].u)!=find(a[i].v)){
merge(a[i].u,a[i].v);
addedge(a[i].u,a[i].v,a[i].w,a[i].id);
addedge(a[i].v,a[i].u,a[i].w,a[i].id);
ans.pb(a[i].id);
}
}
for(int i=1;i<=m;i++){
if(a[i].u==1){
if(find(a[i].u)==find(a[i].v)){
vec.pb(i);
}
else{
merge(a[i].u,a[i].v);
addedge(a[i].u,a[i].v,a[i].w,a[i].id);
addedge(a[i].v,a[i].u,a[i].w,a[i].id);
ans.pb(a[i].id);
num--;
}
}
}
if(num<0||num>vec.size()){
puts("-1");
return 0;
}
for(int i=1;i<n;i++){
if(find(i)!=find(i+1)){
puts("-1");
return 0;
}
}
for(int i=1;i<=num;i++){
// puts("666");
for(int j=1;j<=n;j++){
mx[j]=0;
}
for(int j=hd[1];j;j=e[j].nxt){
// cout<<e[j].v<<":\n";
dfs(e[j].v,1);
}
// for(int j=1;j<=n;j++){
// cout<<mx[j]<<" ";
// }
// cout<<"\n";
int mn=inf,tmp;
for(int j=0;j<vec.size();j++){
if(mn>a[vec[j]].w-e[mx[a[vec[j]].v]].w){
mn=a[vec[j]].w-e[mx[a[vec[j]].v]].w;
tmp=j;
}
}
// cout<<a[vec[tmp]].v<<"\n";
ban[mx[a[vec[tmp]].v]]=ban[mx[a[vec[tmp]].v]^1]=1;
no[e[mx[a[vec[tmp]].v]].id]=1;
// cout<<a[tmp].v<<"\n";
// cout<<a[mx[a[tmp].v]].id<<"\n";
addedge(a[vec[tmp]].u,a[vec[tmp]].v,a[vec[tmp]].w,a[vec[tmp]].id);
addedge(a[vec[tmp]].v,a[vec[tmp]].u,a[vec[tmp]].w,a[vec[tmp]].id);
ans.pb(a[vec[tmp]].id);
vec.erase(vec.begin()+tmp);
}
cout<<n-1<<"\n";
for(int i:ans){
if(!no[i]){
cout<<i<<" ";
}
}
return 0;
}
}
int main(){return asbt::main();}
H. DFS Trees
边权互不相同,则最小生成树只有一棵。
将最小生成树边打上标记,那么我们要求在以 \(i\) 点为根 dfs 时打上标记的边成为树边。换句话说,要求没被打上标记的边成为返祖边。
对于一条没被标记的边 \((u,v)\),我们要求 dfs 时能走完它所在的环上其它的边,设 \(dep_u<dep_v\),考虑两种情况:
- \(lca(u,v)=u\),则只有根在 \(v\) 的子树内或 \(u\) 对应 \(v\) 的儿子的子树外时满足。
- 否则,只有根在 \(u\) 或 \(v\) 的子树中时才可满足。
对于每条边给能满足的点权值加一,最后查询每个点的权值是否为 \(m-(n-1)\) 就好了。可以用树状数组。
Code
复制代码
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
using namespace std;
namespace asbt{
namespace cplx{bool begin;}
const int maxn=2e5+5;
const int inf=0x3f3f3f3f;
int n,m,cnt,anc[22][maxn];
int fa[maxn],sz[maxn],dep[maxn];
int top[maxn],dfn[maxn],hes[maxn];
bool vis[maxn];
pii a[maxn];
vector<pii> e[maxn];
struct dsu{
int fa[maxn],sz[maxn];
il void init(){
for(int i=1;i<=n;i++){
fa[i]=i,sz[i]=1;
}
}
il int find(int x){
return x!=fa[x]?fa[x]=find(fa[x]):x;
}
il void merge(int u,int v){
u=find(u),v=find(v);
if(u==v){
return ;
}
if(sz[u]>sz[v]){
sz[u]+=sz[v],fa[v]=u;
}
else{
sz[v]+=sz[u],fa[u]=v;
}
}
il bool check(int u,int v){
return find(u)==find(v);
}
}D;
il void dfs1(int u){
// cout<<u<<"\n";
anc[0][u]=fa[u];
for(int i=1;i<=20;i++){
anc[i][u]=anc[i-1][anc[i-1][u]];
}
dep[u]=dep[fa[u]]+1;
sz[u]=1;
int mxs=0;
for(pii i:e[u]){
int v=i.fir;
if(!vis[i.sec]||v==fa[u]){
continue;
}
fa[v]=u;
dfs1(v);
sz[u]+=sz[v];
if(mxs<sz[v]){
mxs=sz[v];
hes[u]=v;
}
}
}
il void dfs2(int u){
dfn[u]=++cnt;
if(!top[u]){
top[u]=u;
}
if(hes[u]){
top[hes[u]]=top[u];
dfs2(hes[u]);
}
for(pii i:e[u]){
int v=i.fir;
if(!vis[i.sec]||v==fa[u]||v==hes[u]){
continue;
}
dfs2(v);
}
}
il int lca(int u,int v){
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]]){
swap(u,v);
}
u=fa[top[u]];
}
return dep[u]<dep[v]?u:v;
}
il int ganc(int u,int k){
int tmp=0;
while(k){
if(k&1){
u=anc[tmp][u];
}
k>>=1,tmp++;
}
return u;
}
struct fenwick{
int tr[maxn];
il int lowbit(int x){
return x&-x;
}
il void upd(int pos,int val){
// cout<<pos<<"\n";
for(;pos<=n;pos+=lowbit(pos)){
tr[pos]+=val;
}
}
il int query(int pos){
int res=0;
for(;pos;pos-=lowbit(pos)){
res+=tr[pos];
}
return res;
}
}F;
namespace cplx{
bool end;
il double usdmem(){return (&begin-&end)/1048576.0;}
}
int main(){
read(n)read(m);
for(int i=1,u,v;i<=m;i++){
read(u)read(v);
a[i]=mp(u,v);
e[u].pb(mp(v,i));
e[v].pb(mp(u,i));
}
D.init();
for(int i=1;i<=m;i++){
if(!D.check(a[i].fir,a[i].sec)){
D.merge(a[i].fir,a[i].sec);
vis[i]=1;
}
}
// puts("666");
dfs1(1),dfs2(1);
// puts("777");
for(int i=1,u,v;i<=m;i++){
if(!vis[i]){
u=a[i].fir,v=a[i].sec;
if(dep[u]>dep[v]){
swap(u,v);
}
// cout<<u<<" "<<v<<"\n";
if(lca(u,v)==u){
F.upd(1,1);
u=ganc(v,dep[v]-dep[u]-1);
// puts("777");
F.upd(dfn[u],-1);
F.upd(dfn[u]+sz[u],1);
F.upd(dfn[v],1);
F.upd(dfn[v]+sz[v],-1);
}
else{
// puts("666");
F.upd(dfn[u],1);
// puts("777");
F.upd(dfn[u]+sz[u],-1);
// puts("888");
F.upd(dfn[v],1);
// puts("999");
F.upd(dfn[v]+sz[v],-1);
// puts("777");
}
}
}
for(int i=1;i<=n;i++){
printf("%d",F.query(dfn[i])==m-n+1);
}
return 0;
}
}
int main(){return asbt::main();}
I. The Shortest Statement
\(m-n\le20\),则当我们搞出一棵生成树时最多剩下 \(21\) 条边。相应地会对答案产生改变的点最多有 \(42\) 个。对这些点跑 dijkstra 即可。
Code
复制代码
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
using namespace std;
namespace asbt{
namespace cplx{bool begin;}
const int maxn=1e5+25;
int n,m,q;
int fa[maxn],sz[maxn];
int dep[maxn],hes[maxn];
int top[maxn],tis[maxn];
int dis[50][maxn];
bool vis[maxn],viss[maxn];
vector<pii> e[maxn];
struct edge{
int u,v,w;
}a[maxn];
il int find(int x){
return x!=fa[x]?fa[x]=find(fa[x]):x;
}
il void merge(int u,int v){
u=find(u),v=find(v);
if(u==v){
return ;
}
if(sz[u]>sz[v]){
sz[u]+=sz[v],fa[v]=u;
}
else{
sz[v]+=sz[u],fa[u]=v;
}
}
il void dfs1(int u){
dep[u]=dep[fa[u]]+1;
sz[u]=1;
int mxs=0;
for(pii i:e[u]){
int v=i.fir,w=i.sec;
if(v==fa[u]){
continue;
}
tis[v]=tis[u]+w;
fa[v]=u;
dfs1(v);
sz[u]+=sz[v];
if(mxs<sz[v]){
mxs=sz[v];
hes[u]=v;
}
}
}
il void dfs2(int u){
if(!top[u]){
top[u]=u;
}
if(hes[u]){
top[hes[u]]=top[u];
dfs2(hes[u]);
}
for(pii i:e[u]){
int v=i.fir,w=i.sec;
if(v==fa[u]||v==hes[u]){
continue;
}
dfs2(v);
}
}
il int tdis(int u,int v){
int tu=u,tv=v;
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]]){
swap(u,v);
}
u=fa[top[u]];
}
if(dep[u]>dep[v]){
swap(u,v);
}
return tis[tu]+tis[tv]-tis[u]*2;
}
il void dijkstra(int s,int *dis){
priority_queue<pii> q;
memset(viss,0,sizeof viss);
dis[s]=0,q.push(mp(0,s));
while(q.size()){
int u=q.top().sec,v,w;
q.pop(),viss[u]=1;
for(pii i:e[u]){
v=i.fir,w=i.sec;
if(!viss[v]&&dis[v]>dis[u]+w){
dis[v]=dis[u]+w;
q.push(mp(-dis[v],v));
}
}
}
}
namespace cplx{
bool end;
il double usdmem(){return (&begin-&end)/1048576.0;}
}
signed main(){
read(n)read(m);
for(int i=1,u,v,w;i<=m;i++){
read(u)read(v)read(w);
a[i]=(edge){u,v,w};
}
for(int i=1;i<=n;i++){
fa[i]=i,sz[i]=1;
}
for(int i=1,u,v;i<=m;i++){
u=a[i].u,v=a[i].v;
if(find(u)!=find(v)){
merge(u,v);
e[u].pb(mp(v,a[i].w));
e[v].pb(mp(u,a[i].w));
}
else{
vis[a[i].u]=vis[a[i].v]=1;
}
}
memset(fa,0,sizeof fa);
dfs1(1),dfs2(1);
for(int i=1;i<=n;i++){
e[i].clear();
}
for(int i=1,u,v,w;i<=m;i++){
u=a[i].u,v=a[i].v,w=a[i].w;
e[u].pb(mp(v,w));
e[v].pb(mp(u,w));
}
memset(dis,0x3f,sizeof dis);
int tot=0;
for(int i=1;i<=n;i++){
if(vis[i]){
dijkstra(i,dis[++tot]);
}
}
read(q);
while(q--){
int u,v;
read(u)read(v);
int res=tdis(u,v);
for(int i=1;i<=tot;i++){
res=min(res,dis[i][u]+dis[i][v]);
}
printf("%lld\n",res);
}
return 0;
}
}
signed main(){return asbt::main();}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步