20220926(中!)
t1 扩散
思路
这道题思路很多,这里只浅谈一下最小生成树的做法。显然,每个点每次扩散时,都会使得它到其他点的曼哈顿距离减少1。那么我们要求所有点形成一个连通块的最早时刻自然就是求最小生成树。因为最小生成树上的边,就是这条边所连接的两个点形成连通块的最早时刻。当然,这里要根据曼哈顿距离的奇偶稍微处理一下。
点击查看代码
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#define ll long long
#define fo(i,x,y) for(int i=x;i<=y;++i)
using namespace std;
template<typename T>inline void in(T &x){
x=0;int f=0;char c=getchar();
for(;!isdigit(c);c=getchar())f|=(c=='-');
for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+(c^48);
x=f?-x:x;
}
template<typename T>inline void out(T x){
if(x<0)x=~x+1,putchar('-');
if(x>9)out(x/10);
putchar(x%10^48);
}
const int M=5005,N=55;
int n;
struct point{
int x,y;
}p[N];
struct edge{
int u,v,nex;
int w;
}e[M];
int head[N],tot;
int fa[N];
inline int mlen(int i,int j){return abs(p[i].x-p[j].x)+abs(p[i].y-p[j].y);}
inline void add(int u,int v,ll w){e[++tot].v=v,e[tot].u=u,e[tot].w=w,e[tot].nex=head[u],head[u]=tot;}
inline bool cmp(edge a,edge b){return a.w<b.w;}
inline int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
int ans;
int main(){
in(n);
fo(i,1,n){
fa[i]=i;
in(p[i].x),in(p[i].y);
fo(j,1,i-1){
int d=mlen(i,j);
d&1?d=(d>>1)+1:d=(d>>1);
add(i,j,d);
}
}
sort(e+1,e+tot+1,cmp);
int g=1;
fo(i,1,tot){
int u=find(e[i].u),v=find(e[i].v);
if(u==v)continue;
fa[u]=v;
ans=max(ans,e[i].w);
++g;
if(g==n)break;
}
out(ans);
return 0;
}
t2 [ZJOI2008]树的统计
思路
裸的一个树链剖分。
点击查看代码
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#define ll long long
#define fo(i,x,y) for(int i=x;i<=y;++i)
using namespace std;
template<typename T>inline void in(T &x){
x=0;int f=0;char c=getchar();
for(;!isdigit(c);c=getchar())f|=(c=='-');
for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+(c^48);
x=f?-x:x;
}
template<typename T>inline void out(T x){
if(x<0)x=~x+1,putchar('-');
if(x>9)out(x/10);
putchar(x%10^48);
}
const int N=30005;
int n,q;
struct edge{
int v,nex;
}e[N<<1];
int head[N],tot,val[N],xx,yy;
inline void add(int u,int v){e[++tot].v=v,e[tot].nex=head[u],head[u]=tot;}
int size[N],dep[N],maxn[N],fu[N];
inline void dfs(int x,int fa){
size[x]=1;
for(int i=head[x];i;i=e[i].nex){
int v=e[i].v;
if(v==fa)continue;
dep[v]=dep[x]+1;
fu[v]=x;
dfs(v,x);
size[x]+=size[v];
if(size[v]>size[maxn[x]])maxn[x]=v;
}
}
int dfn[N],cnt,top[N],pre[N];
inline void dfs1(int x,int tp){
dfn[x]=++cnt,pre[cnt]=x,top[x]=tp;
if(maxn[x])dfs1(maxn[x],tp);
for(int i=head[x];i;i=e[i].nex){
int v=e[i].v;
if(v==fu[x]||v==maxn[x])continue;
dfs1(v,v);
}
}
struct node{
int l,r,lch,rch,val,mm;
}rt[N<<1];
int tote;
inline int max(int a,int b){return a>b?a:b;}
inline int build(int l,int r){
++tote;
int x=tote;
rt[x].l=l;
rt[x].r=r;
if(l==r){
rt[x].val=val[pre[l]];
rt[x].mm=rt[x].val;
return x;
}
int mid=(l+r)>>1;
rt[x].lch=build(l,mid);
rt[x].rch=build(mid+1,r);
rt[x].val=rt[rt[x].lch].val+rt[rt[x].rch].val;
rt[x].mm=max(rt[rt[x].lch].mm,rt[rt[x].rch].mm);
return x;
}
inline void modify(int x,int pos,int v){
if(rt[x].l==rt[x].r&&rt[x].l==pos){
rt[x].val=v;
rt[x].mm=v;
return;
}
int mid=(rt[x].l+rt[x].r)>>1;
if(pos<=mid)modify(rt[x].lch,pos,v);
else modify(rt[x].rch,pos,v);
rt[x].val=rt[rt[x].lch].val+rt[rt[x].rch].val;
rt[x].mm=max(rt[rt[x].lch].mm,rt[rt[x].rch].mm);
return;
}
inline int querys(int x,int l,int r){
if(l<=rt[x].l&&rt[x].r<=r){
return rt[x].val;
}
int tmp=0;
int mid=(rt[x].l+rt[x].r)>>1;
if(l<=mid)tmp+=querys(rt[x].lch,l,r);
if(r>mid)tmp+=querys(rt[x].rch,l,r);
return tmp;
}
inline int querym(int x,int l,int r){
if(l<=rt[x].l&&rt[x].r<=r){
return rt[x].mm;
}
int tmp=-1e9;
int mid=(rt[x].l+rt[x].r)>>1;
if(l<=mid)tmp=max(tmp,querym(rt[x].lch,l,r));
if(r>mid)tmp=max(tmp,querym(rt[x].rch,l,r));
return tmp;
}
inline int qsum(int x,int y){
int sum=0;
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]])swap(x,y);
sum+=querys(1,dfn[top[x]],dfn[x]);
x=fu[top[x]];
}
if(dep[x]<dep[y])swap(x,y);
sum+=querys(1,dfn[y],dfn[x]);
return sum;
}
inline int qmax(int x,int y){
int ma=-1e9;
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]])swap(x,y);
ma=max(ma,querym(1,dfn[top[x]],dfn[x]));
x=fu[top[x]];
}
if(dep[x]<dep[y])swap(x,y);
ma=max(ma,querym(1,dfn[y],dfn[x]));
return ma;
}
int main(){
in(n);
int a,b;
fo(i,1,n-1){
in(a),in(b);
add(a,b);
add(b,a);
}
fo(i,1,n)in(val[i]);
fu[1]=1;
dfs(1,1);
dfs1(1,1);
build(1,n);
in(q);
while(q--){
char c[10];
scanf("%s",c);
in(xx);
in(yy);
if(c[0]=='C'){
modify(1,dfn[xx],yy);
}
else if(c[1]=='M')out(qmax(xx,yy)),putchar('\n');
else out(qsum(xx,yy)),putchar('\n');
}
return 0;
}
t3 佳佳的 Fibonacci
思路
一看,就知道大概就是个矩阵了。但是这道题不能直接把\(F[i]\)前的系数代入矩阵,因为矩阵乘法之所以快,就是因为其能多次乘上相同的矩阵。可以再开一个矩阵维护\(F[i]\)的系数,但其实也可以推公式。Fibonacci的前八项分别是:\(1,1,2,3,5,8,13,21\),而其的前六项和是:\(1,2,4,7,12,20\)。不难发现Fibonacci的前\(n\)项和\(S_{n}=F_{n+2}-1\),那么:
\[T_{n}=\sum_{i=1}^{n}F[i]\times i=n\times S_{n}-\sum_{i=0}^{n-1}\sum_{j=1}^{i}F[j]=n\times S_{n}-\sum_{i=1}^{n-1}S_{i}=n\times S_{n}-S_{n+1}+n+1=n\times F_{n+3}-F_{n+2}+2 \]接下来,再进行矩阵快速幂即可。
点击查看代码
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#define ll long long
#define int long long
#define fo(i,x,y) for(int i=x;i<=y;++i)
using namespace std;
template<typename T>inline void in(T &x){
x=0;int f=0;char c=getchar();
for(;!isdigit(c);c=getchar())f|=(c=='-');
for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+(c^48);
x=f?-x:x;
}
template<typename T>inline void out(T x){
if(x<0)x=~x+1,putchar('-');
if(x>9)out(x/10);
putchar(x%10^48);
}
int n,m;
struct matrix{
ll val[2][2];
matrix (){
memset(val,0,sizeof(val));
}
}base,res;
matrix mtimes(matrix a,matrix b){
matrix ans;
fo(i,0,1)
fo(j,0,1)
fo(k,0,1)
ans.val[i][j]=(ans.val[i][j]+a.val[i][k]*b.val[k][j])%m;
return ans;
}
inline matrix mpow(matrix a,int ci){
matrix ans;
ans.val[0][0]=ans.val[1][1]=1;
for(;ci;ci>>=1,a=mtimes(a,a))
if(ci&1)ans=mtimes(ans,a);
return ans;
}
matrix init;
ll nb;
signed main(){
in(n),in(m);
base.val[0][1]=1;
base.val[1][0]=1;
base.val[1][1]=1;
res=mpow(base,n+1);
init.val[0][0]=1;
init.val[0][1]=1;
init=mtimes(init,res);
if(n==1){
out(1%m);
return 0;
}
nb=((init.val[0][0]*n-init.val[0][1]+2)+m)%m;
out(nb);
return 0;
}
t4 [CQOI2009] 中位数
显然,如果一个数要成为中位数,就需要所选序列中大于和小于它的数的个数相等。那我们就只需要维护其相对大小的个数。那我们先向右扫一遍,大于\(b\)就\(++\),小于就\(--\),然后再向左扫一遍,统计答案。
点击查看代码
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<map>
#define ll long long
#define fo(i,x,y) for(int i=x;i<=y;++i)
using namespace std;
template<typename T>inline void in(T &x){
x=0;int f=0;char c=getchar();
for(;!isdigit(c);c=getchar())f|=(c=='-');
for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+(c^48);
x=f?-x:x;
}
template<typename T>inline void out(T x){
if(x<0)x=~x+1,putchar('-');
if(x>9)out(x/10);
putchar(x%10^48);
}
const int N=100005;
int n,b;
int a[N],pos,suml,sumr;
map<int,int>mp;
ll ans;
int main(){
in(n),in(b);
fo(i,1,n){
in(a[i]);
if(a[i]==b)pos=i;
}
fo(i,pos,n){
if(a[i]>b)++sumr;
else if(a[i]<b)--sumr;
++mp[sumr];
}
for(int i=pos;i>=1;--i){
if(a[i]>b)++suml;
else if(a[i]<b)--suml;
ans+=mp[0-suml];
}
if(b>n){
puts("0");
return 0;
}
out(ans);
return 0;
}
t5 [HAOI2015]树上操作
显而易见的树链剖分,稍微需要想想的就只有操作2了。而一棵子树的dfs序是大于根的连续的序。这样,只需要对根的dfs序到它的dfs序加上其子树大小进行区间修改即可。
点击查看代码
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#define ll long long
#define int long long
#define fo(i,x,y) for(int i=x;i<=y;++i)
using namespace std;
template<typename T>inline void in(T &x){
x=0;int f=0;char c=getchar();
for(;!isdigit(c);c=getchar())f|=(c=='-');
for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+(c^48);
x=f?-x:x;
}
template<typename T>inline void out(T x){
if(x<0)x=~x+1,putchar('-');
if(x>9)out(x/10);
putchar(x%10^48);
}
const int N=100005;
int n,m;
struct edge{
int v,nex;
}e[N<<1];
int head[N],tot,val[N],xx,yy;
inline void add(int u,int v){e[++tot].v=v,e[tot].nex=head[u],head[u]=tot;}
int size[N],dep[N],maxn[N],fu[N];
inline void dfs(int x,int fa){
size[x]=1;
for(int i=head[x];i;i=e[i].nex){
int v=e[i].v;
if(v==fa)continue;
dep[v]=dep[x]+1;
fu[v]=x;
dfs(v,x);
size[x]+=size[v];
if(size[v]>size[maxn[x]])maxn[x]=v;
}
}
int dfn[N],cnt,top[N],pre[N];
inline void dfs1(int x,int tp){
dfn[x]=++cnt,pre[cnt]=x,top[x]=tp;
if(maxn[x])dfs1(maxn[x],tp);
for(int i=head[x];i;i=e[i].nex){
int v=e[i].v;
if(v==fu[x]||v==maxn[x])continue;
dfs1(v,v);
}
}
struct node{
int l,r,lch,rch,val;
int tag;
}rt[N<<1];
int tote;
inline int max(int a,int b){return a>b?a:b;}
inline int build(int l,int r){
++tote;
int x=tote;
rt[x].l=l;
rt[x].r=r;
if(l==r){
rt[x].val=val[pre[l]];
return x;
}
int mid=(l+r)>>1;
rt[x].lch=build(l,mid);
rt[x].rch=build(mid+1,r);
rt[x].val=rt[rt[x].lch].val+rt[rt[x].rch].val;
return x;
}
inline int calc(int x){
return rt[x].val+rt[x].tag*(rt[x].r-rt[x].l+1);
}
inline void push_down(int x){
rt[x].val=rt[x].val+rt[x].tag*(rt[x].r-rt[x].l+1);
rt[rt[x].lch].tag+=rt[x].tag;
rt[rt[x].rch].tag+=rt[x].tag;
rt[x].tag=0;
}
inline void modify(int x,int l,int r,int v){
if(l<=rt[x].l&&rt[x].r<=r){
rt[x].tag+=v;
return;
}
int mid=(rt[x].l+rt[x].r)>>1;
push_down(x);
if(l<=mid)modify(rt[x].lch,l,r,v);
if(r>mid)modify(rt[x].rch,l,r,v);
rt[x].val=calc(rt[x].lch)+calc(rt[x].rch);
return;
}
inline int query(int x,int l,int r){
if(l<=rt[x].l&&rt[x].r<=r){
return calc(x);
}
int tmp=0;
int mid=(rt[x].l+rt[x].r)>>1;
push_down(x);
if(l<=mid)tmp+=query(rt[x].lch,l,r);
if(r>mid)tmp+=query(rt[x].rch,l,r);
return tmp;
}
inline int qsum(int x,int y){
int sum=0;
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]])swap(x,y);
sum+=query(1,dfn[top[x]],dfn[x]);
x=fu[top[x]];
}
if(dep[x]<dep[y])swap(x,y);
sum+=query(1,dfn[y],dfn[x]);
return sum;
}
inline void modifyy(int x,int l,int r,int v){
if(l<=rt[x].l&&rt[x].r<=r){
rt[x].tag+=v;
return;
}
int mid=(rt[x].l+rt[x].r)>>1;
push_down(x);
if(l<=mid)modifyy(rt[x].lch,l,r,v);
if(r>mid)modifyy(rt[x].rch,l,r,v);
rt[x].val=calc(rt[x].lch)+calc(rt[x].rch);
}
int opt;
signed main(){
in(n),in(m);
int a,b;
fo(i,1,n)in(val[i]);
fo(i,1,n-1){
in(a),in(b);
add(a,b);
add(b,a);
}
fu[1]=1;
dfs(1,1);
dfs1(1,1);
build(1,n);
while(m--){
in(opt);
in(xx);
if(opt==1)in(yy),modify(1,dfn[xx],dfn[xx],yy);
else if(opt==2)in(yy),modifyy(1,dfn[xx],dfn[xx]+size[xx]-1,yy);
else out(qsum(1,xx)),putchar('\n');
}
return 0;
}