问题 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了……
1 #include<iostream> 2 #include<cstdlib> 3 #include<cstdio> 4 #include<cstring> 5 #include<queue> 6 #include<algorithm> 7 #include<cmath> 8 #define N 200005 9 using namespace std; 10 int n,m,t,zz2; 11 int g[N],a[N],b[N]; 12 int f[N][2],zz; 13 struct ro{ 14 int to,l,from; 15 int next; 16 bool friend operator > (ro a,ro b) 17 { 18 return a.l>b.l; 19 } 20 }road[1000005],road2[1000006]; 21 void build(int x,int y,int z){ 22 zz++; 23 road[zz].to=y; 24 road[zz].from=x; 25 road[zz].l=z; 26 road[zz].next=a[x]; 27 a[x]=zz; 28 } 29 void build2(int x,int y){ 30 zz2++; 31 road2[zz2].from=x; 32 road2[zz2].to=y; 33 road2[zz2].next=b[x]; 34 b[x]=zz2; 35 } 36 int fa[N]; 37 int find(int x){ 38 if(fa[x]==x)return x; 39 fa[x]=find(fa[x]); 40 return fa[x]; 41 } 42 long long hav[N]; 43 void hb(int a,int b){ 44 int x=find(a); 45 int y=find(b); 46 fa[x]=y; 47 hav[y]+=hav[x]; 48 build2(y,x); 49 } 50 bool cle[N],cle2[N]; 51 priority_queue<ro,vector<ro>,greater<ro > > q1; 52 void dfs(int x,int tt){ 53 //cout<<x<<endl 54 if(!cle2[x]) 55 { 56 for(int i=a[x];i>0;i=road[i].next) 57 { 58 int y=road[i].to; 59 if(find(y)==tt) 60 { 61 q1.push(road[i]); 62 } 63 } 64 } 65 bool yx=1; 66 for(int i=b[x];i>0;i=road2[i].next) 67 { 68 int y=road2[i].to; 69 if(!cle[y]) 70 { 71 //yx=0; 72 dfs(y,tt); 73 } 74 if(!cle[y]) 75 { 76 yx=0; 77 } 78 } 79 cle[x]=yx&cle2[x]; 80 } 81 int main(){ 82 scanf("%d%d%d",&n,&m,&t); 83 for(int i=1;i<=n;i++) 84 scanf("%d",&g[i]); 85 for(int i=1;i<=m;i++) 86 scanf("%d%d",&f[i][0],&f[i][1]); 87 for(int i=1;i<=t;i++) 88 { 89 int x,y; 90 scanf("%d%d",&x,&y); 91 build(x,y,i); 92 build(y,x,i); 93 } 94 for(int i=1;i<=n;i++) 95 fa[i]=i; 96 for(int i=1;i<=m;i++) 97 { 98 int from=f[i][0],to=f[i][1]; 99 dfs(from,to); 100 while(!q1.empty()) 101 { 102 ro tt=q1.top(); 103 q1.pop(); 104 int x=tt.from,y=tt.to; 105 int p=min(g[x],g[y]); 106 g[x]-=p; 107 g[y]-=p; 108 hav[to]+=p*2; 109 if(!g[x]) cle2[x]=1; 110 if(!g[y]) cle2[y]=1; 111 } 112 hb(from,to); 113 } 114 long long sum=0; 115 for(int i=1;i<=n;i++) 116 { 117 if(find(i)==i) 118 { 119 sum+=hav[i]; 120 } 121 } 122 printf("%lld ",sum); 123 //while(1); 124 return 0; 125 }
下面我来说一下我的正解。
貌似他们说正解是一个叫做克鲁斯卡尔重构树的东西,不过我不会,于是乎借鉴了一下思想乱搞出一下时间复杂度不是玄学的东西。
首先如果我们假设每两个瓶子倒在一起之后都被扔掉,然后我们重新找一个瓶子装一下,那么我们就可以发现整个就是一个完美二叉树树林,能反应的两瓶药品如果反应一定是在他们的LCA上于是我们求一下离线LCA(之所以不用倍增是因为炸内存)。然后利用一个邻接表存一下发生在该节点的反应。然后按照他给的步骤去模拟设定一个now数组表示该药品实际在哪个节点,每合并一次就把合并后的节点的反应放进优先队列里面,处理一下就好了。
1 #include<iostream> 2 #include<cstdlib> 3 #include<cstdio> 4 #include<cstring> 5 #include<queue> 6 #include<algorithm> 7 #include<cmath> 8 #include<vector> 9 #define N 1000005 10 using namespace std; 11 int n,m,t,zz; 12 long long g[N]; 13 struct qu{ 14 int x,y; 15 int next; 16 int yxj; 17 bool friend operator >(qu a,qu b) 18 { 19 return a.yxj>b.yxj; 20 } 21 }que[1000004]; 22 struct ro{ 23 int to,next,l; 24 }road[1000005]; 25 int b[N*2],zz3; 26 void bui(int x,int y){ 27 zz3++; 28 road[zz3].to=y; 29 road[zz3].next=b[x]; 30 b[x]=zz3; 31 } 32 struct no{ 33 int son[2],bh,fa; 34 long long hav; 35 }node[N*2]; 36 int zz2,now[N],a[N],fa[N],f2[N]; 37 void build(int x,int y,int z) 38 { 39 zz2++; 40 que[zz2].x=x,que[zz2].y=y; 41 que[zz2].yxj=z; 42 que[zz2].next=a[x]; 43 a[x]=zz2; 44 } 45 int find(int x) 46 { 47 if(fa[x]==x)return x; 48 else return fa[x]=find(fa[x]); 49 } 50 void hb(int x,int y) 51 { 52 int aa=find(x),bb=find(y); 53 if(aa!=bb) 54 fa[aa]=bb; 55 } 56 bool vis[N]; 57 void work(int x) 58 { 59 f2[x]=x; 60 vis[x]=1; 61 if(node[x].son[0]) 62 { 63 work(node[x].son[0]); 64 hb(x,node[x].son[0]); 65 f2[find(x)]=x; 66 67 work(node[x].son[1]); 68 hb(x,node[x].son[1]); 69 f2[find(x)]=x; 70 } 71 for(int i=a[x];i>0;i=que[i].next) 72 { 73 int y=que[i].y; 74 if(vis[y]) 75 { 76 bui(f2[find(y)],i); 77 } 78 } 79 } 80 priority_queue<qu,vector<qu>,greater<qu > > q1; 81 int f[N][2]; 82 int main(){ 83 scanf("%d%d%d",&n,&m,&t); 84 zz=n; 85 for(int i=1;i<=n;i++) node[i].bh=i,now[i]=i; 86 for(int i=1;i<=n;i++) 87 scanf("%lld",&g[i]); 88 for(int i=1;i<=m;i++) 89 { 90 int x,y; 91 scanf("%d%d",&x,&y); 92 zz++; 93 node[now[x]].fa=node[now[y]].fa=zz; 94 node[zz].bh=y; 95 node[zz].son[0]=now[x],node[zz].son[1]=now[y]; 96 now[y]=zz; 97 f[i][0]=x,f[i][1]=y; 98 } 99 for(int i=1;i<=zz;i++) 100 fa[i]=i; 101 for(int i=1;i<=t;i++) 102 { 103 int x,y; 104 scanf("%d%d",&x,&y); 105 build(x,y,i); 106 build(y,x,i); 107 } 108 109 for(int i=1;i<=zz;i++) 110 { 111 if(!node[i].fa) 112 { 113 memset(vis,0,sizeof(vis)); 114 work(i); 115 } 116 if(i<=n) 117 now[i]=i; 118 } 119 long long ans=0; 120 for(int i=1;i<=m;i++) 121 { 122 int from=f[i][0],to=f[i][1]; 123 int x=now[from],y=now[to]; 124 int ff=node[x].fa; 125 for(int j=b[ff];j>0;j=road[j].next) 126 q1.push(que[road[j].to]); 127 while(!q1.empty()) 128 { 129 qu aa=q1.top(); 130 int p=min(g[aa.x],g[aa.y]); 131 g[aa.x]-=p; 132 g[aa.y]-=p; 133 node[ff].hav+=2*p; 134 q1.pop(); 135 } 136 node[ff].hav+=node[y].hav+node[x].hav; 137 now[to]=ff; 138 } 139 140 for(int i=1;i<=zz;i++) 141 { 142 if(!node[i].fa) 143 { 144 ans+=node[i].hav; 145 } 146 } 147 printf("%lld ",ans); 148 //while(1); 149 return 0; 150 }