Test 2022.09.26

今天是水题专场

T1 扩散

感觉这种要么就是二分答案网络流,要么就是最小生成树,(随便口胡的),树德保留节目了属于是。

分析

简简单单一眼最小生成树(又是),边权就是两个点之间存在公共区域的时间,这个距离随便找一找规律就好算了。这里直接给结论了\(w(x1,y1,x2,y2)=\) \(\frac{abs(x1-x2)+abs(y1-y2)}{2}\)然后建所有可能的边,最后再最小生成树就行,(听说二分也能做?

Code

点击查看代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#define int long long
using namespace std;
const int maxn=100;
inline int read()
{
	int x=0,f=1;
	char c=getchar();
	while(c<'0'||c>'9')
	{
		if(c=='-')f=-1;
		c=getchar();
	}
	while('0'<=c&&c<='9')
	{
		x=(x<<1)+(x<<3)+(c^48);
		c=getchar();
	}
	return x*f;
}
int n;
int abs1(int x){return x>0?x:-x;}
int calc(int x1,int y1,int x2,int y2){return ceil((1.0*abs1(x1-x2)+1.0*abs1(y1-y2))/2);}
int x[maxn],y[maxn];
struct Edge{int u,v,w;}E[5000];int tote=0;
bool cmp(Edge a,Edge b){return a.w<b.w;}
int f[100];int find(int x){return x==f[x]?x:f[x]=find(f[x]);}
signed main()
{
	n=read();
	for(int i=1;i<=n;i++)x[i]=read(),y[i]=read(),f[i]=i;;
	for(int i=1;i<=n;i++)
		for(int j=i+1;j<=n;j++)
			E[++tote].u=i,E[tote].v=j,E[tote].w=calc(x[i],y[i],x[j],y[j]);
	sort(E+1,E+tote+1,cmp);
	int cnt=0;int ans=-1;
	for(int i=1;i<=tote;i++)
	{
		if(cnt==n-1)break;
		int x=find(E[i].u),y=find(E[i].v);
		if(x==y)continue;
		f[x]=y;
		ans=max(ans,E[i].w);
	}
	printf("%lld",ans);
	return 0;
}

T2 树链剖分板子

没有什么好说的,就浅浅地总结一下

\(dfs1\):求出基本信息:深度、父亲、子树大小、重儿子编号
\(dfs2\):求出\(dfn\)序、对应节点的权值、当前节点的重链顶端节点
\(query\):求出从\(l\)\(r\)的路径上面的信息
实现方法就是只要当前两个节点的顶端节点不同,就让顶端节点深度更深的点跳到它所在重链的顶端,并且统计这一条链的答案,直到\(l、r\)在一条重链上面。最后再把深度深的点跳到浅的点上,再统计这一小节的答案。
画个图就很轻易地知道这时候每个子树的\(dfn\)序是一定连续的,所以就可以用线段树来维护区间最值

Code

点击查看代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#define int long long 
#define MAXN 200000
using namespace std;
inline int read()
{
    long long s=0,w=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
	{
  		if(ch=='-')w=-1;
		ch=getchar();
	}
    while(ch>='0'&&ch<='9')
    {
        s=(s<<3)+(s<<1)+(ch^48);
        ch=getchar();
    }
    return s*w;
} 
template <typename T>void wr(T x)
 {
	if(x<0) putchar('-'),x=-x;
	if(x>9) wr(x/10);
	putchar(x%10^'0');
	return;
}
int n,m;
long long val[MAXN];
struct Edge{int u,v;int nex;}tr[4*MAXN];
int head[MAXN],tote;
void add(int u,int v)
{
    tr[++tote].u=u;
    tr[tote].v=v;
    tr[tote].nex=head[u];
    head[u]=tote;
    tr[++tote].u=v;
    tr[tote].v=u;
    tr[tote].nex=head[v];
    head[v]=tote;
}
int fa[MAXN],siz[MAXN],dep[MAXN],son[MAXN];
void dfs1(int x,int f)
{
    dep[x]=dep[f]+1;siz[x]=1;fa[x]=f;
    int maxsonsize=-1;
    for(int i=head[x];i;i=tr[i].nex)
	{
        int v=tr[i].v;
        if(v==f)continue;
        dfs1(v,x);
        siz[x]+=siz[v];
        if(siz[v]>maxsonsize)maxsonsize=siz[v],son[x]=v;
    }
}
int id[MAXN],num;long long w[MAXN];int top[MAXN];
void dfs2(int x,int TOP)
{
    top[x]=TOP;id[x]=++num;w[id[x]]=val[x];
    if(!son[x])return;
    dfs2(son[x],TOP);
    for(int i=head[x];i;i=tr[i].nex)
	{
        int v=tr[i].v;
        if(v==fa[x]||v==son[x])continue;
        dfs2(v,v);
    }
}
struct Node{int l,r;long long val;long long tag;long long Max;}a[4*MAXN];
long long calc(int x){return a[x].val+(a[x].r-a[x].l+1)*a[x].tag;}
void down(int x)
{
    a[x<<1].tag+=a[x].tag;
    a[(x<<1)|1].tag+=a[x].tag;
    a[x].val=calc(x);
    a[x].tag=0;
    a[x].Max=max(a[x<<1].Max,a[(x<<1)|1].Max); 
}
void build(int x,int l,int r)
{
    a[x].l=l;a[x].r=r;
    if(l==r){a[x].val=a[x].Max=w[l];return;}
    int mid=(l+r)>>1;
    build(x<<1,l,mid);
    build((x<<1)|1,mid+1,r);
    a[x].val=calc(x<<1)+calc((x<<1)|1);
    a[x].Max=max(a[x<<1].Max,a[(x<<1)|1].Max);
}
void update(int x,int l,int r,int k)
{
    if(a[x].l>=l&&a[x].r<=r){a[x].tag+=k,a[x].Max=k;return;}
    down(x);
    int mid=(a[x].l+a[x].r)>>1;
    if(mid>=l)update(x<<1,l,r,k);
    if(mid<r)update((x<<1)|1,l,r,k);
    a[x].val=calc(x<<1)+calc((x<<1)+1);
    a[x].Max=max(a[x<<1].Max,a[(x<<1)|1].Max);
}
void change(int x,int pos,int v)
{
	if(a[x].l==a[x].r){a[x].val=v,a[x].Max=v;return;}
	int mid=(a[x].l+a[x].r)>>1;
	if(pos<=mid)change(x<<1,pos,v);
	else change(x<<1|1,pos,v);
	a[x].val=calc(x<<1)+calc(x<<1|1);
	a[x].Max=max(a[x<<1].Max,a[x<<1|1].Max);
}
long long query(int x,int l,int r)
{
    if(a[x].l>=l&&a[x].r<=r){return calc(x);}
    down(x);
    int mid=(a[x].l+a[x].r)>>1;
    long long ret=0;
    if(mid>=l)ret+=query(x<<1,l,r);
    if(mid<r)ret+=query((x<<1)|1,l,r);
    a[x].val=calc(x<<1)+calc((x<<1)|1);
    a[x].Max=max(a[x<<1].Max,a[(x<<1)|1].Max);
    return ret;
}
int querymax(int x,int l,int r)
{
	if(a[x].l>=l&&a[x].r<=r){return a[x].Max;}
	down(x);
    int mid=(a[x].l+a[x].r)>>1;int ans=-2147483640;
    if(mid>=l)ans=max(querymax(x<<1,l,r),ans);
    if(mid<r)ans=max(querymax((x<<1)|1,l,r),ans);
    return ans;
}
long long query_rootsum(int l,int r)
{
    long long ret=0;
    while(top[l]!=top[r])
	{
        if(dep[top[l]]<dep[top[r]])swap(l,r);
        ret+=query(1,id[top[l]],id[l]);
        l=fa[top[l]];
    }
    if(dep[l]>dep[r])swap(l,r);
    ret+=query(1,id[l],id[r]);
    return ret;
}
long long query_max(int l,int r)
{
	long long ans=-2147483640;
	while(top[l]!=top[r])
	{
		if(dep[top[l]]<dep[top[r]])swap(l,r);
		ans=max(querymax(1,id[top[l]],id[l]),ans);
		l=fa[top[l]];
	}
	if(dep[l]>dep[r])swap(l,r);
	ans=max(querymax(1,id[l],id[r]),ans);
	return ans;
}
string s;
signed main(){
    n=read();

    for(int i=1;i<n;i++)
	{
        int u,v;
        u=read(),v=read();
        add(u,v);
    }
    for(int i=1;i<=n;++i) val[i]=read();
    dfs1(1,0);
    dfs2(1,1);
    build(1,1,n);
    int q=read();
    for(int i=1;i<=q;i++)
	{
		cin>>s;
        if(s[0]=='C')
		{
            int x;
            long long y;
            x=read(),y=read();
            change(1,id[x],y);
        }
        else if(s=="QMAX")
        {
        	int x;long long y;
			x=read(),y=read();
			wr(query_max(x,y));        
        	printf("\n");
		}
        else if(s=="QSUM")
		{
            int x,y;
			x=read(),y=read();
			wr(query_rootsum(y,x));
            printf("\n");
        }
    }
    return 0;
}

T3佳佳的数学作业

一眼矩阵,可是一开始遇到了一些困难,比如转移矩阵里面必须是常值,但是后面经过一系列推导和转化,还是把感觉很妙的一个解给写出来了,马上就要去跑步了,推导的过程明早重点来写,先贴代码吧

\(Upd2022.09.27\):增加了昨天说的心路历程内容

\(Part1\)

一开始肯定想到的是直接递推对吧,有递推公式\(T_n=T_{n-1}+n\times F_n\),写出来的矩阵暂且认为是

\[转移:\begin{bmatrix} 1&n&0\\ 0&1&1\\ 0&1&0\\ \end{bmatrix} 初始:\begin{bmatrix} T_{n-1}\\ F_n\\ F_{n-1} \end{bmatrix} 目标: \begin{bmatrix} T_n\\ F_{n+1}\\ F_{n} \end{bmatrix} \]

你会发现一定会有一个变量\(n\)使得矩阵每次都不同,无法使用快速幂,所以要换思路

\(Part2\)

考虑到\(T_n=F_1+2F_2+3F_3+...+nF_n\)\(F_i\)的系数是阶梯状的,我们可以考虑每次加上\(S_n\),再减去前面相应的部分,最后就可以经过\(n\)次递推得到\(T_n\),我们定义一个\(K_n=T_n\),\(K_1=F_1+...+F_n、K_2=F_1+2F_2+2F_3+...+2F_n,F_3=F_1+2F_2+3F_3+3F_4+...+3F_n\),可以看出来我们每一次递推都可以固定一位的系数,且容易得到递推公式:\(K_i=K_{i-1}+S_n-S_{i-1}\).
以下就省略一些了,最后的转移矩阵推出来就是:

\[\begin{bmatrix} 1&1&-1&0&0\\ 0&1&0&0&0\\ 0&0&1&1&0\\ 0&0&0&1&1\\ 0&0&0&1&0\\ \end{bmatrix} \]

这样就全部都是常数了

Code

点击查看代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define int long long
using namespace std;
const int maxn=1e5;
struct Matrix{int n,m,a[10][10];Matrix(){memset(a,0,sizeof a);};}sup[5];
int N,M;
Matrix operator *(Matrix a,Matrix b)
{
	Matrix tmp;tmp.n=a.n,tmp.m=b.m;
	for(int i=1;i<=a.n;i++)
		for(int j=1;j<=b.m;j++)
		{
			int c=0;
			for(int k=1;k<=a.m;k++)	c=(c%M+(a.a[i][k]%M)*(b.a[k][j]%M))%M;
			tmp.a[i][j]=c;
		}
	return tmp;
}
Matrix base(int x)
{
	Matrix tmp;tmp.n=tmp.m=x;
	for(int i=1;i<=x;i++)tmp.a[i][i]=1;
	return tmp;
}
Matrix Mpower(Matrix a,int b)
{
	Matrix ans=base(a.n);
	while(b)
	{
		if(b&1)ans=ans*a;
		a=a*a;
		b>>=1;
	}
	return ans;
}
void pre()
{
	sup[1].m=3,sup[1].n=3;
	sup[1].a[1][1]=1,sup[1].a[1][2]=1;
	sup[1].a[2][2]=1,sup[1].a[2][3]=1;
	sup[1].a[3][2]=1;
	sup[2].m=sup[2].n=5;
	sup[2].a[1][1]=1,sup[2].a[1][2]=1,sup[2].a[1][3]=-1;
	sup[2].a[2][2]=1;
	sup[2].a[3][3]=1;sup[2].a[3][4]=1;
	sup[2].a[4][4]=1,sup[2].a[4][5]=1;
	sup[2].a[5][4]=1;
}
void print(Matrix a)
{
	for(int i=1;i<=a.n;i++)
	{
		for(int j=1;j<=a.m;j++)
			printf("%lld ",a.a[i][j]);
		printf("\n");
	}
}
signed main()
{
	scanf("%lld%lld",&N,&M);
	pre();
	Matrix ans1;ans1.n=3,ans1.m=1;
	ans1.a[1][1]=0,ans1.a[2][1]=1,ans1.a[3][1]=0;
	ans1=Mpower(sup[1],N)*ans1;
	Matrix ans2;ans2.n=5,ans2.m=1;
	ans2.a[1][1]=0,ans2.a[2][1]=ans1.a[1][1],ans2.a[3][1]=0,ans2.a[4][1]=1,ans2.a[5][1]=0;
	ans2=Mpower(sup[2],N)*ans2;
	printf("%lld",(ans2.a[1][1]+M)%M);
	return 0;
}

T4中位数

说实话一开始真的没什么思路的,但是想到了对于当前这个位置统计它前后大于和小于它的是否相等,但实在没有想到用\(+1 -1\)的方式来表示,还可以用查分的方式来维护区间和

Code

点击查看代码
#include<iostream>
#include<cstdio>
#include<unordered_map>
#define ll long long
using namespace std;
int ans,wei,n,b,a[110000],tot,sum[110000];
unordered_map<int,int>mp;
int main(){
//	freopen("mid.in","r",stdin);
//	freopen("mid.out","w",stdout);
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin>>n>>b;
	for(register int i=1;i<=n;i++){
		cin>>a[i];
		if(a[i]==b)wei=i;
	}
	int big=0,small=0;
	for(register int i=wei;i>0;i--){
		if(a[i]>b)big++;
		else{
			if(a[i]<b)small++;
		}
		int delta=big-small;
		//if(delta==0)ans++;
		if(mp.find(delta)==mp.end()){
			mp[delta]=++tot;
		}
		sum[mp[delta]]++;
	}
	big=0;
	small=0;
	for(register int i=wei;i<=n;i++){
		if(a[i]>b)big++;
		else{
			if(a[i]<b)small++;
		}
		int delta=big-small;
		if(mp.find(-delta)==mp.end())continue;
		ans+=sum[mp[-delta]];
	}
	cout<<ans;
	return 0;
}
/*
7 4
5 7 2 4 3 1 6

9 5
9 8 1 2 5 4 3 6 7
*/

本来以为今天会很早就改完,没想到被树剖卡死了,害得多练啊

posted @ 2022-09-26 21:24  Hanggoash  阅读(7)  评论(0编辑  收藏  举报
动态线条
动态线条end