问题 B: Fiolki

时间限制: 3 Sec  内存限制: 128 MB

题目描述

化学家吉丽想要配置一种神奇的药水来拯救世界。

吉丽有n种不同的液体物质,和n个药瓶(均从1到n编号)。初始时,第i个瓶内装着g[i]克的第i种物质。吉丽需要执行一定的步骤来配置药水,第i个步骤是将第a[i]个瓶子内的所有液体倒入第b[i]个瓶子,此后第a[i]个瓶子不会再被用到。瓶子的容量可以视作是无限的。

吉丽知道某几对液体物质在一起时会发生反应产生沉淀,具体反应是1克c[i]物质和1克d[i]物质生成2克沉淀,一直进行直到某一反应物耗尽。生成的沉淀不会和任何物质反应。当有多于一对可以发生反应的物质在一起时,吉丽知道它们的反应顺序。每次倾倒完后,吉丽会等到反应结束后再执行下一步骤。

吉丽想知道配置过程中总共产生多少沉淀。

输入

第一行三个整数n,m,k(0<=m<n<=200000,0<=k<=500000),分别表示药瓶的个数(即物质的种数),操作步数,可以发生的反应数量。

第二行有n个整数g[1],g[2],…,g[n](1<=g[i]<=10^9),表示初始时每个瓶内物质的质量。

接下来m行,每行两个整数a[i],b[i](1<=a[i],b[i]<=n,a[i]≠b[i]),表示第i个步骤。保证a[i]在以后的步骤中不再出现。

接下来k行,每行是一对可以发生反应的物质c[i],d[i](1<=c[i],d[i]<=n,c[i]≠d[i]),按照反应的优先顺序给出。同一个反应不会重复出现。

输出

配置过程中总共产生多少沉淀。

样例输入

3 2 1 2 3 4 1 2 3 2 2 3

样例输出

6

 乱搞压正解,暴力出奇迹。
 数据水到一定地步,当时本来期望打50分的暴力A了……
 
 #include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
#include<cmath>
#define N 200005
using namespace std;
int n,m,t,zz2;
int g[N],a[N],b[N];
int f[N][],zz;
struct ro{
int to,l,from;
int next;
bool friend operator > (ro a,ro b)
{
return a.l>b.l;
}
}road[],road2[];
void build(int x,int y,int z){
zz++;
road[zz].to=y;
road[zz].from=x;
road[zz].l=z;
road[zz].next=a[x];
a[x]=zz;
}
void build2(int x,int y){
zz2++;
road2[zz2].from=x;
road2[zz2].to=y;
road2[zz2].next=b[x];
b[x]=zz2;
}
int fa[N];
int find(int x){
if(fa[x]==x)return x;
fa[x]=find(fa[x]);
return fa[x];
}
long long hav[N];
void hb(int a,int b){
int x=find(a);
int y=find(b);
fa[x]=y;
hav[y]+=hav[x];
build2(y,x);
}
bool cle[N],cle2[N];
priority_queue<ro,vector<ro>,greater<ro > > q1;
void dfs(int x,int tt){
//cout<<x<<endl
if(!cle2[x])
{
for(int i=a[x];i>;i=road[i].next)
{
int y=road[i].to;
if(find(y)==tt)
{
q1.push(road[i]);
}
}
}
bool yx=;
for(int i=b[x];i>;i=road2[i].next)
{
int y=road2[i].to;
if(!cle[y])
{
//yx=0;
dfs(y,tt);
}
if(!cle[y])
{
yx=;
}
}
cle[x]=yx&cle2[x];
}
int main(){
scanf("%d%d%d",&n,&m,&t);
for(int i=;i<=n;i++)
scanf("%d",&g[i]);
for(int i=;i<=m;i++)
scanf("%d%d",&f[i][],&f[i][]);
for(int i=;i<=t;i++)
{
int x,y;
scanf("%d%d",&x,&y);
build(x,y,i);
build(y,x,i);
}
for(int i=;i<=n;i++)
fa[i]=i;
for(int i=;i<=m;i++)
{
int from=f[i][],to=f[i][];
dfs(from,to);
while(!q1.empty())
{
ro tt=q1.top();
q1.pop();
int x=tt.from,y=tt.to;
int p=min(g[x],g[y]);
g[x]-=p;
g[y]-=p;
hav[to]+=p*;
if(!g[x]) cle2[x]=;
if(!g[y]) cle2[y]=;
}
hb(from,to);
}
long long sum=;
for(int i=;i<=n;i++)
{
if(find(i)==i)
{
sum+=hav[i];
}
}
printf("%lld\n",sum);
//while(1);
return ;
}

暴力

 下面我来说一下我的正解。 
 貌似他们说正解是一个叫做克鲁斯卡尔重构树的东西,不过我不会,于是乎借鉴了一下思想乱搞出一下时间复杂度不是玄学的东西。
 首先如果我们假设每两个瓶子倒在一起之后都被扔掉,然后我们重新找一个瓶子装一下,那么我们就可以发现整个就是一个完美二叉树树林,能反应的两瓶药品如果反应一定是在他们的LCA上于是我们求一下离线LCA(之所以不用倍增是因为炸内存)。然后利用一个邻接表存一下发生在该节点的反应。然后按照他给的步骤去模拟设定一个now数组表示该药品实际在哪个节点,每合并一次就把合并后的节点的反应放进优先队列里面,处理一下就好了。
 
 #include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
#include<cmath>
#include<vector>
#define N 1000005
using namespace std;
int n,m,t,zz;
long long g[N];
struct qu{
int x,y;
int next;
int yxj;
bool friend operator >(qu a,qu b)
{
return a.yxj>b.yxj;
}
}que[];
struct ro{
int to,next,l;
}road[];
int b[N*],zz3;
void bui(int x,int y){
zz3++;
road[zz3].to=y;
road[zz3].next=b[x];
b[x]=zz3;
}
struct no{
int son[],bh,fa;
long long hav;
}node[N*];
int zz2,now[N],a[N],fa[N],f2[N];
void build(int x,int y,int z)
{
zz2++;
que[zz2].x=x,que[zz2].y=y;
que[zz2].yxj=z;
que[zz2].next=a[x];
a[x]=zz2;
}
int find(int x)
{
if(fa[x]==x)return x;
else return fa[x]=find(fa[x]);
}
void hb(int x,int y)
{
int aa=find(x),bb=find(y);
if(aa!=bb)
fa[aa]=bb;
}
bool vis[N];
void work(int x)
{
f2[x]=x;
vis[x]=;
if(node[x].son[])
{
work(node[x].son[]);
hb(x,node[x].son[]);
f2[find(x)]=x; work(node[x].son[]);
hb(x,node[x].son[]);
f2[find(x)]=x;
}
for(int i=a[x];i>;i=que[i].next)
{
int y=que[i].y;
if(vis[y])
{
bui(f2[find(y)],i);
}
}
}
priority_queue<qu,vector<qu>,greater<qu > > q1;
int f[N][];
int main(){
scanf("%d%d%d",&n,&m,&t);
zz=n;
for(int i=;i<=n;i++) node[i].bh=i,now[i]=i;
for(int i=;i<=n;i++)
scanf("%lld",&g[i]);
for(int i=;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
zz++;
node[now[x]].fa=node[now[y]].fa=zz;
node[zz].bh=y;
node[zz].son[]=now[x],node[zz].son[]=now[y];
now[y]=zz;
f[i][]=x,f[i][]=y;
}
for(int i=;i<=zz;i++)
fa[i]=i;
for(int i=;i<=t;i++)
{
int x,y;
scanf("%d%d",&x,&y);
build(x,y,i);
build(y,x,i);
} for(int i=;i<=zz;i++)
{
if(!node[i].fa)
{
memset(vis,,sizeof(vis));
work(i);
}
if(i<=n)
now[i]=i;
}
long long ans=;
for(int i=;i<=m;i++)
{
int from=f[i][],to=f[i][];
int x=now[from],y=now[to];
int ff=node[x].fa;
for(int j=b[ff];j>;j=road[j].next)
q1.push(que[road[j].to]);
while(!q1.empty())
{
qu aa=q1.top();
int p=min(g[aa.x],g[aa.y]);
g[aa.x]-=p;
g[aa.y]-=p;
node[ff].hav+=*p;
q1.pop();
}
node[ff].hav+=node[y].hav+node[x].hav;
now[to]=ff;
} for(int i=;i<=zz;i++)
{
if(!node[i].fa)
{
ans+=node[i].hav;
}
}
printf("%lld\n",ans);
//while(1);
return ;
}

LCA做法

 

Fiolki题解的更多相关文章

  1. 【BZOJ3712】Fiolki(并查集重构树)

    [BZOJ3712]Fiolki(并查集重构树) 题面 BZOJ 题解 很神仙的题目. 我们发现所有的合并关系构成了一棵树. 那么两种不同的东西如果产生反应,一定在两个联通块恰好联通的时候反应. 那么 ...

  2. 2016 华南师大ACM校赛 SCNUCPC 非官方题解

    我要举报本次校赛出题人的消极出题!!! 官方题解请戳:http://3.scnuacm2015.sinaapp.com/?p=89(其实就是一堆代码没有题解) A. 树链剖分数据结构板题 题目大意:我 ...

  3. noip2016十连测题解

    以下代码为了阅读方便,省去以下头文件: #include <iostream> #include <stdio.h> #include <math.h> #incl ...

  4. BZOJ-2561-最小生成树 题解(最小割)

    2561: 最小生成树(题解) Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1628  Solved: 786 传送门:http://www.lyd ...

  5. Codeforces Round #353 (Div. 2) ABCDE 题解 python

    Problems     # Name     A Infinite Sequence standard input/output 1 s, 256 MB    x3509 B Restoring P ...

  6. 哈尔滨理工大学ACM全国邀请赛(网络同步赛)题解

    题目链接 提交连接:http://acm-software.hrbust.edu.cn/problemset.php?page=5 1470-1482 只做出来四道比较水的题目,还需要加强中等题的训练 ...

  7. 2016ACM青岛区域赛题解

    A.Relic Discovery_hdu5982 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Jav ...

  8. poj1399 hoj1037 Direct Visibility 题解 (宽搜)

    http://poj.org/problem?id=1399 http://acm.hit.edu.cn/hoj/problem/view?id=1037 题意: 在一个最多200*200的minec ...

  9. 网络流n题 题解

    学会了网络流,就经常闲的没事儿刷网络流--于是乎来一发题解. 1. COGS2093 花园的守护之神 题意:给定一个带权无向图,问至少删除多少条边才能使得s-t最短路的长度变长. 用Dijkstra或 ...

随机推荐

  1. 2013年新年礼物---CrossFPC 终于出来了

    2012年12月份,玛雅人的预言没有实现,一个内部进行了7年开发的CrossFPC 终于见光了. 网址:http://www.crossfpc.com/ Welcome to CrossFPC, a ...

  2. Android零基础入门第10节:开发IDE大升级,终于迎来了Android Studio

    原文:Android零基础入门第10节:开发IDE大升级,终于迎来了Android Studio 通过前面几期的学习,我们知道了Android的前世今生,也了解了Android的系统架构和应用组件,也 ...

  3. SetForegroundWindow API函数还不够(好多好多解决方案,真是奇思妙想)

    好多好多解决方案: var Input: TInput; begin ZeroMemory(@Input, SizeOf(Input)); SendInput(, Input, SizeOf(Inpu ...

  4. vue补充

    一.安装vue-cli脚手架 1.淘宝镜像下载 用淘宝的国内服务器来向国外的服务器请求,我们向淘宝请求,而不是由我们直接向国外的服务器请求,会大大提升请求速度,使用时,将所有的npm命令换成cnpm即 ...

  5. flask(四)

    1.Flask-Session from flask import session,Flask from flask_session import Session #导入 from redis imp ...

  6. shell多线程

    a='a1' b='b1' c='c1' d='d1' e='e1' ARR=($a $b $c $d $e) rd=`date +%N` for i in ${ARR[*]} do { ;>& ...

  7. JVM(六):探究类加载过程-下

    JVM(六):探究类加载过程-下 上文说了类加载过程的5个阶段,着重介绍了各个阶段做的工作.在本文中,我们对执行加载阶段的主体进行探讨,学习类加载器的模型和逻辑,以及我们该如何自定义一个类加载器. 定 ...

  8. element-ui源码之组件通信那些事

    最近在用element-ui重构前端项目,无意之中翻阅到一个比较好用的组件间通信方式,借助于vue的封装的发布-订阅消息模式与mixin语法.在开始之前先总结下vue常用的组件间通信方式,具体如下: ...

  9. 07 jQuery的位置信息

    一.宽度和高度 获取宽度 .width() 描述:为匹配的元素集合中获取第一个元素的当前计算宽度值.这个方法不接受任何参数..css(width) 和 .width()之间的区别是后者返回一个没有单位 ...

  10. java多线程死锁

    进程(线程)同步的基本概念 进程之间的制约关系 1. 直接制约关系(进程同步) 这个关系主要源于进程合作,例如,有一个输入进程A通过单缓冲向进程B提供数据,当该缓冲空时,进程B因为不能获得所需数据而被 ...