7.11训练日志

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

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

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

T1菜肴制作

题目描述

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

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

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

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

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

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

例一里,首先考虑 111,因为有限制 ⟨3,1⟩\langle 3,1 \rangle3,1⟩ 和 ⟨4,1⟩\langle 4,1 \rangle4,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 \rangle5,2⟩ 的限制,所以接下来先制作 555 再制作 222;接下来考虑 333 时有 ⟨4,3⟩\langle 4,3 \rangle4,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!\n");continue;}
57         for(int i=n;i>=1;i--)printf("%d ",sta[i]);
58         printf("\n");
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\n");
60         else printf("No\n");
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\n",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\n",xx,yy):printf("%d %d\n",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\n",xx,yy):printf("%d %d\n",x,y);
107         return ;
108     }
109     (x<y)?printf("%d %d\n",xx,yy):printf("%d %d\n",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\n",ans);
    return 0;
}
错误代码,请勿粘贴

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

 

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

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

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

T1菜肴制作

题目描述

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

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

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

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

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

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

例一里,首先考虑 111,因为有限制 ⟨3,1⟩\langle 3,1 \rangle3,1⟩ 和 ⟨4,1⟩\langle 4,1 \rangle4,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 \rangle5,2⟩ 的限制,所以接下来先制作 555 再制作 222;接下来考虑 333 时有 ⟨4,3⟩\langle 4,3 \rangle4,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!\n");continue;}
57         for(int i=n;i>=1;i--)printf("%d ",sta[i]);
58         printf("\n");
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\n");
60         else printf("No\n");
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\n",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\n",xx,yy):printf("%d %d\n",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\n",xx,yy):printf("%d %d\n",x,y);
107         return ;
108     }
109     (x<y)?printf("%d %d\n",xx,yy):printf("%d %d\n",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\n",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\n",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\n",s[i].xer,s[i].ver,s[i].vvt,s[i].col);
36         //printf("%d\n",tot);
37     for(LL i=1;i<=tot;i++)
38     {
39         LL x=s[i].xer,y=s[i].ver;
40         //printf("%d %d\n",x,y);
41         LL fx=get(x),fy=get(y);
42         //cout<<fx<<" "<<endl;
43         if(fx!=fy)
44         {
45             //printf("have enter!\n");
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("\n");
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\n",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\n",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 \n",l,r,mid);printf("This is return value!:   %d\n",kul(mid));
81         if(kul(mid)>=need)l=mid+1;
82         else r=mid;
83     }
84     printf("%lld\n",ans);
85     return 0;
86 }
T4

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

 

posted @ 2019-07-12 08:31  hzoi_lsc  阅读(231)  评论(0编辑  收藏  举报