第八天——图论

今天是集训的最后一天,是一个特别牛逼的大姐姐。她上课说就算把PPT给我们我们题也做不出来。我们上课说她肯定出题很没良心,她居然说她有良心的话还会安排到最后一天来上课吗。好吧我的确上课没听懂,题也不会我骗分骗了20分我特别高兴。

第一题:输并yes和no我居然都没分……这题每次对于添加边的操作将对应的区间看做节点边并连一条有向边。每次对于查询操作都从区间a开始跑一遍BFS,判断是否可以达到区间b。是则存在路径;否则不存在。

#include <bits/stdc++.h>
using namespace std;
struct elist
{
int v,last;
}e[MXM];
struct node
{
int a,b;
}num[MXN];
int n,tot,zh[MXN],etot;
bool vis[MXN];
queue <int> q;
void eadd(int u,int v)
{
e[++etot].v=v;
e[etot].last=zh[u];
zh[u]=etot;
}
bool bfs(int uu,int vv)
{
int u;
q.push(uu);
vis[uu]=0;
while(!q.empty())
 {
  u=q.front();q.pop();
  for(int i=zh[u];i;i=e[i].last)
    if(!vis[e[i].v])
      {
      if(e[i].v==vv){ while(!q.empty()) q.pop();return 1;}
      vis[e[i].v]=1;
      q.push(e[i].v);
        }
 }
    return 0;
}
void solve()
{
int t,x,y;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
 scanf("%d%d%d",&t,&x,&y);
 if(t==1)
   {
     num[++tot].a=x;num[tot].b=y;
     for(int j=1;j<=tot-1;j++){
      if((num[j].a<num[tot].a&&num[tot].a<num[j].b)|| (num[j].a<num[tot].b&&num[tot].b<num[j].b) )
        eadd(tot,j);//
      if((num[tot].a<num[j].a&&num[j].a<num[tot].b)|| (num[tot].a<num[j].b&&num[j].b<num[tot].b) )
        eadd(j,tot);//
      }
}
 else
   {
    memset(vis,0,sizeof(vis));
    if(bfs(x,y)) printf("YES\n");
    else printf("NO\n");
}
     }
}
int main()
{
    freopen("interval.in", "r", stdin);
    freopen("interval.out", "w", stdout);
solve();
    fclose(stdin);
    fclose(stdout);
    return 0;

}

第二题:二分答案,对于每个答案,将图中所有边权小于该答案的边删除,并判断是否可以从节点1到达节点n,若是,则说明该答案合法,在较大的一端继续二分;否则说明该答案不合法,在较小一端二分。

#include <bits/stdc++.h>
using namespace std;
struct elist {
int v,w,last;
}e[MXM];
int T,n,m,zh[MXN],etot;
bool flag[MXN];
queue <int> q;
inline void eadd(int u,int v,int w) {
e[++etot].v=v;e[etot].w=w;e[etot].last=zh[u];zh[u]=etot;
e[++etot].v=u;e[etot].w=w;e[etot].last=zh[v];zh[v]=etot;
}
inline bool Check(int x) {
int u;
//printf("%d\n",x);for(int i=1;i<=300000000;i++) ;
memset(flag,0,sizeof(flag));
u=1;q.push(u);
flag[u]=1;
while(!q.empty()) {
u=q.front();q.pop();
for(int i=zh[u];i;i=e[i].last) if(!flag[e[i].v]&&e[i].w>=x) {
flag[e[i].v]=1;
q.push(e[i].v);
}
}
return flag[n];
}
inline int Mybin(int low,int high) {
int ans=low;
for(int mid=(low+high)>>1;low<=high;(Check(mid)?(ans=max(ans,mid),low=mid+1):(high=mid-1)),mid=(low+high)>>1);
return ans;
}
inline void solve(){
int u,v,w,R;
scanf("%d%d",&n,&m);
memset(zh,0,sizeof(zh));etot=0;
R=0;
for(int i=1;i<=m;i++) {
scanf("%d%d%d",&u,&v,&w);
eadd(u,v,w);
R=max(R,w);
}
//printf("%d\n",R);
printf("%d\n",Mybin(0,R));
}
int main(){
freopen("maximum.in","r",stdin);
freopen("maximum.out","w",stdout);
solve();
fclose(stdin);
fclose(stdout);
return 0;

}

第三题:将每个零钱罐看做一个节点。对于每个零钱罐,向其钥匙所在的零钱罐连一条有向边。若要打开当前零钱罐,则要么打碎它,要么打开其指向的零钱罐。因此,遍历所有节点,每次从当前节点出发,不断标记节点。一直跑到第一个已经被标记过的节点。若当前被标记的节点是之前就被标记的则对于当前链而言,所有零钱罐在之前就已经被开启。否则需要且仅需要新打碎一个零钱罐。

#include <bits/stdc++.h>
using namespace std;
int n,pre[MXN],record[MXN],ans;
inline int getint(){
    char ch;
    while((ch=getchar())<'0'||ch>'9');
    int x=ch-'0';
    while((ch=getchar())>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0';
    return x;
}
inline void Init(){
    n=getint();
    for(int i=1;i<=n;i++) pre[i]=getint();
}
inline void solve(){
    int tot=0;
    Init();
    for(int i=1;i<=n;i++) if(!record[i]){
        int x=i;tot++;
        while(!record[x]){
            record[x]=tot;
            x=pre[x];
        }
        if(record[x]==tot) ans++;
    }
    printf("%d\n",ans);
}
int main(){
    freopen("money.in", "r", stdin);
    freopen("money.out", "w", stdout);
    solve();
    fclose(stdin);
    fclose(stdout);
    return 0;

}

第四题:通过对kruskal算法的分析可以发现,对于一个无向图而言,其所有最小生成树的相同边权的边数都相同,且组成了相同的联通分量。因此,从小到大对于每一条边,爆力枚举所有边的选择情况,计算所有可以构成合法解的方案。且每次在枚举后,都将相应的边加入当前生成树中。最后将所有边权的对应值相乘,即为结果。

#include <bits/stdc++.h>
using namespace std;
struct elist{
    int u,v,w;
}e[MXM];
int n,m,etot,father[MXN],h[MXN],record[MXN][2],rtot,total,ans,tot;
void eadd(int u,int v,int w){
    e[++etot].u=u;e[etot].v=v;e[etot].w=w;
}
void Init(){
    int u,v,w;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&u,&v,&w);
        eadd(u,v,w);
    }
}
int getfather(int x){return (father[x]==x)?x:(getfather(father[x]));}//////////
bool cmp(elist x,elist y){return x.w<y.w;}
void kruskal(){
    sort(e+1,e+m+1,cmp);
    for(int i=1;i<=n;i++) father[i]=i;
    for(int i=1;i<=m;i++){
        int fu=getfather(e[i].u),fv=getfather(e[i].v);
        if(fu==fv) continue;
        tot++;
        e[i].w==record[rtot][0]?record[rtot][1]++:(record[++rtot][1]=1,record[rtot][0]=e[i].w);//////////
        father[fu]=fv;
    }
}
void dfs(int step,int s,int t,int ff){
    for(int i=s;i<=t;i++){
        int fu=getfather(e[i].u),fv=getfather(e[i].v);
        if(fu==fv) continue;
        father[fu]=fv;
        if(step==ff) {ans++;ans%=MOD;}
        else dfs(step+1,i+1,t,ff);
        father[fu]=fu;
    }
}
void make_father(int s,int t,int y){
    int tot=0;
    for(int i=s;i<=t;i++){
        int fu=getfather(e[i].u),fv=getfather(e[i].v);
        if(fu==fv) continue;
        tot++;
        father[fu]=fv;
        if(tot==y) return ;
    }
}
void solve(){
    kruskal();
    if(tot<n-1) {printf("0\n");return ;}////
    int pre=1,noww;
    total=1;
    for(int i=1;i<=n;i++) father[i]=i;
    for(int i=1;i<=n;i++) h[i]=0;///
    //for(int i=1;i<=rtot;i++) printf("%d %d\n",record[i][0],record[i][1]);
    for(int i=1;i<=rtot;i++){
        ans=0;
        while(e[pre].w!=record[i][0]) pre++;
        noww=pre;
        while(e[noww].w==record[i][0]) noww++;
        noww--;
        //for(int j=1;j<=n;j++) printf("%d ",father[j]);printf("\n");
        dfs(1,pre,noww,record[i][1]);
        //for(int j=1;j<=n;j++) printf("%d ",father[j]);printf("\n");
        make_father(pre,noww,record[i][1]);
        total=(total*ans)%MOD;
        pre=noww+1;
    }
    printf("%d\n",total);
}
int main(){
    freopen("tree.in", "r", stdin);
    freopen("tree.out", "w", stdout);
    Init();
    solve();
    fclose(stdin);
    fclose(stdout);
    return 0;

}

集训结束了。我似乎有些收获,但似乎又没有收获。

猜你喜欢

转载自blog.csdn.net/wangzhuojia/article/details/79326414