mcfx's blog - 数据结构
/category/ds/
-
Codeforces 809E. Surprise me!
/archives/236/
2017-05-25T20:24:07+08:00
###Description
给一棵树,每个点点权 $$a_i$$,保证 $$a_i$$各不相同,现在随机选两个点 $$u,v$$,求 $$f(u,v)=\phi(a_u\cdot a_v)\cdot dis(u,v)$$ 的期望,$$mod\ 10^9+7$$。
链接:[http://codeforces.com/contest/809/problem/E](http://codeforces.com/contest/809/problem/E "http://codeforces.com/contest/809/problem/E")
###Solution
首先,有一个结论:$$\phi(a\cdot b)=\frac{\phi(a)\cdot\phi(b)\cdot g}{\phi(g)},g=gcd(a,b)$$
证明:考虑找一个 $$x$$,使得 $$x|b,gcd(\frac{b}{x},a)=1,gcd(x,\frac{b}{x})=1$$ 且 $$a$$ 包含 $$x$$ 中所有质因子。
那么 $$\phi(a\cdot b)=\phi(a)\cdot x\cdot\phi(\frac{b}{x})=\frac{\phi(a)\cdot\phi(b)\cdot x}{\phi(x)}$$
显然,$$x$$ 是 $$g$$ 的倍数,所以 $$\frac{x}{g}=\frac{\phi(x)}{\phi(g)}$$,所以$$\phi(a\cdot b)=\frac{\phi(a)\cdot\phi(b)\cdot g}{\phi(g)}$$
有了这个结论之后可以考虑点分,那么设 $$dis(x)$$ 表示 $$x$$ 到当前根的距离。
设 $$h(x)=\frac{x}{\phi(x)}$$,那么$$f(u,v)=\phi(a_u)\cdot\phi(a_v)\cdot h(g)\cdot(dis(u)+dis(v)),g=gcd(a_u,a_v)$$
考虑当 $$u$$ 确定时,需要知道 $$\sum\phi(a_v)\cdot h(gcd(a_u,a_v))$$ 和 $$\sum\phi(a_v)\cdot h(gcd(a_u,a_v))\cdot dis(v)$$。
设 $$g(x)=h(x)-\sum[y|x,y\neq x]g(y)$$,那么 $$h(gcd(a_u,a_v))=\sum[x|a_u,x|a_v]g(x)$$
这样就可以直接枚举约数算贡献了。
###Code
```c++
#include
typedef long long ll;
inline void repr(int&a,int b){if(ato,x);
sz[x]+=sz[i->to];
}
}
inline void dfs2(int x,int fa)
{
int t=rsz-sz[x];
for(edge*i=p[x];i;i=i->ne)
if(i->to!=fa&&!vis[i->to])
{
repr(t,sz[i->to]);
dfs2(i->to,x);
}
if(tne)
if(i->to!=fa&&!vis[i->to])
dfs3(i->to,x,dis+1);
}
inline void dfs4(int x,int fa,int dis)
{
int a=phi[w[x]],b=(ll)a*dis%P,A,B;
foe(i,v[w[x]])
{
get(*i,A,B);
ans=(ans+(ll)a*B+(ll)b*A)%P;
}
for(edge*i=p[x];i;i=i->ne)
if(i->to!=fa&&!vis[i->to])
dfs4(i->to,x,dis+1);
}
inline void work(int x)
{
dfs1(x,0);
rsz=nsz=sz[x],nro=x;
dfs2(x,0);
vis[x=nro]=1;
foe(i,v[w[x]])set(*i,phi[w[x]],0);
for(edge*i=p[x];i;i=i->ne)
if(!vis[i->to])
{
dfs4(i->to,x,1);
dfs3(i->to,x,1);
}
fo0(i,qe)g[q[i]]=h[q[i]]=-1;
qe=0;
for(edge*i=p[x];i;i=i->ne)
if(!vis[i->to])work(i->to);
}
int main()
{
fo1(i,N-1)for(int j=i;j
-
BZOJ 2989: 数列 & 4170: 极光
/archives/163/
2017-01-08T00:20:00+08:00
###Description
给定一个长度为n的正整数数列a[i]。
定义2个位置的graze值为两者位置差与数值差的和,即graze(x,y)=|x-y|+|a[x]-a[y]|。
2种操作(k都是正整数):
1.Modify x k:将第x个数的值修改为k。
2.Query x k:询问有几个i满足graze(x,i)
-
BZOJ 4539: [Hnoi2016]树
/archives/149/
2016-12-16T15:59:00+08:00
###Description
小A想做一棵很大的树,但是他手上的材料有限,只好用点小技巧了。开始,小A只有一棵结点数为N的树,结点的编号为1,2,…,N,其中结点1为根;我们称这颗树为模板树。小A决定通过这棵模板树来构建一颗大树。构建过程如下:(1)将模板树复制为初始的大树。(2)以下(2.1)(2.2)(2.3)步循环执行M次(2.1)选择两个数字a,b,其中1=k)return kth(s[a].lc,s[b].lc,l,f,k);
return kth(s[a].rc,s[b].rc,f+1,r,k-t);
}
int root[N+1];
}
int n,m,q;
namespace t1
{
int p[N+1],idm,id[N+1],idr[N+1],fa[N+1][17],dep[N+1],sz[N+1];
struct edge
{
int to,ne;
}e[N
-
BZOJ 1176&2683&4066
/archives/97/
2016-12-11T14:51:00+08:00
[传送门][1] [传送门][2] [传送门][3]
题目大意:单点加,矩阵查询
直接上kdtree就好了,定期暴力重构
1176题代码:
```c++
#include
typedef unsigned char uchar;
typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef long double ldb;
#define xx first
#define yy second
template inline T max(T a,T b){return a>b?a:b;}
template inline T min(T a,T b){return a0?a:-a;}
template inline void repr(T &a,T b){if(ab)a=b;}
template T gcd(T a,T b){if(b)return gcd(b,a%b);return a;}
template T sqr(T x){return x*x;}
#define mp(a,b) std::make_pair(a,b)
#define pb push_back
#define lb(x) ((x)&(-(x)))
namespace kd
{
const int D=2,inf=1000000000;
char dnow;
struct point
{
int d[D],val;
int operator[](int x){return d[x];}
};
inline bool operator
-
BZOJ 3697: 采药人的路径
/archives/96/
2016-12-05T13:02:00+08:00
###Description
采药人的药田是一个树状结构,每条路径上都种植着同种药材。
采药人以自己对药材独到的见解,对每种药材进行了分类。大致分为两类,一种是阴性的,一种是阳性的。
采药人每天都要进行采药活动。他选择的路径是很有讲究的,他认为阴阳平衡是很重要的,所以他走的一定是两种药材数目相等的路径。采药工作是很辛苦的,所以他希望他选出的路径中有一个可以作为休息站的节点(不包括起点和终点),满足起点到休息站和休息站到终点的路径也是阴阳平衡的。他想知道他一共可以选择多少种不同的路径。
###Input
第1行包含一个整数N。
接下来N-1行,每行包含三个整数a_i、b_i和t_i,表示这条路上药材的类型。
###Output
输出符合采药人要求的路径数目。
###Sample Input
7
1 2 0
3 1 1
2 4 0
5 2 0
6 3 1
5 7 1
###Sample Output
1
###HINT
对于100%的数据,N ≤ 100,000。
###Solution
点分治,边权为0变成-1,dfs时记前缀和,休息站就判之前有没有出现过该权值,注意处理根节点
###Code
```c++
#include
typedef unsigned char uchar;
typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef long double ldb;
#define xx first
#define yy second
template inline T max(T a,T b){return a>b?a:b;}
template inline T min(T a,T b){return a0?a:-a;}
template inline void repr(T &a,T b){if(ab)a=b;}
template T gcd(T a,T b){if(b)return gcd(b,a%b);return a;}
#define mp(a,b) std::make_pair(a,b)
#define pb push_back
#define lb(x) ((x)&(-(x)))
#define sqr(x) ((x)*(x))
int n,p[100001],em=1,sz[100001],ro,nro,apr[200000],cnt[200000][2];
ll ans;
bool vis[100001];
struct edge
{
int to,ne,w;
}e[200000];
inline void add(int a,int b,int w)
{
e[em].to=b,e[em].ne=p[a],e[em].w=w,p[a]=em++;
}
void dfs1(int x,int fa)
{
sz[x]=1;
for(int i=p[x];i;i=e[i].ne)
if(!vis[e[i].to]&&e[i].to!=fa)
dfs1(e[i].to,x),sz[x]+=sz[e[i].to];
}
void dfs2(int x,int fa)
{
bool ok=sz[ro]sz[ro])ok=0;
}
if(ok)nro=x;
}
void dfs3(int x,int fa,int len)
{
if(len)
{
if(apr[len+n])
ans+=cnt[n-len][0];
else
ans+=cnt[n-len][1];
}
else
{
if(apr[len+n])
ans+=cnt[n][0]+1;
else
ans+=cnt[n][0];
}
apr[len+n]++;
for(int i=p[x];i;i=e[i].ne)
if(!vis[e[i].to]&&e[i].to!=fa)
dfs3(e[i].to,x,len+e[i].w);
apr[len+n]--;
}
void dfs4(int x,int fa,int len)
{
if(len)
{
if(apr[len+n])
cnt[n+len][0]++,cnt[n+len][1]++;
else
cnt[n+len][0]++;
}
else
{
cnt[n][0]++;
}
apr[len+n]++;
for(int i=p[x];i;i=e[i].ne)
if(!vis[e[i].to]&&e[i].to!=fa)
dfs4(e[i].to,x,len+e[i].w);
apr[len+n]--;
}
void dfs5(int x,int fa,int len)
{
cnt[n+len][0]=cnt[n+len][1]=0;
for(int i=p[x];i;i=e[i].ne)
if(!vis[e[i].to]&&e[i].to!=fa)
dfs5(e[i].to,x,len+e[i].w);
}
void solve(int x)
{
dfs1(x,0);
ro=x;
dfs2(x,0);
vis[nro]=1;
for(int i=p[nro];i;i=e[i].ne)
if(!vis[e[i].to])dfs3(e[i].to,0,e[i].w),dfs4(e[i].to,0,e[i].w);
dfs5(nro,0,0);
for(int i=p[nro];i;i=e[i].ne)
if(!vis[e[i].to])solve(e[i].to);
}
int main()
{
scanf("%d",&n);
for(int i=1;i
-
BZOJ 4700: 适者
/archives/90/
2016-12-01T20:46:00+08:00
###Description
敌方有n台人形兵器,每台的攻击力为Ai,护甲值为Di。我方只有一台人形兵器,攻击力为ATK。战斗看作回合制,
每回合进程如下:
·1 我方选择对方某台人形兵器并攻击,令其护甲值减少ATK,若护甲值
-
BZOJ 4722: 由乃
/archives/83/
2016-11-27T12:35:00+08:00
###Description
给一个长为n的序列a,每个数在0到v - 1之间,有m次操作。
操作1:每次询问一个区间中是否可以选出两个下标的集合X,Y,满足:
1.X和Y没有交集
2.设集合X中有一个元素是i,则其对集合X的贡献是a[i] + 1,要求集合X的元素的总贡献和集合Y的元素的总贡献
相等如果可以选出这两个集合,输出 Yuno否则输出 Yuki
操作2:修改一个区间l,r之间的数,使得所有l
-
BZOJ 3196: Tyvj 1730 二逼平衡树
/archives/80/
2016-11-17T00:28:00+08:00
###Description
您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:
1.查询k在区间内的排名
2.查询区间内排名为k的值
3.修改某一位值上的数值
4.查询k在区间内的前驱(前驱定义为小于x,且最大的数)
5.查询k在区间内的后继(后继定义为大于x,且最小的数)
###Input
第一行两个数 n,m 表示长度为n的有序序列和m个操作
第二行有n个数,表示有序序列
下面有m行,opt表示操作标号
若opt=1 则为操作1,之后有三个数l,r,k 表示查询k在区间[l,r]的排名
若opt=2 则为操作2,之后有三个数l,r,k 表示查询区间[l,r]内排名为k的数
若opt=3 则为操作3,之后有两个数pos,k 表示将pos位置的数修改为k
若opt=4 则为操作4,之后有三个数l,r,k 表示查询区间[l,r]内k的前驱
若opt=5 则为操作5,之后有三个数l,r,k 表示查询区间[l,r]内k的后继
###Output
对于操作1,2,4,5各输出一行,表示查询结果
###Sample Input
9 6
4 2 2 1 9 4 0 1 1
2 1 4 3
3 4 10
2 1 4 3
1 2 5 9
4 3 9 5
5 2 8 5
###Sample Output
2
4
3
4
9
###HINT
1.n和m的数据范围:n,mb?a:b;}
template inline T min(T a,T b){return a0?a:-a;}
template inline void repr(T &a,T b){if(ab)a=b;}
template T gcd(T a,T b){if(b)return gcd(b,a%b);return a;}
#define mp(a,b) std::make_pair(a,b)
#define pb push_back
#define lb(x) ((x)&(-(x)))
#define sqr(x) ((x)*(x))
struct node
{
node *lc,*rc;
int cnt;
node();
}_null,*null=&_null;
node::node(){lc=rc=null;cnt=0;}
int pf;
void erase(node *x)
{
if(x->lc!=null)erase(x->lc);
if(x->rc!=null)erase(x->rc);
delete x;
}
void modify(node *&x,int l,int r,int p,int v)
{
if(x==null)x=new node;
x->cnt+=v;
if(!x->cnt)
{
erase(x);
x=null;
return;
}
if(l!=r)
{
int f=(l+r)/2;
if(plc,l,f,p,v);
else
modify(x->rc,f+1,r,p,v);
}
}
struct group
{
node *x[50];
int mul[50],sz;
inline int cnt()
{
int ret=0;
for(int i=0;icnt*mul[i];
return ret;
}
inline group* lc(group *f)
{
f->sz=sz;
for(int i=0;ix[i]=x[i]->lc,f->mul[i]=mul[i];
return f;
}
inline group* rc(group *f)
{
f->sz=sz;
for(int i=0;ix[i]=x[i]->rc,f->mul[i]=mul[i];
return f;
}
};
int cnt(group *x,int l,int r,int ql,int qr)
{
if(l==ql&&r==qr)return x->cnt();
int t=(l+r)/2,ans=0;
group ch;
if(qllc(&ch),l,t,ql,min(t,qr));
if(qr>t)ans+=cnt(x->rc(&ch),t+1,r,max(ql,t+1),qr);
return ans;
}
int kth(group *x,int l,int r,int rk)
{
if(l==r)return l;
group ch;
x->lc(&ch);
int lcnt;
if((lcnt=ch.cnt())>=rk)
return kth(&ch,l,(l+r)/2,rk);
else
return kth(x->rc(&ch),(l+r)/2+1,r,rk-lcnt);
}
int gmax(group *x,int l,int r,int p)
{
if(!x->cnt())return 0;
if(l==r)return l;
int t=(l+r)/2;
group ch;
if(plc(&ch),l,t,p);
if(r==p)
{
x->rc(&ch);
if(ch.cnt())return gmax(&ch,t+1,r,p);
return gmax(x->lc(&ch),l,t,t);
}
return max(gmax(x->lc(&ch),l,t,t),gmax(x->rc(&ch),t+1,r,p));
}
int gmin(group *x,int l,int r,int p)
{
if(!x->cnt())return 0x7fffffff;
if(l==r)return l;
int t=(l+r)/2;
group ch;
if(p>t)return gmin(x->rc(&ch),t+1,r,p);
if(l==p)
{
x->lc(&ch);
if(ch.cnt())return gmin(&ch,l,t,p);
return gmin(x->rc(&ch),t+1,r,t+1);
}
return min(gmin(x->lc(&ch),l,t,p),gmin(x->rc(&ch),t+1,r,t+1));
}
#define nl 0
#define nr 100000001
node *root[50001];
int v[50001];
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=0;i
-
BZOJ 4668: 冷战 LCT
/archives/72/
2016-11-09T16:51:00+08:00
###Description
1946 年 3 月 5 日,英国前首相温斯顿·丘吉尔在美国富尔顿发表“铁幕演说”,正式拉开了冷战序幕。
美国和苏联同为世界上的“超级大国”,为了争夺世界霸权,两国及其盟国展开了数十年的斗争。在这段时期,虽然分歧和冲突严重,但双方都尽力避免世界范围的大规模战争(第三次世界大战)爆发,其对抗通常通过局部代理战争、科技和军备竞赛、太空竞争、外交竞争等“冷”方式进行,即“相互遏制,不动武力”,因此称之为“冷战”。
Reddington 是美国的海军上将。由于战争局势十分紧张,因此他需要时刻关注着苏联的各个活动,避免使自己的国家陷入困境。苏联在全球拥有 N 个军工厂,但由于规划不当,一开始这些军工厂之间是不存在铁路的,为了使武器制造更快,苏联决定修建若干条道路使得某些军工厂联通。
Reddington 得到了苏联的修建日程表,并且他需要时刻关注着某两个军工厂是否联通,以及最早在修建哪条道路时会联通。具体而言,现在总共有M 个操作,操作分为两类:
• 0 u v,这次操作苏联会修建一条连接 u 号军工厂及 v 号军工厂的铁路,注意铁路都是双向的;
• 1 u v, Reddington 需要知道 u 号军工厂及 v 号军工厂最早在加入第几条条铁路后会联通,假如到这次操作都没有联通,则输出 0;
作为美国最强科学家, Reddington 需要你帮忙设计一个程序,能满足
他的要求。
###Input
第一行两个整数 N, M。
接下来 M 行,每行为 0 u v 或 1 u v 的形式。
数据是经过加密的,对于每次加边或询问,真正的 u, v 都等于读入的
u, v 异或上上一次询问的答案。一开始这个值为 0。
1 ≤ N, M ≤ 500000,解密后的 u, v 满足1 ≤ u, v ≤ N, u不等于v
###Output
对于每次 1 操作,输出 u, v 最早在加入哪条边后会联通,若到这个操
作时还没联通,则输出 0。
###Sample Input
5 9
0 1 4
1 2 5
0 2 4
0 3 4
1 3 1
0 7 0
0 6 1
0 1 6
1 2 6
###Sample Output
```
0
3
5
```
###Solution
直接用LCT维护动态最小生成树就行了,虽然很暴力,但能AC
###Code
```c++
#include
typedef unsigned char uchar;
typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
#define xx first
#define yy second
template inline T max(T a,T b){return a>b?a:b;}
template inline T min(T a,T b){return a0?a:-a;}
template inline void repr(T &a,T b){if(ab)a=b;}
template inline T gcd(T a,T b){if(b)return gcd(b,a%b);return a;}
#define mp(a,b) std::make_pair(a,b)
#define pb push_back
#define lb(x) ((x)&(-(x)))
#define pm(a,b,c,d) a=(a+(ll)(b)*(c))%(d)
const int N=1000001;
struct node
{
node *c[2],*fa;
int val,ma;
bool rev;
inline void rotate(bool f);
inline void pushdown();
inline void pushup();
}s[N],_null,*null=&_null;
inline void node::pushdown()
{
if(fa->c[0]==this||fa->c[1]==this)
fa->pushdown();
if(rev)
{
rev=0;
std::swap(c[0],c[1]);
if(c[0]!=null)c[0]->rev=!c[0]->rev;
if(c[1]!=null)c[1]->rev=!c[1]->rev;
}
}
inline void node::rotate(bool f)
{
fa->c[f]=c[!f];
c[!f]=fa;
fa=c[!f]->fa;
if(c[!f]->fa->c[1]==c[!f])
c[!f]->fa->c[1]=this;
else if(c[!f]->fa->c[0]==c[!f])
c[!f]->fa->c[0]=this;
c[!f]->fa=this;
c[!f]->c[f]->fa=c[!f];
c[!f]->pushup();
}
inline void node::pushup()
{
ma=val;
if(c[0]!=null)repr(ma,c[0]->ma);
if(c[1]!=null)repr(ma,c[1]->ma);
}
inline void init(node *x,int v)
{
x->val=v,x->c[0]=null,x->c[1]=null,x->fa=null;
}
inline void splay(node *a)
{
a->pushdown();
while(a->fa->c[0]==a||a->fa->c[1]==a)
a->rotate(a==a->fa->c[1]);
a->pushup();
}
inline void access(node *a)
{
node *x=null;
while(a!=null)
{
splay(a);
a->c[1]=x;
a->pushup();
x=a,a=a->fa;
}
}
inline void movetoroot(node *x)
{
access(x);
splay(x);
x->rev=!x->rev;
}
inline void link(node *x,node *y)
{
movetoroot(x);
x->fa=y;
}
inline void cut(node *x,node *y)
{
movetoroot(x);
access(y);
splay(y);
x->fa=null;
y->c[0]=null;
y->pushup();
}
inline void split(node *x,node *y)
{
movetoroot(x),access(y),splay(y);
}
inline node* findroot(node *x)
{
while(x->fa!=null)
x=x->fa;
return x;
}
int main()
{
int n,m,lstans=0,opt,x,y;
scanf("%d%d",&n,&m);
for(int i=1;i
-
BZOJ 3038: 上帝造题的七分钟2
/archives/70/
2016-11-09T12:55:00+08:00
###Description
XLk觉得《上帝造题的七分钟》不太过瘾,于是有了第二部。
"第一分钟,X说,要有数列,于是便给定了一个正整数数列。
第二分钟,L说,要能修改,于是便有了对一段数中每个数都开平方(下取整)的操作。
第三分钟,k说,要能查询,于是便有了求一段数的和的操作。
第四分钟,彩虹喵说,要是noip难度,于是便有了数据范围。
第五分钟,诗人说,要有韵律,于是便有了时间限制和内存限制。
第六分钟,和雪说,要省点事,于是便有了保证运算过程中及最终结果均不超过64位有符号整数类型的表示范围的限制。
第七分钟,这道题终于造完了,然而,造题的神牛们再也不想写这道题的程序了。"
——《上帝造题的七分钟·第二部》
所以这个神圣的任务就交给你了。
###Input
第一行一个整数n,代表数列中数的个数。
第二行n个正整数,表示初始状态下数列中的数。
第三行一个整数m,表示有m次操作。
接下来m行每行三个整数k,l,r,k=0表示给[l,r]中的每个数开平方(下取整),k=1表示询问[l,r]中各个数的和。
###Output
对于询问操作,每行输出一个回答。
###Sample Input
10
1 2 3 4 5 6 7 8 9 10
5
0 1 10
1 1 10
1 1 5
0 5 8
1 4 8
###Sample Output
19
7
6
###Solution
显然,一个数最多开根6次就会变成1
那么直接用线段树维护区间最大值,每次暴力修改即可
###Code
```c++
#include
typedef unsigned char uchar;
typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
#define xx first
#define yy second
template inline T max(T a,T b){return a>b?a:b;}
template inline T min(T a,T b){return a0?a:-a;}
template inline void repr(T &a,T b){if(ab)a=b;}
#define mp(a,b) std::make_pair(a,b)
#define pb push_back
int n,t=1;
ll s[270000],ma[270000];
inline void up(int x)
{
s[x]=s[x