• 7.11训练日志


    ---恢复内容开始---

    今天是集训第一天,整个人的状态还是可以的!

    就是一天都被玄学错误缠身无法脱身;

    T1菜肴制作

    题目描述

    知名美食家小 A 被邀请至 ATM 大酒店,为其品评菜肴。

    ATM 酒店为小 A 准备了 NNN 道菜肴,酒店按照为菜肴预估的质量从高到低给予 111 到 NNN 的顺序编号,预估质量最高的菜肴编号为 111。由于菜肴之间口味搭配的问题,某些菜肴必须在另一些菜肴之前制作,具体的,一共有 MMM 条形如「iii 号菜肴『必须』先于 jjj 号菜肴制作”的限制」,我们将这样的限制简写为 ⟨i,j⟩langle i,j anglei,j⟩。

    现在,酒店希望能求出一个最优的菜肴的制作顺序,使得小 A 能尽量先吃到质量高的菜肴:也就是说,

    1. 在满足所有限制的前提下,111 号菜肴「尽量」优先制作;
    2. 在满足所有限制,111 号菜肴「尽量」优先制作的前提下,222 号菜肴「尽量」优先制作;
    3. 在满足所有限制,111 号和 222 号菜肴「尽量」优先的前提下,333 号菜肴「尽量」优先制作;
    4. 在满足所有限制,111 号和 222 号和 333 号菜肴「尽量」优先的前提下,4 号菜肴「尽量」优先制作;
    5. 以此类推。

    例一:共四道菜肴,两条限制 ⟨3,1⟩langle 3,1 angle3,1⟩、⟨4,1⟩langle 4,1 angle4,1⟩,那么制作顺序是 3,4,1,23,4,1,23,4,1,2。

    例二:共五道菜肴,两条限制 ⟨5,2⟩langle 5,2 angle5,2⟩、⟨4,3⟩langle 4,3 angle4,3⟩,那么制作顺序是 1,5,2,4,31,5,2,4,31,5,2,4,3。

    例一里,首先考虑 111,因为有限制 ⟨3,1⟩langle 3,1 angle3,1⟩ 和 ⟨4,1⟩langle 4,1 angle4,1⟩,所以只有制作完 333 和 444 后才能制作 111,而根据(3),333 号又应「尽量」比 444 号优先,所以当前可确定前三道菜的制作顺序是 3,4,13,4,13,4,1;接下来考虑 222,确定最终的制作顺序是 3,4,1,23,4,1,23,4,1,2。

    例二里,首先制作 111 是不违背限制的;接下来考虑 222 时有 ⟨5,2⟩langle 5,2 angle5,2⟩ 的限制,所以接下来先制作 555 再制作 222;接下来考虑 333 时有 ⟨4,3⟩langle 4,3 angle4,3⟩ 的限制,所以接下来先制作 444 再制作 333,从而最终的顺序是 1,5,2,4,31,5,2,4,31,5,2,4,3。

    现在你需要求出这个最优的菜肴制作顺序。无解输出“Impossible!” (不含引号,首字母大写,其余字母小写)

    这道题看见之后就是图论,(废话,这就是图论专题!),那么他让输出应该进行的顺序,我们应该怎么办呢?回想当时学过的知识,我脑中忽然浮现出拓扑排序;对,让入度为零的点入队,等等,这题还要按特殊的顺序,所以应该维护一个堆,当时我维护了一个小顶堆,但是后续的调试告诉我行不通(如果使用小顶堆,第三个样例就过不去,不信可以自己试试,神犇操作请自动忽略!)所以使用小根堆维护拓扑起点,反向建边,在处理几个玄学错误,就A了。

     
     1 #include<iostream>
     2 #include<cstring>
     3 #include<cmath>
     4 #include<cstring>
     5 #include<queue>
     6 #include<cstdio>
     7 using namespace std;
     8 #define LL long long
     9 #define re register
    10 const int maxn=100500;
    11 int head[maxn],nxt[maxn],ver[maxn],tot;
    12 inline void add(int q,int w){ver[++tot]=w;nxt[tot]=head[q];head[q]=tot;}
    13 int T,n,m;
    14 bool flag=0;
    15 int d[maxn];
    16 int sta[maxn],cnt=0;
    17 priority_queue<int>q;
    18 void turpop()
    19 {
    20     for(int i=n;i>=1;i--)
    21         if(d[i]==0)
    22             q.push(i);
    23     while(q.size())
    24     {
    25         int u=q.top();q.pop();
    26         sta[++cnt]=u;
    27         for(int i=head[u];i;i=nxt[i])
    28         {
    29             int y=ver[i];
    30             d[y]--;
    31             if(d[y]==0)q.push(y);
    32         }
    33     }
    34 }
    35 int main()
    36 {
    37     //freopen("cnm.txt","r",stdin);
    38     scanf("%d",&T);
    39     while(T--)
    40     {
    41         flag=0;
    42         memset(head,0,sizeof(head));
    43         memset(ver,0,sizeof(ver));
    44         memset(nxt,0,sizeof(nxt));
    45         memset(d,0,sizeof(d));
    46         tot=0;
    47         cnt=0;
    48         scanf("%d%d",&n,&m);
    49         for(re int i=1;i<=m;i++)
    50         {
    51             int x,y;
    52             scanf("%d %d",&x,&y);d[x]++;
    53             add(y,x);
    54         }
    55         turpop();
    56         if(cnt<n){printf("Impossible!
    ");continue;}
    57         for(int i=n;i>=1;i--)printf("%d ",sta[i]);
    58         printf("
    ");
    59     }
    60 }
    T1

    T2

    B. 矩阵游戏

    题目描述

     小Q是一个非常聪明的孩子,除了国际象棋,他还很喜欢玩一个电脑益智游戏——矩阵游戏。矩阵游戏在一个N*N黑白方阵进行(如同国际象棋一般,只是颜色是随意的)。每次可以对该矩阵进行两种操作:
    行交换操作:选择矩阵的任意两行,交换这两行(即交换对应格子的颜色)
    列交换操作:选择矩阵的任意行列,交换这两列(即交换对应格子的颜色)
    游戏的目标,即通过若干次操作,使得方阵的主对角线(左上角到右下角的连线)上的格子均为黑色。对于某些关卡,小Q百思不得其解,以致他开始怀疑这些关卡是不是根本就是无解的!!于是小Q决定写一个程序来判断这些关卡是否有解。

    这题可以说是比较水的了,坐在我旁边的人应该都知道我两分钟就得出使用二分图匹配(低调),然后5分钟就码完了,整个那叫一个顺,但是,之后就是玄学~~~~~~;

    先是玄学MLE,然后玄学RE,然后TLE,整个就是一个惨,然后错过了AC的最佳时机,被外校的大佬抢先AC,粘码如下

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cmath>
     4 #include<cstring>
     5 using namespace std;
     6 const int maxn=500000;
     7 int T,n;
     8 int head[maxn*2],nxt[maxn*2],ver[maxn*2],tot;
     9 int v[maxn];
    10 int match[maxn];
    11 bool flag;
    12 void add(int x,int y){ver[++tot]=y;nxt[tot]=head[x];head[x]=tot;}
    13 bool dfs(int x,int fa)
    14 {
    15     for(int i=head[x];i;i=nxt[i])
    16     {
    17         int y=ver[i];
    18         if(!v[y])
    19         {
    20             v[y]=fa;
    21             if(!match[y]||dfs(match[y],fa))
    22             {
    23                 match[y]=x;
    24                 return 1;
    25             }
    26         }
    27     }
    28     return 0;
    29 }
    30 int main()
    31 {
    32     //freopen("cnm.txt","r",stdin);
    33     scanf("%d",&T);
    34     while(T--)
    35     {
    36         flag=1;
    37         tot=0;
    38         for(int i=1;i<=n;i++)
    39             head[i]=ver[i]=nxt[i]=match[i]=v[i]=0;
    40         /*memset(head,0,sizeof(head));
    41         memset(ver,0,sizeof(ver));
    42         memset(nxt,0,sizeof(nxt));
    43         memset(v,0,sizeof(v));
    44         memset(match,0,sizeof(match));*/
    45         scanf("%d",&n);
    46         for(int i=1;i<=n;i++)
    47             for(int j=1;j<=n;j++)
    48             {
    49                 int xx=0;
    50                 scanf("%d",&xx);
    51                 if(xx)add(i,j);
    52             }
    53         flag=1;
    54         for(int i=1;i<=n&&flag;i++)
    55         {
    56             memset(v,0,sizeof(v));
    57             if(!dfs(i,i))flag=0;
    58         }
    59         if(flag==1)printf("Yes
    ");
    60         else printf("No
    ");
    61         flag=1;
    62     }
    63 }
    T2水码

     T3

    C. 约会 Rendezvous

    内存限制:128 MiB 时间限制:1000 ms 标准输入输出
    题目类型:传统 评测方式:文本比较
     

    题目描述

    给定一个有 nnn 个顶点的有向图,每个顶点有且仅有一条出边。每次询问给出两个顶点 aia_iai​​ 和 bib_ibi​​,求满足以下条件的 xix_ixi​​ 和 yiy_iyi​​:

    • 从顶点 aia_iai​​ 沿出边走 xix_ixi​​ 步与从顶点 bib_ibi​​ 沿出边走 yiy_iyi​​ 步到达的顶点相同。
    • max(xi,yi)max(x_i, y_i)max(xi​​,yi​​) 最小。
    • 满足以上条件的情况下 min(xi,yi)min(x_i, y_i)min(xi​​,yi​​) 最小。
    • 如果以上条件没有给出一个唯一的解,则还需要满足 xi≥yix_i ge y_ixi​​yi​​.

    如果不存在这样的 xix_ixi​​ 和 yiy_iyi​​,则 xi=yi=−1x_i = y_i = -1xi​​=yi​​=1.

    输入格式

    第一行两个正整数 nnn 和 kkk(1≤n≤500 000,1≤k≤500 0001 le n le 500 000,1 le k le 500 0001n500 000,1k500 000),表示顶点数和询问个数。

    接下来一行 nnn 个正整数,第 iii 个数表示 iii 号顶点出边指向的顶点。

    接下来 kkk 行表示询问,每行两个整数 aia_iai​​ 和 bib_ibi​​.

    输出格式

    对每组询问输出两个整数 xix_ixi​​ 和 yiy_iyi​​.

    这题一改改一年,有思路就能做对,但是这题卡常严重,读者自行体会!

      1 #include<cstdio>
      2 #include<cmath>
      3 using namespace std;
      4 const int L=1<<20|1;
      5 char buffer[L],*S,*T;
      6 #define getchar() ((S==T&&(T=(S=buffer)+fread(buffer,1,L,stdin),S==T))?EOF:*S++)
      7 const int maxn=5e6+999,K=20;
      8 int head[maxn],Next[maxn],to[maxn];
      9 int n,m,v[maxn],t,now,u,o,tr[maxn],d[maxn],f[maxn][21],par[maxn],q[maxn],len[maxn],xf[maxn];
     10 inline void add(int x,int y){to[++t]=y;Next[t]=head[x],head[x]=t;}
     11 inline int read()
     12 {
     13     int ss=0;char bb=getchar();
     14     while(bb<'0' || bb>'9')bb=getchar();
     15     while(bb>='0' && bb<='9')ss=(ss<<1)+(ss<<3)+(bb^48),bb=getchar();
     16     return ss;
     17 }
     18 inline void swap(int &x,int &y){int z=x;x=y,y=z;}
     19 inline int max(int x,int y){return x>y?x:y;}
     20 inline int min(int x,int y){return x<y?x:y;}
     21 void bfs(int x)
     22 {
     23     q[u=1]=x,o=0;
     24     while(o<u)
     25     {
     26         x=q[++o];
     27         for(register int i=head[x];i;i=Next[i])
     28         {
     29             int y=to[i];
     30             if(d[y])continue;
     31             d[y]=d[x]+1,tr[y]=now;
     32             f[y][0]=x,par[y]=q[1];
     33             for(register int j=1;j<=K;++j)
     34                 f[y][j]=f[f[y][j-1]][j-1];
     35             q[++u]=y;
     36         }
     37     }
     38     return ;
     39 }
     40 void find(int x)
     41 {
     42     int y=x;
     43     tr[x]=now;
     44     while(!tr[v[y]])y=v[y],tr[y]=now;
     45     d[y]=len[now]=xf[y]=1,par[y]=y;
     46     for(register int i=v[y];i!=y;i=v[i])
     47     {
     48         d[i]=1,xf[i]=++len[now];
     49         par[i]=i,bfs(i);
     50     }
     51     bfs(y);
     52     return ;
     53 }
     54 int lca(int x,int y)
     55 {
     56     if(d[x]>d[y])swap(x,y);
     57     for(register int i=K;i>=0;--i)
     58         if(d[f[y][i]]>=d[x])y=f[y][i];
     59     if(x==y)return x;
     60     for(register int i=K;i>=0;--i)
     61         if(f[x][i]^f[y][i])
     62             x=f[x][i],y=f[y][i];
     63     return f[x][0];
     64 }
     65 inline void print(int x,int y,int xx,int yy);
     66 int main()
     67 {
     68     n=read(),m=read();
     69     for(register int i=1;i<=n;++i)
     70     {
     71         int tt=read();
     72         v[i]=tt,add(tt,i);
     73     }
     74     for(register int i=1;i<=n;++i)
     75         if(!tr[i])++now,find(i);
     76     while(m--)
     77     {
     78         int ff=read(),tt=read();
     79         if(tr[ff]^tr[tt]){puts("-1 -1");continue;}
     80         if(ff==tt){puts("0 0");continue;}
     81         if(par[ff]^par[tt])
     82         {
     83             int fi=d[ff]-1,ti=d[tt]-1,ans;
     84             ff=par[ff],tt=par[tt];
     85             ans=abs(xf[tt]-xf[ff]);
     86             if(xf[ff]>xf[tt])ans=len[tr[tt]]-ans;
     87             print(fi+ans,ti,fi,ti+len[tr[tt]]-ans);
     88             continue;
     89         }
     90         int fi=d[lca(ff,tt)];
     91         printf("%d %d
    ",d[ff]-fi,d[tt]-fi);
     92     }
     93     return 0;
     94 }
     95 inline void print(int x,int y,int xx,int yy)
     96 {
     97     int mf=max(x,y),ms=max(xx,yy);
     98     if(mf^ms)
     99     {
    100         (mf>ms)?printf("%d %d
    ",xx,yy):printf("%d %d
    ",x,y);
    101         return ;
    102     }
    103     mf=min(x,y),ms=min(xx,yy);
    104     if(mf^ms)
    105     {
    106         (mf>ms)?printf("%d %d
    ",xx,yy):printf("%d %d
    ",x,y);
    107         return ;
    108     }
    109     (x<y)?printf("%d %d
    ",xx,yy):printf("%d %d
    ",x,y);
    110 }
    T3

    重点是T4

    前方高能!

    D. tree

    题目描述

    给你一个无向带权连通图,每条边是黑色或白色。让你求一棵最小权的恰好有need条白色边的生成树。题目保证有解。

    输入格式

    第一行V,E,need分别表示点数,边数和需要的白色边数。
    接下来E行,每行s,t,c,col表示这边的端点(点从0开始标号),边权,颜色(0白色1黑色)。

    输出格式

    一行表示所求生成树的边权和。
    V<=50000,E<=100000,所有数据边权为[1,100]中的正整数。

    开始一看题,这不是最小生成树的水题吗?然后打了一个自己看的过样例的程序,错误代码粘贴如下,复制后果自负;

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int maxn=10000;
    struct ed{
    int head,nxt,ver,vvt,col,xer;
    }bla[maxn],whi[maxn];
    int n,m,need;
    int totw,totb;
    inline void addwhi(int x,int y,int c,int col)
    {whi[++totw].ver=y;whi[totw].xer=x;whi[totw].nxt=whi[x].head;whi[x].head=totw;whi[totw].vvt=c;whi[totw].col=col;}
    inline void addbla(int x,int y,int c,int col)
    {bla[++totb].ver=y;bla[totb].xer=x;bla[totb].nxt=bla[x].head;bla[x].head=totb;bla[totb].vvt=c;bla[totb].col=col;}
    bool v[maxn];
    int ans=0;
    bool cmp(ed a,ed b){return a.vvt<b.vvt;}
    int main()
    {    
        freopen("cnm.txt","r",stdin);
        scanf("%d%d%d",&n,&m,&need);
        for(int i=1;i<=m;i++)
        {
            int x,y,c,col;
            scanf("%d%d%d%d",&x,&y,&c,&col);
            add(x,y,c,col),add(y,x,c,col);
            if(col==0)addwhi(x,y,c,col),addwhi(y,x,c,col);
            else addbla(x,y,c,col),addbla(y,x,c,col);
        }
        sort(bla+1,bla+totb+1,cmp);
        sort(whi+1,whi+totw+1,cmp);
        int cnt=0;
        for(int i=1;i<=totw;i++)
        {
            int x=whi[i].xer,y=whi[i].ver;
            //cout<<"white edge :  "<<"x:   "<<x<<"   y:   "<<y<<"vvt    :"<<whi[i].vvt<<endl;
            if(v[x]||v[y])continue;
            if(cnt==need)break;
            v[x]=v[y]=1;
            //cout<<"tiaoshi   "<<whi[i].vvt<<endl;
            ans+=whi[i].vvt;
            //cout<<"ans   "<<ans<<endl;
            cnt++;
        }
        for(int i=1;i<=totb;i++)
        {
            if(cnt==n-1)break;
            int x=bla[i].xer,y=bla[i].ver;
            if(v[x]&&v[y])continue;
                cnt++;
                v[x]=v[y]=1;
                ans+=bla[i].vvt;
                cnt++;
        }
        printf("%d
    ",ans);
        return 0;
    }
    错误代码,请勿粘贴

    后来发现这棵生成树使用贪心会出现WA的现象,(自己手膜样例出来的错误,但是不会证明,如果有会证明的大佬可以联系本juruo)所有数据都是边权为[1,100]的正整数,不觉得这很诡异吗?,所以我们立即就想二分,但是传统的二分都是二分答案,这题由于每条便的边权很小,我们可以枚举边权

    ---恢复内容结束---

    今天是集训第一天,整个人的状态还是可以的!

    就是一天都被玄学错误缠身无法脱身;

    T1菜肴制作

    题目描述

    知名美食家小 A 被邀请至 ATM 大酒店,为其品评菜肴。

    ATM 酒店为小 A 准备了 NNN 道菜肴,酒店按照为菜肴预估的质量从高到低给予 111 到 NNN 的顺序编号,预估质量最高的菜肴编号为 111。由于菜肴之间口味搭配的问题,某些菜肴必须在另一些菜肴之前制作,具体的,一共有 MMM 条形如「iii 号菜肴『必须』先于 jjj 号菜肴制作”的限制」,我们将这样的限制简写为 ⟨i,j⟩langle i,j anglei,j⟩。

    现在,酒店希望能求出一个最优的菜肴的制作顺序,使得小 A 能尽量先吃到质量高的菜肴:也就是说,

    1. 在满足所有限制的前提下,111 号菜肴「尽量」优先制作;
    2. 在满足所有限制,111 号菜肴「尽量」优先制作的前提下,222 号菜肴「尽量」优先制作;
    3. 在满足所有限制,111 号和 222 号菜肴「尽量」优先的前提下,333 号菜肴「尽量」优先制作;
    4. 在满足所有限制,111 号和 222 号和 333 号菜肴「尽量」优先的前提下,4 号菜肴「尽量」优先制作;
    5. 以此类推。

    例一:共四道菜肴,两条限制 ⟨3,1⟩langle 3,1 angle3,1⟩、⟨4,1⟩langle 4,1 angle4,1⟩,那么制作顺序是 3,4,1,23,4,1,23,4,1,2。

    例二:共五道菜肴,两条限制 ⟨5,2⟩langle 5,2 angle5,2⟩、⟨4,3⟩langle 4,3 angle4,3⟩,那么制作顺序是 1,5,2,4,31,5,2,4,31,5,2,4,3。

    例一里,首先考虑 111,因为有限制 ⟨3,1⟩langle 3,1 angle3,1⟩ 和 ⟨4,1⟩langle 4,1 angle4,1⟩,所以只有制作完 333 和 444 后才能制作 111,而根据(3),333 号又应「尽量」比 444 号优先,所以当前可确定前三道菜的制作顺序是 3,4,13,4,13,4,1;接下来考虑 222,确定最终的制作顺序是 3,4,1,23,4,1,23,4,1,2。

    例二里,首先制作 111 是不违背限制的;接下来考虑 222 时有 ⟨5,2⟩langle 5,2 angle5,2⟩ 的限制,所以接下来先制作 555 再制作 222;接下来考虑 333 时有 ⟨4,3⟩langle 4,3 angle4,3⟩ 的限制,所以接下来先制作 444 再制作 333,从而最终的顺序是 1,5,2,4,31,5,2,4,31,5,2,4,3。

    现在你需要求出这个最优的菜肴制作顺序。无解输出“Impossible!” (不含引号,首字母大写,其余字母小写)

    这道题看见之后就是图论,(废话,这就是图论专题!),那么他让输出应该进行的顺序,我们应该怎么办呢?回想当时学过的知识,我脑中忽然浮现出拓扑排序;对,让入度为零的点入队,等等,这题还要按特殊的顺序,所以应该维护一个堆,当时我维护了一个小顶堆,但是后续的调试告诉我行不通(如果使用小顶堆,第三个样例就过不去,不信可以自己试试,神犇操作请自动忽略!)所以使用小根堆维护拓扑起点,反向建边,在处理几个玄学错误,就A了。

     
     1 #include<iostream>
     2 #include<cstring>
     3 #include<cmath>
     4 #include<cstring>
     5 #include<queue>
     6 #include<cstdio>
     7 using namespace std;
     8 #define LL long long
     9 #define re register
    10 const int maxn=100500;
    11 int head[maxn],nxt[maxn],ver[maxn],tot;
    12 inline void add(int q,int w){ver[++tot]=w;nxt[tot]=head[q];head[q]=tot;}
    13 int T,n,m;
    14 bool flag=0;
    15 int d[maxn];
    16 int sta[maxn],cnt=0;
    17 priority_queue<int>q;
    18 void turpop()
    19 {
    20     for(int i=n;i>=1;i--)
    21         if(d[i]==0)
    22             q.push(i);
    23     while(q.size())
    24     {
    25         int u=q.top();q.pop();
    26         sta[++cnt]=u;
    27         for(int i=head[u];i;i=nxt[i])
    28         {
    29             int y=ver[i];
    30             d[y]--;
    31             if(d[y]==0)q.push(y);
    32         }
    33     }
    34 }
    35 int main()
    36 {
    37     //freopen("cnm.txt","r",stdin);
    38     scanf("%d",&T);
    39     while(T--)
    40     {
    41         flag=0;
    42         memset(head,0,sizeof(head));
    43         memset(ver,0,sizeof(ver));
    44         memset(nxt,0,sizeof(nxt));
    45         memset(d,0,sizeof(d));
    46         tot=0;
    47         cnt=0;
    48         scanf("%d%d",&n,&m);
    49         for(re int i=1;i<=m;i++)
    50         {
    51             int x,y;
    52             scanf("%d %d",&x,&y);d[x]++;
    53             add(y,x);
    54         }
    55         turpop();
    56         if(cnt<n){printf("Impossible!
    ");continue;}
    57         for(int i=n;i>=1;i--)printf("%d ",sta[i]);
    58         printf("
    ");
    59     }
    60 }
    T1

    T2

    B. 矩阵游戏

    题目描述

     小Q是一个非常聪明的孩子,除了国际象棋,他还很喜欢玩一个电脑益智游戏——矩阵游戏。矩阵游戏在一个N*N黑白方阵进行(如同国际象棋一般,只是颜色是随意的)。每次可以对该矩阵进行两种操作:
    行交换操作:选择矩阵的任意两行,交换这两行(即交换对应格子的颜色)
    列交换操作:选择矩阵的任意行列,交换这两列(即交换对应格子的颜色)
    游戏的目标,即通过若干次操作,使得方阵的主对角线(左上角到右下角的连线)上的格子均为黑色。对于某些关卡,小Q百思不得其解,以致他开始怀疑这些关卡是不是根本就是无解的!!于是小Q决定写一个程序来判断这些关卡是否有解。

    这题可以说是比较水的了,坐在我旁边的人应该都知道我两分钟就得出使用二分图匹配(低调),然后5分钟就码完了,整个那叫一个顺,但是,之后就是玄学~~~~~~;

    先是玄学MLE,然后玄学RE,然后TLE,整个就是一个惨,然后错过了AC的最佳时机,被外校的大佬抢先AC,粘码如下

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cmath>
     4 #include<cstring>
     5 using namespace std;
     6 const int maxn=500000;
     7 int T,n;
     8 int head[maxn*2],nxt[maxn*2],ver[maxn*2],tot;
     9 int v[maxn];
    10 int match[maxn];
    11 bool flag;
    12 void add(int x,int y){ver[++tot]=y;nxt[tot]=head[x];head[x]=tot;}
    13 bool dfs(int x,int fa)
    14 {
    15     for(int i=head[x];i;i=nxt[i])
    16     {
    17         int y=ver[i];
    18         if(!v[y])
    19         {
    20             v[y]=fa;
    21             if(!match[y]||dfs(match[y],fa))
    22             {
    23                 match[y]=x;
    24                 return 1;
    25             }
    26         }
    27     }
    28     return 0;
    29 }
    30 int main()
    31 {
    32     //freopen("cnm.txt","r",stdin);
    33     scanf("%d",&T);
    34     while(T--)
    35     {
    36         flag=1;
    37         tot=0;
    38         for(int i=1;i<=n;i++)
    39             head[i]=ver[i]=nxt[i]=match[i]=v[i]=0;
    40         /*memset(head,0,sizeof(head));
    41         memset(ver,0,sizeof(ver));
    42         memset(nxt,0,sizeof(nxt));
    43         memset(v,0,sizeof(v));
    44         memset(match,0,sizeof(match));*/
    45         scanf("%d",&n);
    46         for(int i=1;i<=n;i++)
    47             for(int j=1;j<=n;j++)
    48             {
    49                 int xx=0;
    50                 scanf("%d",&xx);
    51                 if(xx)add(i,j);
    52             }
    53         flag=1;
    54         for(int i=1;i<=n&&flag;i++)
    55         {
    56             memset(v,0,sizeof(v));
    57             if(!dfs(i,i))flag=0;
    58         }
    59         if(flag==1)printf("Yes
    ");
    60         else printf("No
    ");
    61         flag=1;
    62     }
    63 }
    T2水码

     T3

    C. 约会 Rendezvous

     

    题目描述

    给定一个有 nnn 个顶点的有向图,每个顶点有且仅有一条出边。每次询问给出两个顶点 aia_iai​​ 和 bib_ibi​​,求满足以下条件的 xix_ixi​​ 和 yiy_iyi​​:

    • 从顶点 aia_iai​​ 沿出边走 xix_ixi​​ 步与从顶点 bib_ibi​​ 沿出边走 yiy_iyi​​ 步到达的顶点相同。
    • max(xi,yi)max(x_i, y_i)max(xi​​,yi​​) 最小。
    • 满足以上条件的情况下 min(xi,yi)min(x_i, y_i)min(xi​​,yi​​) 最小。
    • 如果以上条件没有给出一个唯一的解,则还需要满足 xi≥yix_i ge y_ixi​​yi​​.

    如果不存在这样的 xix_ixi​​ 和 yiy_iyi​​,则 xi=yi=−1x_i = y_i = -1xi​​=yi​​=1.

    输入格式

    第一行两个正整数 nnn 和 kkk(1≤n≤500 000,1≤k≤500 0001 le n le 500 000,1 le k le 500 0001n500 000,1k500 000),表示顶点数和询问个数。

    接下来一行 nnn 个正整数,第 iii 个数表示 iii 号顶点出边指向的顶点。

    接下来 kkk 行表示询问,每行两个整数 aia_iai​​ 和 bib_ibi​​.

    输出格式

    对每组询问输出两个整数 xix_ixi​​ 和 yiy_iyi​​.

    这题一改改一年,有思路就能做对,但是这题卡常严重,读者自行体会!

      1 #include<cstdio>
      2 #include<cmath>
      3 using namespace std;
      4 const int L=1<<20|1;
      5 char buffer[L],*S,*T;
      6 #define getchar() ((S==T&&(T=(S=buffer)+fread(buffer,1,L,stdin),S==T))?EOF:*S++)
      7 const int maxn=5e6+999,K=20;
      8 int head[maxn],Next[maxn],to[maxn];
      9 int n,m,v[maxn],t,now,u,o,tr[maxn],d[maxn],f[maxn][21],par[maxn],q[maxn],len[maxn],xf[maxn];
     10 inline void add(int x,int y){to[++t]=y;Next[t]=head[x],head[x]=t;}
     11 inline int read()
     12 {
     13     int ss=0;char bb=getchar();
     14     while(bb<'0' || bb>'9')bb=getchar();
     15     while(bb>='0' && bb<='9')ss=(ss<<1)+(ss<<3)+(bb^48),bb=getchar();
     16     return ss;
     17 }
     18 inline void swap(int &x,int &y){int z=x;x=y,y=z;}
     19 inline int max(int x,int y){return x>y?x:y;}
     20 inline int min(int x,int y){return x<y?x:y;}
     21 void bfs(int x)
     22 {
     23     q[u=1]=x,o=0;
     24     while(o<u)
     25     {
     26         x=q[++o];
     27         for(register int i=head[x];i;i=Next[i])
     28         {
     29             int y=to[i];
     30             if(d[y])continue;
     31             d[y]=d[x]+1,tr[y]=now;
     32             f[y][0]=x,par[y]=q[1];
     33             for(register int j=1;j<=K;++j)
     34                 f[y][j]=f[f[y][j-1]][j-1];
     35             q[++u]=y;
     36         }
     37     }
     38     return ;
     39 }
     40 void find(int x)
     41 {
     42     int y=x;
     43     tr[x]=now;
     44     while(!tr[v[y]])y=v[y],tr[y]=now;
     45     d[y]=len[now]=xf[y]=1,par[y]=y;
     46     for(register int i=v[y];i!=y;i=v[i])
     47     {
     48         d[i]=1,xf[i]=++len[now];
     49         par[i]=i,bfs(i);
     50     }
     51     bfs(y);
     52     return ;
     53 }
     54 int lca(int x,int y)
     55 {
     56     if(d[x]>d[y])swap(x,y);
     57     for(register int i=K;i>=0;--i)
     58         if(d[f[y][i]]>=d[x])y=f[y][i];
     59     if(x==y)return x;
     60     for(register int i=K;i>=0;--i)
     61         if(f[x][i]^f[y][i])
     62             x=f[x][i],y=f[y][i];
     63     return f[x][0];
     64 }
     65 inline void print(int x,int y,int xx,int yy);
     66 int main()
     67 {
     68     n=read(),m=read();
     69     for(register int i=1;i<=n;++i)
     70     {
     71         int tt=read();
     72         v[i]=tt,add(tt,i);
     73     }
     74     for(register int i=1;i<=n;++i)
     75         if(!tr[i])++now,find(i);
     76     while(m--)
     77     {
     78         int ff=read(),tt=read();
     79         if(tr[ff]^tr[tt]){puts("-1 -1");continue;}
     80         if(ff==tt){puts("0 0");continue;}
     81         if(par[ff]^par[tt])
     82         {
     83             int fi=d[ff]-1,ti=d[tt]-1,ans;
     84             ff=par[ff],tt=par[tt];
     85             ans=abs(xf[tt]-xf[ff]);
     86             if(xf[ff]>xf[tt])ans=len[tr[tt]]-ans;
     87             print(fi+ans,ti,fi,ti+len[tr[tt]]-ans);
     88             continue;
     89         }
     90         int fi=d[lca(ff,tt)];
     91         printf("%d %d
    ",d[ff]-fi,d[tt]-fi);
     92     }
     93     return 0;
     94 }
     95 inline void print(int x,int y,int xx,int yy)
     96 {
     97     int mf=max(x,y),ms=max(xx,yy);
     98     if(mf^ms)
     99     {
    100         (mf>ms)?printf("%d %d
    ",xx,yy):printf("%d %d
    ",x,y);
    101         return ;
    102     }
    103     mf=min(x,y),ms=min(xx,yy);
    104     if(mf^ms)
    105     {
    106         (mf>ms)?printf("%d %d
    ",xx,yy):printf("%d %d
    ",x,y);
    107         return ;
    108     }
    109     (x<y)?printf("%d %d
    ",xx,yy):printf("%d %d
    ",x,y);
    110 }
    T3

    重点是T4

    前方高能!

    D. tree

    题目描述

    给你一个无向带权连通图,每条边是黑色或白色。让你求一棵最小权的恰好有need条白色边的生成树。题目保证有解。

    输入格式

    第一行V,E,need分别表示点数,边数和需要的白色边数。
    接下来E行,每行s,t,c,col表示这边的端点(点从0开始标号),边权,颜色(0白色1黑色)。

    输出格式

    一行表示所求生成树的边权和。
    V<=50000,E<=100000,所有数据边权为[1,100]中的正整数。

    开始一看题,这不是最小生成树的水题吗?然后打了一个自己看的过样例的程序,错误代码粘贴如下,复制后果自负;

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int maxn=10000;
    struct ed{
    int head,nxt,ver,vvt,col,xer;
    }bla[maxn],whi[maxn];
    int n,m,need;
    int totw,totb;
    inline void addwhi(int x,int y,int c,int col)
    {whi[++totw].ver=y;whi[totw].xer=x;whi[totw].nxt=whi[x].head;whi[x].head=totw;whi[totw].vvt=c;whi[totw].col=col;}
    inline void addbla(int x,int y,int c,int col)
    {bla[++totb].ver=y;bla[totb].xer=x;bla[totb].nxt=bla[x].head;bla[x].head=totb;bla[totb].vvt=c;bla[totb].col=col;}
    bool v[maxn];
    int ans=0;
    bool cmp(ed a,ed b){return a.vvt<b.vvt;}
    int main()
    {    
        freopen("cnm.txt","r",stdin);
        scanf("%d%d%d",&n,&m,&need);
        for(int i=1;i<=m;i++)
        {
            int x,y,c,col;
            scanf("%d%d%d%d",&x,&y,&c,&col);
            add(x,y,c,col),add(y,x,c,col);
            if(col==0)addwhi(x,y,c,col),addwhi(y,x,c,col);
            else addbla(x,y,c,col),addbla(y,x,c,col);
        }
        sort(bla+1,bla+totb+1,cmp);
        sort(whi+1,whi+totw+1,cmp);
        int cnt=0;
        for(int i=1;i<=totw;i++)
        {
            int x=whi[i].xer,y=whi[i].ver;
            //cout<<"white edge :  "<<"x:   "<<x<<"   y:   "<<y<<"vvt    :"<<whi[i].vvt<<endl;
            if(v[x]||v[y])continue;
            if(cnt==need)break;
            v[x]=v[y]=1;
            //cout<<"tiaoshi   "<<whi[i].vvt<<endl;
            ans+=whi[i].vvt;
            //cout<<"ans   "<<ans<<endl;
            cnt++;
        }
        for(int i=1;i<=totb;i++)
        {
            if(cnt==n-1)break;
            int x=bla[i].xer,y=bla[i].ver;
            if(v[x]&&v[y])continue;
                cnt++;
                v[x]=v[y]=1;
                ans+=bla[i].vvt;
                cnt++;
        }
        printf("%d
    ",ans);
        return 0;
    }
    错误代码,请勿粘贴

    后来发现这棵生成树使用贪心会出现WA的现象,(自己手膜样例出来的错误,但是不会证明,如果有会证明的大佬可以联系本juruo)所有数据都是边权为[1,100]的正整数,不觉得这很诡异吗?,所以我们立即就想二分,但是传统的二分都是二分答案,这题由于每条便的边权很小,我们可以枚举边权然后二分求解,就是每个加上一个mid边权,是它恰好维持一个生成树拥有need条白边,这棵生成树会因为mid值的变化白边数发生改变,所以二分mid,但是mid不是ans;据说这是wqs二分,wqs大佬证明了这些;

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<cmath>
     5 #include<algorithm>
     6 using namespace std;
     7 const int maxn=1000000;
     8 #define LL long long
     9 //mind road:divide it two parts;
    10 //我想到二分和克鲁斯,但是没想到加权打法!
    11 struct ed{
    12 LL head,nxt,ver,xer,vvt,col;
    13 }s[maxn];
    14 LL n,m,need;
    15 LL fa[maxn],ans,tot;
    16 void add(LL x,LL y,LL c,LL col){s[++tot].ver=y;s[tot].nxt=s[tot].head;s[tot].head=tot;s[tot].xer=x;s[tot].vvt=c;s[tot].col=col;}
    17 bool cmp(ed a,ed b){if(a.vvt==b.vvt)return a.col<b.col;else return a.vvt<b.vvt;}
    18 LL get(LL x)
    19 {
    20     if(fa[x]==x)return x;
    21     else fa[x]=get(fa[x]);
    22     return fa[x];
    23 }
    24 LL kul(LL mid)
    25 {
    26     //printf("This is m  : %d
    ",m);
    27     LL cnt=0,k=0,sum=0;
    28     for(LL i=1;i<=n;i++)
    29         fa[i]=i;
    30     for(LL i=1;i<=tot;i++)
    31         if(s[i].col==0)
    32         s[i].vvt+=mid;
    33     sort(s+1,s+tot+1,cmp);
    34     //for(LL i=1;i<=m;i++)
    35         //printf("This is the sort:  %d  %d   %d   %d
    ",s[i].xer,s[i].ver,s[i].vvt,s[i].col);
    36         //printf("%d
    ",tot);
    37     for(LL i=1;i<=tot;i++)
    38     {
    39         LL x=s[i].xer,y=s[i].ver;
    40         //printf("%d %d
    ",x,y);
    41         LL fx=get(x),fy=get(y);
    42         //cout<<fx<<" "<<endl;
    43         if(fx!=fy)
    44         {
    45             //printf("have enter!
    ");
    46             fa[fx]=fy;
    47             k++;
    48             sum+=s[i].vvt;
    49             if(s[i].col==0)cnt++;
    50             if(k==n-1)break;
    51         }
    52         //printf("
    ");
    53     }
    54     for(LL i=1;i<=tot;i++)
    55         if(s[i].col==0)
    56             s[i].vvt-=mid;
    57     //for(LL i=1;i<=tot;i++)
    58         //printf("This is the end:  %d  %d   %d   %d
    ",s[i].xer,s[i].ver,s[i].vvt,s[i].col);
    59     if(cnt>=need)
    60         ans=sum-mid*need;
    61         //printf("THIS is ans:   %d
    ",ans);
    62     return cnt;
    63 }
    64 int main()
    65 {
    66     //freopen("cnm.txt","r",stdin);
    67     scanf("%lld%lld%lld",&n,&m,&need);
    68     for(LL i=1;i<=m;i++)
    69     {
    70         LL x,y,c,col;
    71         scanf("%lld%lld%lld%lld",&x,&y,&c,&col);
    72         x+=1,y+=1;
    73         //this x seems could be 0 ans his fa is 0 too;have a conlift;
    74         add(x,y,c,col);
    75     }
    76     LL l=(LL)-1e9,r=(LL)1e9,mid=0;
    77     while(l<r)
    78     {
    79         LL mid=(l+r)>>1;
    80         //printf("l:%d  r:%d  mid:%d 
    ",l,r,mid);printf("This is return value!:   %d
    ",kul(mid));
    81         if(kul(mid)>=need)l=mid+1;
    82         else r=mid;
    83     }
    84     printf("%lld
    ",ans);
    85     return 0;
    86 }
    T4

    本人很弱,一天就干了这些,脑壳疼!!~~~

  • 相关阅读:
    Windows8 自制DatePicker
    Windows8 商店支付功能
    通过(Node Js||.Net)基于HTML5的WebSocket实现实时视频文字传输(上)
    Windows8 检测网络
    Windows8 推送通知
    Windows8&Windows Phone 做一个图片效果
    js 两种时间数字字符串格式
    使用Homebrew安装Git(Mac)
    JavaScript核心参考教程客户端JavaScript
    MVC模式浅谈
  • 原文地址:https://www.cnblogs.com/hzoi-lsc/p/11172980.html
Copyright © 2020-2023  润新知