专辑:题解&&海量集训
一、水水的图
分析:
对于某两个点,如果想要把他们删除并且互不影响的话,那么他们一定不是在树上
那么根据这个性质我们就可以用树结构,广搜建立树,首先,删除的边必须在树上,才有可能对距离产生影响。而在树上的边,依题意可知,只有在其指向节点只能通过这条路到达这个深度时,这条边才是会产生影响的。因此记录使某节点在其深度的边的个数,当不止一条边可以使该节点获得这个深度,则这条树上的边也是可删除的
那么具体代码如下:
#include<bits/stdc++.h>
using namespace std;
int n,m;
int len;
int linkk[1000001];
struct node{
int y,next;
}e[1000001];
int dis[1000001];
int in[1000001];
bool vis[1000001];
struct node1{
int l,r;
}a[10000001];
void insert(int xx,int yy){
e[++len].next=linkk[xx];
linkk[xx]=len;
e[len].y=yy;
}
int main(){
freopen("train.in","r",stdin);
freopen("train.out","w",stdout);
scanf("%d %d",&n,&m);
for (int i=1;i<=m;i++){
scanf("%d %d",&a[i].l,&a[i].r);
insert(a[i].l,a[i].r);insert(a[i].r,a[i].l);
}
memset(dis,50,sizeof(dis));
memset(vis,0,sizeof(vis));
vis[1]=1;
dis[1]=0;
queue< int > q;
q.push(1);
while (!q.empty()){
int x=q.front();
q.pop();
for (int i=linkk[x];i;i=e[i].next){
if (!vis[e[i].y]){
q.push(e[i].y);
vis[e[i].y]=1;
dis[e[i].y]=dis[x]+1;
in[e[i].y]=1;
}
else
if (dis[e[i].y]==dis[x]+1) in[e[i].y]+=1;
}
}
bool f=1;
for (int i=1;i<=m;i++){
int xx=a[i].l,yy=a[i].r;
if (dis[yy]<dis[xx]) swap(xx,yy);
if (dis[yy]-dis[xx]==0||in[yy]>1) printf("%d\n",i),f=0;
}
if (f) printf("NO");
fclose(stdin);
fclose(stdout);
return 0;
}
二、重量不同的硬币
问题描述:
Fj有N个硬币,编号为1…N。
现在有W个推断,为(A,B),表示硬币A比硬币B重。
寻找并输出一个硬币编号,要求其重量明确不同于其他硬币的个数最多。
如果有多个答案,输出字典序最小的一个。
如果给出的数据有矛盾,输出"IMPOSSIBLE"
输入格式
Line 1: 两个整数: N and W.
Lines 2..W+1: 每行两个整数: A, B
输出格式
Line 1: 重量不同于其他硬币的个数最多的硬币编号。
样例数据
input
7 6
1 6
1 5
3 6
4 3
2 4
2 5
output
2
有7个硬币,6个推断对,2的重量不同于3,4,5,6
分析:对于两枚硬币如果a>b,那么必定b<a,所以硬币a和b是互不相同的。所以我们需要建一张正图表示a比b大,因为b<a,所以我们还需要建立一张反图,在正图和反图上跑两边dfs来记录比当前点小的硬币和比当前点大的硬币的个数,记录最大值即可。不过在跑BFS前我们需要用拓扑排序来判环。
那么具体代码如下:
#include<bits/stdc++.h>
using namespace std;
int len1=0;
int len2=0;
int linkk1[1000001];
int linkk2[1000001];
struct node{
int y,next;
};
node e1[1000001],e2[10000001];
int n,m;
int in[1010001];
int maxx=0,maxn;
int sum1[1000001];
int sum2[1000001];
bool vis1[1001];
bool vis2[1001];
void insert1(int xx,int yy){
e1[++len1].next=linkk1[xx];
e1[len1].y=yy;
linkk1[xx]=len1;
}
void insert2(int xx,int yy){
e2[++len2].next=linkk2[xx];
e2[len2].y=yy;
linkk2[xx]=len2;
}
int tuopusort(){
queue < int > q;
int Time=0;
for (int i=1;i<=n;i++) if (!in[i]) q.push(i);
while (!q.empty()){
Time++;
int x=q.front();q.pop();
for (int i=linkk1[x];i;i=e1[i].next){
in[e1[i].y]--;
if (!in[e1[i].y]) q.push(e1[i].y);
}
}
return Time;
}
void dfs1(int k){
sum1[k]=1;vis1[k]=1;
for (int i=linkk1[k];i;i=e1[i].next){
if (vis1[e1[i].y]) continue;
dfs1(e1[i].y);
sum1[k]+=sum1[e1[i].y];
}
}
void dfs2(int k){
sum2[k]=1;vis2[k]=1;
for (int i=linkk2[k];i;i=e2[i].next){
if (vis2[e2[i].y]) continue;
dfs2(e2[i].y);
sum2[k]+=sum2[e2[i].y];
}
}
int main(){
freopen("coin.in","r",stdin);
freopen("coin.out","w",stdout);
scanf("%d %d",&n,&m);
for (int i=1;i<=m;i++){
int x,y;
scanf("%d %d",&x,&y);
in[y]++;
insert1(x,y);
insert2(y,x);
}
int t=tuopusort();
if (t<n){
cout<<"IMPOSSIBLE";
return 0;
}
for (int i=1;i<=n;i++){
int ans=0;
memset(vis1,0,sizeof(vis1));
memset(vis2,0,sizeof(vis2));
dfs1(i);dfs2(i);
ans=sum1[i]+sum2[i];
if (maxx<ans) maxx=ans,maxn=i;
}
cout<<maxn;
fclose(stdin);
fclose(stdout);
return 0;
}
三、超级牛游戏
问题描述:
现在有N(1 <= N <= 2000)头奶牛在玩 超级牛 游戏。每头奶牛有一个唯一的ID,ID范围是 1 … 2 ^ 30-1。
超级牛比赛是淘汰赛 - 每场比赛后,输者退赛,赢者继续留在比赛,直到只剩一队游戏结束。 输赢是FJ自己决定的,或者说结果可以任意决定!
比赛的积分规则十分奇葩:积分=第一队的ID XOR 第二队的ID。 比如,12队和20队打比赛,积分是24,因为01100 XOR 10100 = 11000。
FJ认为,分越高越刺激。所以他想让总积分最高。请帮助FJ设计比赛。
输入格式
第一行包含一个整数N
以下N行包含N个队伍的ID。
输出格式
一行,一个整数,表示答案。
样例数据
input
4
3
6
9
10
output
37
输出详情:实现37的一种方法如下:
3 VS 9 ==》9胜,本场积分 3 XOR 9 = 10,目前队伍:6 9 10
6 VS 9 ==》6胜,本场积分 6 XOR 9 = 15,目前队伍:6 10
6 VS 10 ==》管他谁胜呢反正不用再比赛了,本场积分 6 XOR 10 = 12
总积分 10 + 15 + 12 = 37。
团队6和10面脱落,和队10胜。
拿下点的总数是(10)+(15)+(12)=
注:按位异或运算,由^符号通常表示,是进行逻辑异或运算的两个二进制整数的每个位置逐位操作。 规则如下 0 xor 0 = 0 0 xor 1 = 1 1 xor 0 = 1 1 xor 1 = 0
分析:因为每头牛都会进行一场比赛(显而易见的),所以假如我们把他们之间的比赛(关系)看成一张图,他们就是这张图上的节点,一共有n个节点。题目中又说两头牛经过一场比赛以后,会淘汰一头牛,也就是说这头牛不能和另外一头牛比赛,那么比赛结束后一共会进行n-1场比赛,那么这个图就是n-1场比赛将n个点连成一起,这是一棵生成树,而且题目中说值要最大,那么就是一棵最大生成树。
那么具体代码如下:
#include<bits/stdc++.h>
using namespace std;
#define LL long long
LL n;
struct node{
LL x,y,v;
}e[10000001];
LL fa[10000001];
LL ans=0;
inline bool mycmp(node x,node y){
return x.v>y.v;
}
int getfa(LL k){
return k==fa[k]?k:fa[k]=getfa(fa[k]);
}
int len;
int main(){
freopen("superbull.in","r",stdin);
freopen("superbull.out","w",stdout);
scanf("%lld",&n);
LL a[n];
for (LL i=1;i<=n;i++) fa[i]=i;
for (LL i=1;i<=n;i++) scanf("%lld",&a[i]);
for (LL i=1;i<=n;i++)
for (LL j=i+1;j<=n;j++)
e[++len]=(node){i,j,a[i]^a[j]}/*,e[++len]=(node){j,i,a[i]^a[j]}*/;
sort(e+1,e+len+1,mycmp);
for (LL i=1;i<=len;i++){
LL x=getfa(e[i].x),y=getfa(e[i].y);
if (x!=y) ans+=e[i].v,fa[x]=y;
}
printf("%lld",ans);
fclose(stdin);
fclose(stdout);
return 0;
}
四、damage
问题描述
农夫John的农场遭受了一场地震.有一些牛棚遭到了损坏,但幸运地,所有牛棚间的路经都还能使用.
FJ的农场有P(1 <= P <= 30,000)个牛棚,编号1…P. C(1 <= C <= 100,000)条双向路经联 接这些牛棚,编号为1…C. 路经i连接牛棚a_i和b_i (1 <= a_i<= P;1 <= b_i <= P).路经 可能连接a_i到它自己,两个牛棚之间可能有多条路经.农庄在编号为1的牛棚.
N (1 <= N <= P)头在不同牛棚的牛通过手机短信report_j(2 <= report_j <= P)告诉FJ它 们的牛棚(report_j)没有损坏,但是它们无法通过路径和没有损坏的牛棚回到到农场.
当FJ接到所有短信之后,找出最小的不可能回到农庄的牛棚数目.这个数目包括损坏的牛棚.
输入格式
第1行: 三个空格分开的数: P, C, 和 N
第2..C+1行: 每行两个空格分开的数: a_i 和 b_i
第C+2..C+N+1行: 每行一个数: report_j
输出格式
第1行: 一个数,最少不能回到农庄的牛的数目(包括损坏的牛棚).
样例数据
input
4 3 1
1 2
2 3
3 4
3
output
3
分析:因为发短信给FJ的牛棚是好的但是无法到达原点的牛棚,所以这个牛棚周围的牛棚必定也无法到达原点(易证),所以我们只需要将这个牛棚周围的牛棚全部标记为不能到达原点,最后从原点跑一遍bfs看能到达哪些点即可
那么具体代码如下:
#include<bits/stdc++.h>
using namespace std;
int n,m,k;
int len=0;
int linkk[1000001];
int vis[1000001];
bool f[10000001];
struct node{
int y,next;
}e[2000001];
void insert(int xx,int yy){
e[++len].next=linkk[xx];
linkk[xx]=len;
e[len].y=yy;
}
void Break(int x){
for (int i=linkk[x];i;i=e[i].next)
vis[e[i].y]=-1,f[e[i].y]=1;
}
int main(){
freopen("damage..in","r",stdin);
freopen("damage..out","w",stdout);
scanf("%d %d %d",&n,&m,&k);
for (int i=1;i<=m;i++){
int x,y;
scanf("%d %d",&x,&y);
insert(x,y);
insert(y,x);
}
memset(vis,0,sizeof(vis));
for (int i=1;i<=k;i++){
int x;
scanf("%d",&x);
vis[x]=-1;
Break(x);
}
queue < int > q;
q.push(1);
int ans=0;
// for (int i=1;i<=n;i++) cout<<vis[i]<<' ';
while (!q.empty()){
int x=q.front();
q.pop();
if (vis[x]==-1||f[x]) continue;
vis[x]=1;f[x]=1;ans++;
for (int i=linkk[x];i;i=e[i].next)
q.push(e[i].y);
}
cout<<n-ans;
fclose(stdin);
fclose(stdout);
}