Vjudge 20220427 练习13 总结
written on 2022-04-28
(图论专场)
首先,这个交了13遍才过的A题,让我不由得感慨输入的重要性。。
先上样例和题面的样例描述:
The input contains several data sets in text format. Each data set represents one set of subjects of the study, with the following description:(多组数据)
the number of students
the description of each student, in the following format
student_identifier:(number_of_romantic_relations) student_identifier1 student_identifier2 student_identifier3 ...
or
student_identifier:(0)
7
0: (3) 4 5 6
1: (2) 4 6
2: (0)
3: (0)
4: (2) 0 1
5: (1) 0
6: (2) 0 1
3
0: (2) 1 2
1: (1) 0
2: (1) 0
刚开始脑抽了,居然用字符串读入,又长又臭的代码就不贴了,正确的读入方式,应该是直接
scanf("%d: (%d)",&x,&num);
这样就行了。。以后一定要吸取教训。。。
还有一点,就是如果加双向边,你可以不用染色,在编号不重复的情况下,最后最大匹配除以2就是染色后的答案,这样会更方便一点。
B题比较套路,题面显然的二分答案,然后弗洛伊德,建图后,上二分图最大匹配就好了,就是要建虚点这样来保证一个挤奶机可以多用
C题考场上居然没写出来,第一个条件显然强连通上tarjan缩点,然后它就变成了一个有向无环图。
考虑第二个条件,我们可以画个图理解一下( 1->2 , 1->3 , 2->4 , 3->4 ),为了满足这个条件,就必须满足一个性质,即:
路径上除终点外,从每个顶点出发只有一条边指向路径上的另一顶点。
这不就是最小路径覆盖的性质吗?所以直接拆点建二分图,求最大匹配,再用总数相减就是答案了
D题是以前没有做过的新题型,线段树优化建图,总的来说思路就是利用线段树的一个节点对应一个区间的思想来建图,这样,虽然总结点数变成了原来的 \(2*2=4\) 倍左右,但是总建边数变少了,这样再跑最短路,就能获得解答了
code
#include<bits/stdc++.h>
#define N 400005
#define M 2000005
using namespace std;
typedef long long ll;
int n,T,st;
int tot,ver[M],nxt[M],head[N];
ll edge[M];
void add_E(int x,int y,ll z){ver[++tot]=y,edge[tot]=z,nxt[tot]=head[x],head[x]=tot;}
int id1[N],id2[N],In[N],Out[N];
struct Seg
{
int tot;
void build(int p,int l,int r)
{
id1[p]=++tot,id2[p]=++tot;//id1:son to fa(little to big)
if(l==r)
{
In[l]=id1[p],Out[l]=id2[p];
add_E(Out[l],In[l],0);//从最后那个点然后还能回来继续走一个循环
return ;
}
int mid=l+r>>1;
build(p<<1,l,mid),build(p<<1|1,mid+1,r);
add_E(id1[p<<1],id1[p],0),add_E(id1[p<<1|1],id1[p],0);
add_E(id2[p],id2[p<<1],0),add_E(id2[p],id2[p<<1|1],0);
}
void update(int p,int l,int r,int L,int R,ll v,bool flag,int x)
{
if(L<=l&&R>=r)
{
if(!flag) add_E(In[x],id2[p],v);
else add_E(id1[p],Out[x],v);
return ;
}
int mid=l+r>>1;
if(L<=mid) update(p<<1,l,mid,L,R,v,flag,x);
if(R>mid) update(p<<1|1,mid+1,r,L,R,v,flag,x);
}
}t1;
struct F
{
int x;
ll v;
bool operator<(const F &p) const{return v>p.v;}
};
priority_queue<F> q;
ll dis[N];
bool mark[N];
void dijkstra()
{
memset(dis,0x3f,sizeof(dis));
dis[In[st]]=dis[Out[st]]=0,q.push((F){In[st],0});
while(q.size())
{
int x=q.top().x;
q.pop();
if(mark[x]) continue;
mark[x]=1;
for(int i=head[x];i;i=nxt[i])
{
int y=ver[i];
ll z=edge[i];
if(dis[y]>dis[x]+z)
{
dis[y]=dis[x]+z;
q.push((F){y,dis[y]});
}
}
}
}
int main()
{
scanf("%d%d%d",&n,&T,&st);
t1.tot=0,t1.build(1,1,n);
while(T--)
{
int op,x;
ll w;
scanf("%d%d",&op,&x);
if(op==1)
{
int y;
scanf("%d%lld",&y,&w);
add_E(In[x],Out[y],w);
continue;
}
int l,r;
scanf("%d%d%lld",&l,&r,&w);
if(op==2) t1.update(1,1,n,l,r,w,0,x);//x->l,r
else t1.update(1,1,n,l,r,w,1,x);
}
dijkstra();
for(int i=1;i<=n;i++)
{
if(dis[Out[i]]>=1e16) printf("-1 ");
else printf("%lld ",dis[Out[i]]);
}
}
具体细节见代码