BZOJ3127:[USACO2013OPEN]Yin and Yang
浅谈树分治:https://www.cnblogs.com/AKMer/p/10014803.html
题目传送门:https://www.lydsy.com/JudgeOnline/problem.php?id=3127
这题很合我的胃口,要我统计树上的“太极”路径。
假设没有要求中间某个点到两端黑白也是相等的,那么我们直接用\(f[i]\)记录黑白两种边的差值为\(i\)的路径有多少条就行了。但是要求路径上需要存在中间某个点到两个端点的黑白边也是相同的。那么我们就用\(f[i][0]\)记录差值为\(i\),不存在这种点的路径条数,\(f[i][1]\)记录存在这种点的路径条数。然后根据根到当前点的路径上有无这种点,分别用\(f[-num][0]+f[-num][1]\)和\(f[-num][1]\)更新即可,\(num\)表示根到当前点的路径上黑白边的差值。如果判断路径上是否有这种点呢?记一个全局数组\(sum\),\(sum[i]\)表示在当前递归的栈里,从根到这个点路径上黑白边差值为\(i\)的点有多少个。如果\(sum[num]\)不为零,那么从根到当前点的路径上必然会有一个这样的点。
时间复杂度:\(O(nlogn)\)
空间复杂度:\(O(n)\)
点分治版代码如下:
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
#define fr first
#define sc second
const int maxn=1e5+5;
ll ans;
bool vis[maxn];
int n,tot,mx,rt,N;
int sum[maxn<<1],f[maxn<<1][2],siz[maxn];
int now[maxn],pre[maxn*2],son[maxn*2],val[maxn*2];
int read() {
int x=0,f=1;char ch=getchar();
for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0';
return x*f;
}
void add(int a,int b,int c) {
pre[++tot]=now[a];
now[a]=tot,son[tot]=b,val[tot]=c;
}
struct rubbish {
int top;
pii sta[maxn];
bool bo[maxn<<1][2];
void clear() {
while(top) {
int num1=sta[top].fr,num2=sta[top].sc;
bo[num1][num2]=0,f[num1][num2]=0,top--;
}
}
void ins(int a,int b) {
if(bo[a][b])return;
bo[a][b]=1;
sta[++top]=make_pair(a,b);
}
}R;
void find_son(int fa,int u) {
int res=0;siz[u]=1;
for(int p=now[u],v=son[p];p;p=pre[p],v=son[p])
if(!vis[v]&&v!=fa)find_son(u,v),siz[u]+=siz[v],res=max(res,siz[v]);
res=max(res,N-siz[u]);
if(res<mx)mx=res,rt=u;
}
void query(int fa,int u,int num) {
if(sum[num+maxn]||num==0)ans+=f[-num+maxn][0]+f[-num+maxn][1];
else ans+=f[-num+maxn][1];
sum[num+maxn]++;
for(int p=now[u],v=son[p];p;p=pre[p],v=son[p])
if(!vis[v]&&v!=fa)query(u,v,num+val[p]);
sum[num+maxn]--;
}
void solve(int fa,int u,int num) {
f[num+maxn][sum[num+maxn]!=0]++;
R.ins(num+maxn,sum[num+maxn]!=0);
sum[num+maxn]++,siz[u]=1;
for(int p=now[u],v=son[p];p;p=pre[p],v=son[p])
if(!vis[v]&&v!=fa)solve(u,v,num+val[p]),siz[u]+=siz[v];
sum[num+maxn]--;
}
void work(int u,int size) {
N=size,mx=rt=n+1,find_son(0,u),u=rt,vis[u]=1;
for(int p=now[u],v=son[p];p;p=pre[p],v=son[p])
if(!vis[v]) {
query(u,v,val[p]);
solve(u,v,val[p]);
}
ans+=f[maxn][1];R.clear();
for(int p=now[u],v=son[p];p;p=pre[p],v=son[p])
if(!vis[v])work(v,siz[v]);
}
int main() {
n=read();
for(int i=1;i<n;i++) {
int a=read(),b=read(),c=(read()<<1)-1;
add(a,b,c),add(b,a,c);
}work(1,n);printf("%lld\n",ans);
return 0;
}
边分治版代码如下:
#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
#define fr first
#define sc second
const int maxn=2e5+5;
ll ans;
bool vis[maxn];
int n,tot,mx,id,N;
int siz[maxn],f[maxn<<1][2],sum[maxn<<1];
int now[maxn],pre[maxn*2],son[maxn*2],val[maxn*2];
vector<pii>to[maxn];
vector<pii>::iterator it;
int read() {
int x=0,f=1;char ch=getchar();
for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0';
return x*f;
}
void add(int a,int b,int c) {
pre[++tot]=now[a];
now[a]=tot,son[tot]=b,val[tot]=c;
}
void find_son(int fa,int u) {
for(int p=now[u],v=son[p];p;p=pre[p],v=son[p])
if(v!=fa)to[u].push_back(make_pair(v,val[p])),find_son(u,v);
}
void rebuild() {
tot=1;memset(now,0,sizeof(now));
for(int i=1;i<=n;i++) {
int size=to[i].size();
if(size<=2) {
for(it=to[i].begin();it!=to[i].end();it++) {
pii tmp=*it;
add(i,tmp.fr,tmp.sc),add(tmp.fr,i,tmp.sc);
}
}
else {
pii u1=make_pair(++n,0),u2;
if(size==3)u2=to[i].front();
else u2=make_pair(++n,0);
add(i,u1.fr,u1.sc),add(u1.fr,i,u1.sc);
add(i,u2.fr,u2.sc),add(u2.fr,i,u2.sc);
if(size==3) {
for(int j=1;j<3;j++)
to[n].push_back(to[i].back()),to[i].pop_back();
}
else {
int p=0;
for(it=to[i].begin();it!=to[i].end();it++) {
if(!p)to[n-1].push_back(*it);
else to[n].push_back(*it);p^=1;
}
}
}
}
}
struct rubbish {
int top;
pii sta[maxn];
bool bo[maxn<<1][2];
void clear() {
while(top) {
int num1=sta[top].fr,num2=sta[top].sc;
bo[num1][num2]=0,f[num1][num2]=0,top--;
}
}
void ins(int a,int b) {
if(bo[a][b])return;
bo[a][b]=1,sta[++top]=make_pair(a,b);
}
}R;
void find_edge(int fa,int u) {
siz[u]=1;
for(int p=now[u],v=son[p];p;p=pre[p],v=son[p])
if(!vis[p>>1]&&v!=fa) {
find_edge(u,v),siz[u]+=siz[v];
if(abs(N-2*siz[v])<mx)
mx=abs(N-2*siz[v]),id=p>>1;
}
}
void solve(int fa,int u,int num,bool bo) {
if(bo) {
f[num+maxn][(sum[num+maxn]!=0)]++;
R.ins(num+maxn,sum[num+maxn]!=0),sum[num+maxn]++;
}siz[u]=1;
for(int p=now[u],v=son[p];p;p=pre[p],v=son[p])
if(!vis[p>>1]&&v!=fa)solve(u,v,num+val[p],val[p]!=0),siz[u]+=siz[v];
if(bo)sum[num+maxn]--;
}
void query(int fa,int u,int num,bool bo) {
if(bo) {
if(!sum[num+maxn])ans+=f[-num+maxn][1];
else {
ans+=f[-num+maxn][0]+f[-num+maxn][1];
if(val[id<<1]==0&&num==0)ans--;//这种情况就是,在u2所在的联通块里找到了一条满足条件的路径,但是会在这里被f[-num+maxn][0],也就是0号点到u1的路径匹配上算一次,到时候去处理u2所在的联通块会被重复计算,所以就减掉了。
}
sum[num+maxn]++;
}siz[u]=1;
for(int p=now[u],v=son[p];p;p=pre[p],v=son[p])
if(!vis[p>>1]&&v!=fa)query(u,v,num+val[p],val[p]!=0),siz[u]+=siz[v];
if(bo)sum[num+maxn]--;
}
void work(int u,int size) {
if(size<2)return;
N=size,mx=id=n+1,find_edge(0,u),vis[id]=1;
int u1=son[id<<1],u2=son[id<<1|1];
solve(0,u1,0,1),query(0,u2,val[id<<1],(val[id<<1]!=0));
R.clear(),work(u1,siz[u1]),work(u2,siz[u2]);
}
int main() {
n=read();
for(int i=1;i<n;i++) {
int a=read(),b=read(),c=(read()<<1)-1;
add(a,b,c),add(b,a,c);
}find_son(0,1),rebuild();
work(1,n),printf("%lld\n",ans);
return 0;
}
BZOJ3127:[USACO2013OPEN]Yin and Yang的更多相关文章
- bzoj3127/3697 [Usaco2013 Open]Yin and Yang
传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=3127 http://www.lydsy.com/JudgeOnline/problem.ph ...
- 洛谷P3085 [USACO13OPEN]阴和阳Yin and Yang(点分治,树上差分)
洛谷题目传送门 闲话 偶然翻到一道没有题解的淀粉质,想证明一下自己是真的弱 然而ZSYC(字符串组合)早就切了 然后证明成功了,WA到怀疑人生,只好借着ZSY的代码拍,拍了几万组就出来了... 思路 ...
- 【BZOJ】【3697】采药人的路径&【3127】【USACO2013 Open】Yin and Yang
点分治 Orz hzwer 倒是比较好想到点分治……然而在方案统计这里,我犯了两个错误…… 1.我比较傻逼的想的是:通过儿子来更新父亲,也就是统计以x为根的子树中xxxx的路径有多少条……这样转移. ...
- Yin and Yang Stones(思路题)
Problem Description: A mysterious circular arrangement of black stones and white stones has appeared ...
- BZOJ 3127 [Usaco2013 Open]Yin and Yang(树点分治)
[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=3127 [题目大意] 给出一棵01边权树,求存在多少条路径,使得路径上0和1的数量相同, ...
- Usaco2012-2013 金组 题解 (暂缺Hill walk以及Figue eight)
https://files.cnblogs.com/files/Winniechen/usaco2012-2013.pdf 做的不是很好,还请见谅! 如果有什么疑问,可以QQ上找我. QQ号:1967 ...
- 转:Mysql在大型网站的应用架构演变
原文来自于:http://www.cnblogs.com/Creator/p/3776110.html 原创文章,转载请注明: 转载自http://www.cnblogs.com/Creator/本文 ...
- 更换JDK版本时的问题:Error: could not open `C:\Java\jre7\lib\amd64\jvm.cfg'
1.先把oracle自带的weblogic给卸载了,然后打开eclipse,发现报错了:Error: could not open `C:\Java\jre7\lib\amd64\jvm.cfg' J ...
- Ansible13:Playbook循环语句
目录 循环语句 简介 loop关键字说明 在循环语句中注册变量 旧循环语句 1. with_items 2. with_nested 3. with_dict 4. with_fileglob 5. ...
随机推荐
- iframe子页面获取父页面元素的方法
在iframe子页面获取父页面元素 代码如下: $.('#objld', parent.document); 在父页面获取iframe子页面的元素 代码如下: $("#objid" ...
- VSTS跟Kubernetes整合进行CI/CD
利用VSTS跟Kubernetes整合进行CI/CD 为什么VSTS要搭配Kubernetes? 通常我们在开发管理软件项目的时候都会碰到一个很头痛的问题,就是开发.测试.生产环境不一致,导致开发 ...
- 如何利用JQuery获取iframe内联框架对象?
parent.$("#iframeID").get(0).contentWindow; 父.$("选择器").get(0).contentWindow; get ...
- 【Caffe】源码解析----caffe.proto (转载)
分析caffe源码,看首先看caffe.proto,是明智的选择.好吧,我不是创造者,只是搬运工. 原文地址:http://blog.csdn.net/qq_16055159/article/deta ...
- Swift实战(一): 剪子包袱锤ios应用
来自十奶的大作业教学视频. http://www.swiftv.cn/course/ic2tqzob 主要了解了MVC模型. 首先是View,通过设计mainstoryboard构建UI界面,主要靠拖 ...
- jQuery+bootstrap实现美化警告/确认/提示对话框插件
http://www.html580.com/12067/demo http://craftpip.github.io/jquery-confirm/
- c# combobox 绑定枚举方式
建立一个类 : using System; using System.Collections.Generic; using System.Linq; using System.Text; using ...
- 【分享】自己写的一个可空的DateTimePicker控件-附源码
最近这段时间在重构以前的一个项目,其中有一项就是要把DateTimePicker控件值可空.大家都知道的DateTimePicker值为DateTime类型,DateTime类型值不能等于Null.但 ...
- 九度OJ 1018:统计同成绩学生人数 (基础题)
时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:8807 解决:4651 题目描述: 读入N名学生的成绩,将获得某一给定分数的学生人数输出. 输入: 测试输入包含若干测试用例,每个测试用例的 ...
- 【题解】P4886快递员
[题解]P4886 快递员 淀粉质好题!!!加深了我对点分治的理解.最近分治学了好多啊. 题目大意 给定你一颗有边权的树,再给你\(m\)和点对,请你在树上选出来一个点,使得所有点对到这个点的距离的最 ...