高一上九月下旬日记

9.21

闲话

做题纪要

[ABC372A] delete .

  • 基础字符串。

    点击查看代码
    int main()
    {
    	string s;
    	cin>>s;
    	for(int i=0;i<s.size();i++)
    	{
    		if(s[i]!='.')
    		{
    			cout<<s[i];
    		}
    	}
    	return 0;
    }
    

[ABC372B] 3^A

  • 转化成 \(3\) 进制后输出即可。

    点击查看代码
    int r[100010];
    int main()
    {
    	int m,n=0,i,j;
    	cin>>m;
    	while(m)
    	{
    		r[0]++;
    		r[r[0]]=m%3;
    		n+=m%3;
    		m/=3;	
    	}
    	cout<<n<<endl;
    	reverse(r+1,r+1+r[0]);
    	for(i=1;i<=r[0];i++)
    	{
    		for(j=1;j<=r[i];j++)
    		{
    			cout<<r[0]-i<<" ";
    		}
    	}
    	return 0;
    }
    

[ABC372C] Count ABC Again

  • 修改至多有 \(5\)ABC 会出现问题,处理一下即可。

    点击查看代码
    char s[200010];
    int main()
    {
    	int n,q,ans=0,x,i;
    	char c;
    	cin>>n>>q;
    	scanf("%s",s+1);
    	for(i=3;i<=n;i++)
    	{
    		ans+=(s[i-2]=='A'&&s[i-1]=='B'&&s[i]=='C');
    	}
    	for(i=1;i<=q;i++)
    	{
    		cin>>x>>c;
    		if(s[x]!=c)
    		{
    			if(x>=3)
    			{
    				ans-=(s[x-2]=='A'&&s[x-1]=='B'&&s[x]=='C');
    			}
    			if(x>=2)
    			{
    				ans-=(s[x-1]=='A'&&s[x]=='B'&&s[x+1]=='C');
    			}
    			if(x>=1)
    			{
    				ans-=(s[x]=='A'&&s[x+1]=='B'&&s[x+2]=='C');
    			}
    			s[x]=c;
    			if(x>=3)
    			{
    				ans+=(s[x-2]=='A'&&s[x-1]=='B'&&s[x]=='C');
    			}
    			if(x>=2)
    			{
    				ans+=(s[x-1]=='A'&&s[x]=='B'&&s[x+1]=='C');
    			}
    			if(x>=1)
    			{
    				ans+=(s[x]=='A'&&s[x+1]=='B'&&s[x+2]=='C');
    			}
    		}
    		cout<<ans<<endl;
    	}
    	return 0;
    }
    

[ABC372E] K-th Largest Connected Components

  • 强化版: luogu P3224 [HNOI2012] 永无乡

    点击查看代码
    struct SMT
    {
    	int root[200010],rt_sum=0;
    	struct SegmentTree
    	{
    		int ls,rs,sum,id;
    	}tree[200010<<5];
    	#define lson(rt) (tree[rt].ls)
    	#define rson(rt) (tree[rt].rs)
    	void pushup(int rt)
    	{
    		tree[rt].sum=tree[lson(rt)].sum+tree[rson(rt)].sum;
    	}
    	int build()
    	{
    		rt_sum++;
    		lson(rt_sum)=rson(rt_sum)=tree[rt_sum].sum=tree[rt_sum].id=0;
    		return rt_sum;
    	}
    	void update(int rt,int l,int r,int pos,int val,int id)
    	{
    		if(l==r)
    		{
    			tree[rt].id=id;
    			tree[rt].sum+=val;
    			return;
    		}
    		int mid=(l+r)/2;
    		if(pos<=mid)
    		{
    			lson(rt)=(lson(rt)==0)?build():lson(rt);
    			update(lson(rt),l,mid,pos,val,id);
    		}
    		else
    		{
    			rson(rt)=(rson(rt)==0)?build():rson(rt);
    			update(rson(rt),mid+1,r,pos,val,id);
    		}
    		pushup(rt);
    	}
    	int merge(int rt1,int rt2,int l,int r)
    	{
    		if(rt1==0)
    		{
    			return rt2;
    		}
    		if(rt2==0)
    		{
    			return rt1;
    		}
    		if(l==r)
    		{
    			tree[rt1].id=(tree[rt1].id!=0)?tree[rt1].id:tree[rt2].id;
    			tree[rt1].sum+=tree[rt2].sum;
    			return rt1;
    		}
    		int mid=(l+r)/2;
    		lson(rt1)=merge(lson(rt1),lson(rt2),l,mid);
    		rson(rt1)=merge(rson(rt1),rson(rt2),mid+1,r);
    		pushup(rt1);
    		return rt1;
    	}
    	int query(int rt,int l,int r,int x,int y)
    	{
    		if(rt==0)
    		{
    			return 0;
    		}
    		if(x<=l&&r<=y)
    		{
    			return tree[rt].sum;
    		}
    		int mid=(l+r)/2,ans=0;
    		if(x<=mid)
    		{
    			ans+=query(lson(rt),l,mid,x,y);
    		}
    		if(y>mid)
    		{
    			ans+=query(rson(rt),mid+1,r,x,y);
    		}
    		return ans;
    	}
    	int kth_min(int rt,int l,int r,int k)
    	{
    		if(l==r)
    		{
    			return tree[rt].id;
    		}
    		int mid=(l+r)/2;
    		if(k<=tree[lson(rt)].sum)
    		{
    			return kth_min(lson(rt),l,mid,k);
    		}
    		else
    		{
    			return kth_min(rson(rt),mid+1,r,k-tree[lson(rt)].sum);
    		}
    	}
    }T;
    struct DSU
    {
    	int fa[200010];
    	void init(int n)
    	{
    		for(int i=1;i<=n;i++)
    		{
    			fa[i]=i;
    		}
    	}
    	int find(int x)
    	{
    		return (fa[x]==x)?x:fa[x]=find(fa[x]);
    	}
    	void merge(int x,int y,int n)
    	{
    		x=find(x);
    		y=find(y);
    		if(x!=y)
    		{
    			T.root[x]=T.merge(T.root[x],T.root[y],1,n);
    			fa[y]=x;
    		}
    	}
    }D;
    int main()
    {
    	int n,q,pd,u,v,i;
    	cin>>n;
    	for(i=1;i<=n;i++)
    	{
    		T.root[i]=T.build();
    		T.update(T.root[i],1,n,i,1,i);
    	}
    	D.init(n);
    	cin>>q;
    	for(i=1;i<=q;i++)
    	{
    		cin>>pd>>u>>v;
    		if(pd==2)
    		{
    			u=D.find(u);
    			cout<<((T.query(T.root[u],1,n,1,n)<v)?-1:T.kth_min(T.root[u],1,n,T.query(T.root[u],1,n,1,n)-v+1))<<endl;
    		}
    		else
    		{
    			D.merge(u,v,n);
    		}
    	}
    	return 0;
    }
    

[ABC372D] Buildings

  • 单调栈处理出每个数左侧第一个大于它的数,记为 \(l_{i}\) ,则会对 \([l_{i},i)\) 中的点产生 \(1\) 的贡献。

    点击查看代码
    int a[200010];
    stack<int>s;
    struct SMT
    {
    	struct SegmentTree
    	{
    		int l,r,sum,lazy;
    	}tree[800010];
    	int lson(int x)
    	{
    		return x*2;
    	}
    	int rson(int x)
    	{
    		return x*2+1;
    	}
    	void pushup(int rt)
    	{
    		tree[rt].sum=tree[lson(rt)].sum+tree[rson(rt)].sum;
    	}
    	void build(int rt,int l,int r)
    	{
    		tree[rt].l=l;
    		tree[rt].r=r;
    		if(l==r)
    		{
    			return;
    		}
    		int mid=(l+r)/2;
    		build(lson(rt),l,mid);
    		build(rson(rt),mid+1,r);
    	}
    	void pushdown(int rt)
    	{
    		if(tree[rt].lazy!=0)
    		{
    			tree[lson(rt)].lazy+=tree[rt].lazy;
    			tree[rson(rt)].lazy+=tree[rt].lazy;
    			tree[lson(rt)].sum+=tree[rt].lazy*(tree[lson(rt)].r-tree[lson(rt)].l+1);
    			tree[rson(rt)].sum+=tree[rt].lazy*(tree[rson(rt)].r-tree[rson(rt)].l+1);
    			tree[rt].lazy=0;
    		}
    	}
    	void update(int rt,int x,int y,int val)
    	{
    		if(x<=tree[rt].l&&tree[rt].r<=y)
    		{
    			tree[rt].lazy+=val;
    			tree[rt].sum+=val*(tree[rt].r-tree[rt].l+1);
    			return;
    		}
    		pushdown(rt);
    		int mid=(tree[rt].l+tree[rt].r)/2;
    		if(x<=mid)
    		{
    			update(lson(rt),x,y,val);
    		}
    		if(y>mid)
    		{
    			update(rson(rt),x,y,val);
    		}
    		pushup(rt);
    	}
    	int query(int rt,int pos)
    	{
    		if(tree[rt].l==tree[rt].r)
    		{
    			return tree[rt].sum;
    		}
    		pushdown(rt);
    		int mid=(tree[rt].l+tree[rt].r)/2;
    		if(pos<=mid)
    		{
    			return query(lson(rt),pos);
    		}
    		else
    		{
    			return query(rson(rt),pos);
    		}
    	}
    }T;
    int main()
    {
    	int n,i;
    	cin>>n;
    	T.build(1,1,n);
    	for(i=1;i<=n;i++)
    	{
    		cin>>a[i];
    		while(s.empty()==0&&a[s.top()]<a[i])
    		{
    			s.pop();
    		}
    		if(i-1>=1)
    		{
    			T.update(1,((s.empty()==0)?s.top():0),i-1,1);
    		}
    		s.push(i);
    	}
    	for(i=1;i<=n;i++)
    	{
    		cout<<T.query(1,i)<<" ";
    	}
    	return 0;
    }
    

9.22

闲话

  • 白天九科周测,年级部把政史地没安排在一起,导致我们班只能改公自,不能改奥赛联排,于是一天除晚三外有五节公自,遂颓《红楼梦》。
  • 晚三开班会,说了下明天的换课情况,明天下午第九(奥赛)、十(奥赛自习)节课跳团体操。第八节的奥赛课正常;第九节的奥赛课调到了上午第五节课,奥赛自习调到了晚三;喂了点鸡汤,说了下 \(THU,PKU\) 的极高淘汰率和学生内卷程度,并称他当时大学摆烂了,毕业后就来 \(HZ\) 当老师了。
  • 详见 2024 CSP-S 游记 9.22

做题纪要

9.23

闲话

  • 早预备的时候班主任来班说了下习字让我们找课间或晚新闻写。
  • 上午第五节课,因教练要去 \(HS\) 上课,所以除物奥外都在班里上公自(第四节课是物理,遂物奥的直接两节联排),因信奥一下课就去机房了,遂没管我们。临吃午饭的时候 \(miaomiao\) 过来问我们最近电脑还经常死机吗,见我们说不经常死机了后他说那看来是修好了。
  • 详见 2024 CSP-S 游记 9.23
  • 下午团体操要求每班能且仅能让 \(45\) 个人去跳。临开始时我们班有 \(47\) 人跳操,因我和另外几人都站得比较靠后,遂被“强制遣散”到看台上。于是实际每班能且仅能让 \(40\) 个人跳团体操。
  • 晚新闻时统计了下实际不能跳操的人员名单。
  • 晚三班主任说也改成公自了,但是我们不想上晚三,都跑各自奥赛教室去上奥赛课/奥赛自习了,班主任只好说这节课改成奥赛课/奥赛自习,他就接着给数奥上两节联排了。

做题纪要

SP1825 FTOUR2 - Free tour II

  • luogu P4149 [IOI2011] Race 套个支持单点修改、区间查询 \(\max\) 的数据结构即可。

  • 线段树貌似会 \(TLE\) ,换成树状数组维护前缀 \(\max\) 即可。

    点击查看代码
    struct node
    {
    	int nxt,to,w;
    }e[400010];
    int head[400010],col[400010],ask,ans=0,cnt=0;
    void add(int u,int v,int w)
    {
    	cnt++;
    	e[cnt].nxt=head[u];
    	e[cnt].to=v;
    	e[cnt].w=w;
    	head[u]=cnt;
    }
    struct BIT
    {	
    	int c[400010];
    	int lowbit(int x)
    	{
    		return (x&(-x));
    	}
    	void init()
    	{
    		memset(c,-0x3f,sizeof(c));
    	}
    	void add(int n,int x,int val)
    	{
    		for(int i=x;i<=n;i+=lowbit(i))
    		{
    			c[i]=max(c[i],val);
    		}
    	}
    	int getsum(int x)
    	{
    		int ans=-0x3f3f3f3f;
    		for(int i=x;i>=1;i-=lowbit(i))
    		{
    			ans=max(ans,c[i]);
    		}
    		return ans;
    	}
    	void clear(int n,int x)
    	{
    		for(int i=x;i<=n;i+=lowbit(i))
    		{
    			c[i]=-0x3f3f3f3f;
    		}
    	}
    }T;
    struct Divide_On_Tree
    {
    	int siz[400010],weight[400010],vis[400010],dis[400010],sum[400010],tmp[400010],d[400010],center=0;
    	queue<int>q;
    	void init(int n)
    	{
    		T.init();
    		center=0;
    		get_center(1,0,n);
    		get_siz(center,0);
    		divide(center);
    	}
    	void get_center(int x,int fa,int n)
    	{
    		siz[x]=1;
    		weight[x]=0;
    		for(int i=head[x];i!=0;i=e[i].nxt)
    		{
    			if(e[i].to!=fa&&vis[e[i].to]==0)
    			{
    				get_center(e[i].to,x,n);
    				siz[x]+=siz[e[i].to];
    				weight[x]=max(weight[x],siz[e[i].to]);
    			}
    		}
    		weight[x]=max(weight[x],n-siz[x]);
    		if(weight[x]<=n/2)
    		{
    			center=x;
    		}
    	}
    	void get_siz(int x,int fa)
    	{
    		siz[x]=1;
    		for(int i=head[x];i!=0;i=e[i].nxt)
    		{
    			if(e[i].to!=fa&&vis[e[i].to]==0)
    			{
    				get_siz(e[i].to,x);
    				siz[x]+=siz[e[i].to];
    			}
    		}
    	}
    	void get_dis(int x,int fa,int rt)
    	{
    		if(sum[x]+col[rt]<=ask)
    		{
    			tmp[0]++;
    			tmp[tmp[0]]=sum[x];
    			d[tmp[0]]=dis[x];
    			for(int i=head[x];i!=0;i=e[i].nxt)
    			{
    				if(e[i].to!=fa&&vis[e[i].to]==0)
    				{
    					dis[e[i].to]=dis[x]+e[i].w;
    					sum[e[i].to]=sum[x]+col[e[i].to];
    					get_dis(e[i].to,x,rt);
    				}
    			}
    		}
    	}
    	void divide(int x)
    	{
    		T.add(ask+2,0+1,0);
    		q.push(0);
    		vis[x]=1;
    		for(int i=head[x];i!=0;i=e[i].nxt)
    		{
    			if(vis[e[i].to]==0)
    			{
    				tmp[0]=0;
    				dis[e[i].to]=e[i].w;
    				sum[e[i].to]=col[e[i].to];
    				get_dis(e[i].to,x,x);
    				for(int j=1;j<=tmp[0];j++)
    				{
    					ans=max(ans,T.getsum(ask-tmp[j]+1)+d[j]);
    				}	
    				for(int j=1;j<=tmp[0];j++)
    				{
    					T.add(ask+2,tmp[j]+col[x]+1,d[j]);
    					q.push(tmp[j]);
    				}
    			}
    		}
    		while(q.empty()==0)
    		{
    			T.clear(ask+2,q.front()+col[x]+1);
    			q.pop();
    		}
    		for(int i=head[x];i!=0;i=e[i].nxt)
    		{
    			if(vis[e[i].to]==0)
    			{
    				center=0;
    				get_center(e[i].to,x,siz[e[i].to]);
    				get_siz(center,0);
    				divide(center);
    			}
    		}
    	}
    }D;
    int main()
    {
    	int n,m,u,v,w,i;
    	scanf("%d%d%d",&n,&ask,&m);
    	for(i=1;i<=m;i++)
    	{
    		scanf("%d",&u);
    		col[u]=1;
    	}
    	for(i=1;i<=n-1;i++)
    	{
    		scanf("%d%d%d",&u,&v,&w);
    		add(u,v,w);
    		add(v,u,w);
    	}
    	D.init(n);
    	printf("%d\n",ans);
    	return 0;
    }
    

9.24

闲话

  • 吃完早饭就直接来机房了。因第四节课是数学课, \(miaomiao\) 原想让我们多占节课打模拟赛的念头打消了。
  • 详见 2024 CSP-S 游记 9.24
  • 晚一、二要去跳团体操,原晚一的物理自习调到了明天第五节课,但明天第五节课是数学自习,遂不调了,让我们找时间把作业写了。
  • 晚二的生物自习调到晚三了。班主任对我们没有把晚一调到上午第五节课感到很困惑。

做题纪要

luogu P10930 异象石

  • 多倍经验: luogu P3320 [SDOI2015] 寻宝游戏 | CF176E Archaeology

  • 做法同 luogu P9340 [JOISC 2023 Day3] Tourismset 动态维护加入、删除即可。

    点击查看代码
    struct node
    {
    	ll nxt,to,w;
    }e[200010];
    ll head[200010],siz[200010],fa[200010],dep[200010],dis[200010],son[200010],rk[200010],top[200010],dfn[200010],cnt=0,tot=0;
    set<ll>s;
    set<ll>::iterator pre,nxt;
    void add(ll u,ll v,ll w)
    {
    	cnt++;
    	e[cnt].nxt=head[u];
    	e[cnt].to=v;
    	e[cnt].w=w;
    	head[u]=cnt;
    }
    void dfs1(ll x,ll father)
    {
    	tot++;
    	dfn[x]=tot;
    	rk[tot]=x;
    	siz[x]=1;
    	fa[x]=father;
    	dep[x]=dep[father]+1;
    	for(ll i=head[x];i!=0;i=e[i].nxt)
    	{
    		if(e[i].to!=father)
    		{
    			dis[e[i].to]=dis[x]+e[i].w;
    			dfs1(e[i].to,x);
    			siz[x]+=siz[e[i].to];
    			son[x]=(siz[e[i].to]>siz[son[x]])?e[i].to:son[x];
    		}
    	}
    }
    void dfs2(ll x,ll id)
    {
    	top[x]=id;
    	if(son[x]!=0)
    	{
    		dfs2(son[x],id);
    		for(ll i=head[x];i!=0;i=e[i].nxt)
    		{
    			if(e[i].to!=fa[x]&&e[i].to!=son[x])
    			{
    				dfs2(e[i].to,e[i].to);
    			}
    		}
    	}
    }
    ll lca(ll u,ll v)
    {
    	while(top[u]!=top[v])
    	{
    		if(dep[top[u]]>dep[top[v]])
    		{
    			u=fa[top[u]];
    		}
    		else
    		{
    			v=fa[top[v]];
    		}
    	}
    	return (dep[u]<dep[v])?u:v;
    }
    ll get_dis(ll x,ll y)
    {
    	x=rk[x];
    	y=rk[y];
    	return dis[x]+dis[y]-2*dis[lca(x,y)];
    }
    void add(ll x,ll &ans)
    {
    	nxt=s.upper_bound(x);
    	pre=--s.lower_bound(x);
    	if(*nxt!=0x7f7f7f7f)
    	{
    		if(*pre!=-0x7f7f7f7f)
    		{
    			ans-=get_dis(*pre,*nxt);
    			ans+=get_dis(*pre,x);
    		}
    		ans+=get_dis(x,*nxt);
    	}
    	else
    	{
    		if(*pre!=-0x7f7f7f7f)
    		{
    			ans+=get_dis(*pre,x);
    		}
    	}
    	s.insert(x);
    }
    void del(ll x,ll &ans)
    {
    	nxt=s.upper_bound(x);
    	pre=--s.lower_bound(x);
    	if(*nxt!=0x7f7f7f7f)
    	{
    		if(*pre!=-0x7f7f7f7f)
    		{
    			ans+=get_dis(*pre,*nxt);
    			ans-=get_dis(*pre,x);
    		}
    		ans-=get_dis(x,*nxt);
    	}
    	else
    	{
    		if(*pre!=-0x7f7f7f7f)
    		{
    			ans-=get_dis(*pre,x);
    		}
    	}
    	s.erase(s.lower_bound(x));
    }
    ll ask(ll ans)
    {
    	if(s.size()<=3)
    	{
    		return ans;
    	}
    	else
    	{
    		pre=s.upper_bound(-0x7f7f7f7f);
    		nxt=--s.lower_bound(0x7f7f7f7f);
    		return (ans+get_dis(*pre,*nxt))/2;
    	}
    }
    int main()
    {
    	ll n,m,u,v,w,ans=0,x,i;
    	char pd;
    	cin>>n;
    	for(i=1;i<=n-1;i++)
    	{
    		cin>>u>>v>>w;
    		add(u,v,w);
    		add(v,u,w);
    	}
    	dfs1(1,0);
    	dfs2(1,1);
    	cin>>m;
    	s.insert(-0x7f7f7f7f);
    	s.insert(0x7f7f7f7f);
    	for(i=1;i<=m;i++)
    	{
    		cin>>pd;
    		if(pd=='+')
    		{
    			cin>>x;
    			add(dfn[x],ans);
    		}
    		if(pd=='-')
    		{
    			cin>>x;
    			del(dfn[x],ans);
    		}
    		if(pd=='?')
    		{
    			cout<<ask(ans)<<endl;
    		}
    	}
    	return 0;
    }
    

[ABC372F] Teleporting Takahashi 2

  • 考虑让点的编号从 \(0\) 开始,从 \(1\) 开始太麻烦了。

  • 先考虑 \(O((n+m)k)\) 的暴力。

    • \(f_{i,j}\) 表示走了 \(i\) 步当前在 \(j\) 的方案数,状态转移方程为 \(f_{i,j}=\sum\limits_{(h,j) \in E}f_{i-1,h}\) ,边界为 \(f_{0,0}=1\)
    • 最终有 \(\sum\limits_{i=0}^{n-1}f_{k,i}\) 即为所求。
  • \(m\) 条边组成的集合为 \(E'\) ,因为原图是一个环,则状态转移方程可以拆做 \(f_{i,j}=f_{i-1,(j-1+n) \bmod n}+\sum\limits_{(h,j) \in E'}f_{i-1,h}\)

    • \((j-1+n) \bmod n= \begin{cases} j-1 & 1 \le j \le n-1 \\ n-1 & j=0 \end{cases}\)
  • 第一维显然可以滚动数组优化,后面的时间复杂度已足够优秀,而前面的 \(f_{i,j}\)\(f_{i-1,(j-1+n) \bmod n+1}\) 转移本质是数组的移位,但仍需继续优化。

  • 手动实现移位过程,记录点的偏移量即可。

  • 注意去重和取模。

    点击查看代码
    const ll p=998244353;
    ll f[200010],tmp[200010],u[200010],v[200010];
    ll getv(ll v,ll n,ll i)
    {
    	return ((v-i)%n+n)%n;
    }
    ll getu(ll u,ll n,ll i)
    {
    	return ((u-i+1)%n+n)%n;
    }
    int main()
    {
    	ll n,m,k,ans=0,i,j;
    	cin>>n>>m>>k;
    	for(i=1;i<=m;i++)
    	{
    		cin>>u[i]>>v[i];
    		u[i]--;
    		v[i]--;
    	}****
    	f[0]=1;
    	for(i=1;i<=k;i++)
    	{
    		for(j=1;j<=m;j++)
    		{
    			tmp[getu(u[j],n,i)]=f[getu(u[j],n,i)];
    			tmp[getv(v[j],n,i)]=f[getv(v[j],n,i)];
    		}
    		for(j=1;j<=m;j++)
    		{
    			f[getv(v[j],n,i)]=(f[getv(v[j],n,i)]+tmp[getu(u[j],n,i)])%p;
    		}
    	}
    	for(i=0;i<=n-1;i++)
    	{
    		ans=(ans+f[i])%p;
    	}
    	cout<<ans<<endl;
    	return 0;
    }
    

9.25

闲话

  • 上午举行 HZ 建校 \(73\) 周年校庆活动,话说初中我在 HS 咋没有这个活动(除初一的 \(70\) 周年校庆整得很隆重)呢。占了第二节课的语文阅读/写作和第三节课的数学课,语文阅读/写作没有补,数学把第四节课的体育占了。校庆活动主要是领导、教师代表、校友代表、学生代表讲话,然后是给和 HZ 同一天生日的师生送生日礼物,各级部和校友代表的 \(73\) 圈接力跑(实际圈数不知道怎么算的),放飞七彩气球(还有“抢夺”掉下来的气球)。
  • 详见 2024 CSP-S 游记 9.25

做题纪要

9.26

闲话

  • 下午大课间被强制拉下去跳团体操。因为是为准备运动会的开幕式,所以之间上看台见习去了。
  • 晚新闻得知了 \(9.29\) 晚上 \(7:00\) 开始运动会开幕式, \(9.30\) 上午 \(7:00\) 开始运动会,然后 \(11:20\) 放假,预期 \(10.3\) 上午 \(11:20\) 到教室做好。
  • 晚一的物理自习班内众人因没整积累本被老师狂 \(D\)
  • 晚三班主任说下午教务处查内务的结果是 \(201,202\) 班内务极差,班主任没好意思在班主任群里公开回复,然后年级主任就给班主任发私信表明这件事了。真实情况是 \(201\) 班扣分总数几乎 \(1\) 部所有班级扣的分一样多,然后班主任就恼了,说我们都上了快一个月的课,对年级部的管理都摸清楚了,关于查内务 HZ 不像 HS 一样提前告知什么时候/固定某天的什么时候查内务, HZ 是教务处老师想什么时候去查就什么时候(早上、下午、晚上都有可能)去查内务。另外为符合国家政策需要,衡水要新建高铁,目前不知道因为什么原因(教育局给市区几个稍微人多点的学校开会时没说)导致水压降低,短时间内会出现停水现象,让我们不要恐慌,说整个衡水市都这样,我们打 \(12345\) 跟上级领导反馈也没用。

做题纪要

luogu P6329 【模板】点分树 | 震波

  • 点分树/动态点分治板子。

    • 建树
      • 点分树是通过更改原树形态使树的层数变为稳定 \(O(\log n)\) 的一种重构树,常用于解决与树形态无关的带修改问题,例如路径问题、连通块问题、统计点对数量问题等。
        • 以路径问题为例,对于两点 \(x,y\) 间的距离 \(dis_{x,y}\) ,不一定非要处理出 \(\operatorname{LCA}(x,y)\) 然后按照 \(dis_{x,y}=dis_{rt,x}+dis_{rt,y}-2 \times dis_{rt,\operatorname{LCA}(x,y)}\) 计算,而可以枚举断点 \(z(z \in x \to y)\) 通过 \(dis_{x,y}=dis_{x,z}+dis_{z,y}\) 计算,因而分割成子问题求解。
      • 通过点分治每次找重心的方式来对原树进行重构,将每一次找到的重心与上一层的重心之间连一条边,这样就可以形成一棵 \(O(\log n)\) 层的树来保证部分暴力的正确复杂度。
      • 点分树的性质
        • 点分树上所有子树的大小和与深度和均为 \(O(n \log n)\)
        • 点分树上以 \(x\) 为根节点的子树对应原树中以 \(x\) 为重心/分治中心时所覆盖到的连通块。
        • 对于两点 \((u,v)\) ,其在点分树上的 \(\operatorname{LCA}\) 一定在 \(u \to v\) 的路径上,即 \(dis_{u,v}=dis_{u,\operatorname{LCA}(u,v)}+dis_{\operatorname{LCA}(u,v),v}\)
        • 点分树上两点的距离和原树上的距离无关,不能直接累加。
    • 修改
      • 在点分树上暴力跳父亲修改。
    • 查询
      • 某个节点在其点分树上的祖先节点的信息可能会被重复计算,考虑简单容斥。一般的方法是对于某个连通块内的所有节点分别维护其到重心的路径信息和其到点分树上重心的父亲节点的路径信息。
        • 常将问题拆成重心的信息和重心除某一方向的子树外的子树信息。
      • 具体使用情况因题而异。
  • 考虑枚举 \(x,y\) 在点分树上的 \(\operatorname{LCA}=z\) ,设 \(z\)\(x\) 方向上的子节点为 \(s\) , 则等价于查询 \(\begin{aligned} &\sum\limits_{\operatorname{LCA}(x,y)=z \land dis_{x,z}+dis_{z,y} \le k}a_{y} \\ &=\sum\limits_{\operatorname{LCA}(x,y)=z \land dis_{z,y} \le k-dis_{x,z}}a_{y} \\ &=\sum\limits_{y \in Subtree(z) \land y \notin Subtree(s) \land dis_{z,y} \le k-dis_{x,z}}a_{y} \\ &=\sum\limits_{y \in Subtree(z) \land dis_{z,y} \le k-dis_{x,z}}a_{y}-\sum\limits_{y \in Subtree(s) \land dis_{z,y} \le k-dis_{x,z}}a_{y} \end{aligned}\)

    • 同时也解释了上文为什么要维护到点分树上重心的父亲节点的路径信息。
  • 两种信息各开两棵动态开点线段树,以此支持单点修改、区间查询,空间复杂度为 \(O(n \log n)\)

  • \(\operatorname{LCA}\) 的实现方式,决定是否用 \(ST\) 表的 \(O(n \log n)\) 的预处理和 \(O(1)\) 的询问来代替树剖的 \(O(n)\) 的预处理和 \(O(\log n)\) 的询问,即决定最终单次询问是 \(O(\log n)\) 还是 \(O(\log^{2} n)\)

    点击查看代码
    struct node
    {
    	int nxt,to;
    }e[200010];
    int head[200010],a[200010],ask,cnt=0;
    void add(int u,int v)
    {
    	cnt++;
    	e[cnt].nxt=head[u];
    	e[cnt].to=v;
    	head[u]=cnt;
    }
    struct LCA
    {
    	int siz[200010],fa[200010],dep[200010],son[200010],top[200010];
    	void init()
    	{
    		dfs1(1,0);
    		dfs2(1,1);
    	}
    	void dfs1(int x,int father)
    	{
    		siz[x]=1;
    		fa[x]=father;
    		dep[x]=dep[father]+1;
    		for(int i=head[x];i!=0;i=e[i].nxt)
    		{
    			if(e[i].to!=father)
    			{
    				dfs1(e[i].to,x);
    				siz[x]+=siz[e[i].to];
    				son[x]=(siz[e[i].to]>siz[son[x]])?e[i].to:son[x];
    			}
    		}
    	}
    	void dfs2(int x,int id)
    	{
    		top[x]=id;
    		if(son[x]!=0)
    		{
    			dfs2(son[x],id);
    			for(int i=head[x];i!=0;i=e[i].nxt)
    			{
    				if(e[i].to!=fa[x]&&e[i].to!=son[x])
    				{
    					dfs2(e[i].to,e[i].to);
    				}
    			}
    		}
    	}
    	int lca(int u,int v)
    	{
    		while(top[u]!=top[v])
    		{
    			if(dep[top[u]]>dep[top[v]])
    			{
    				u=fa[top[u]];
    			}
    			else
    			{
    				v=fa[top[v]];
    			}
    		}
    		return (dep[u]<dep[v])?u:v;
    	}
    	int get_dis(int x,int y)//处理出原树上距离
    	{
    		return dep[x]+dep[y]-2*dep[lca(x,y)];
    	}
    }L;
    struct SMT
    {
    	int root[200010],rt_sum=0;
    	struct SegmentTree
    	{
    		int ls,rs,sum;
    	}tree[200010<<5];
    	#define lson(rt) (tree[rt].ls)
    	#define rson(rt) (tree[rt].rs)
    	void pushup(int rt)
    	{
    		tree[rt].sum=tree[lson(rt)].sum+tree[rson(rt)].sum;
    	}
    	int build_rt()
    	{
    		rt_sum++;
    		lson(rt_sum)=rson(rt_sum)=tree[rt_sum].sum=0;
    		return rt_sum;
    	}
    	void update(int &rt,int l,int r,int pos,int val)
    	{
    		if(rt==0)
    		{
    			rt=build_rt();
    		}
    		if(l==r)
    		{
    			tree[rt].sum+=val;
    			return;
    		}
    		int mid=(l+r)/2;
    		if(pos<=mid)
    		{
    			update(lson(rt),l,mid,pos,val);
    		}
    		else
    		{
    			update(rson(rt),mid+1,r,pos,val);
    		}
    		pushup(rt);
    	}
    	int query(int rt,int l,int r,int x,int y)
    	{
    		if(rt==0)
    		{
    			return 0;
    		}
    		if(x<=l&&r<=y)
    		{
    			return tree[rt].sum;
    		}
    		int mid=(l+r)/2,ans=0;
    		if(x<=mid)
    		{
    			ans+=query(lson(rt),l,mid,x,y);
    		}
    		if(y>mid)
    		{
    			ans+=query(rson(rt),mid+1,r,x,y);
    		}
    		return ans;
    	}
    }T[2];
    struct Divide_On_Tree
    {
    	int siz[200010],weight[200010],vis[200010],fa[200010],center=0;
    	void init(int n)
    	{
    		center=0;
    		get_center(1,0,n);
    		get_siz(center,0);
    		build(center);	
    	}
    	void get_center(int x,int fa,int n)
    	{
    		siz[x]=1;
    		weight[x]=0;
    		for(int i=head[x];i!=0;i=e[i].nxt)
    		{
    			if(e[i].to!=fa&&vis[e[i].to]==0)
    			{
    				get_center(e[i].to,x,n);
    				siz[x]+=siz[e[i].to];
    				weight[x]=max(weight[x],siz[e[i].to]);
    			}
    		}
    		weight[x]=max(weight[x],n-siz[x]);
    		if(weight[x]<=n/2)
    		{
    			center=x;
    		}
    	}
    	void get_siz(int x,int fa)
    	{
    		siz[x]=1;
    		for(int i=head[x];i!=0;i=e[i].nxt)
    		{
    			if(e[i].to!=fa&&vis[e[i].to]==0)
    			{
    				get_siz(e[i].to,x);
    				siz[x]+=siz[e[i].to];
    			}
    		}
    	}
    	void build(int x)//建点分树
    	{
    		vis[x]=1;
    		for(int i=head[x];i!=0;i=e[i].nxt)
    		{
    			if(vis[e[i].to]==0)
    			{
    				center=0;
    				get_center(e[i].to,0,siz[e[i].to]);
    				get_siz(center,0);
    				fa[center]=x;//重心间连边
    				build(center);
    			}
    		}
    	}
    	void update(int x,int val)
    	{
    		T[0].update(T[0].root[x],0,ask,0,val);//先修改 x 自身
    		for(int rt=x;fa[rt]!=0;rt=fa[rt])
    		{
    			T[0].update(T[0].root[fa[rt]],0,ask,L.get_dis(fa[rt],x),val);
    			T[1].update(T[1].root[rt],0,ask,L.get_dis(fa[rt],x),val);
    		}
    	}
    	int query(int x,int k)
    	{
    		int ans=T[0].query(T[0].root[x],0,ask,0,k);//先查询 x 自身
    		for(int rt=x;fa[rt]!=0;rt=fa[rt])
    		{
    			if(L.get_dis(fa[rt],x)<=k)
    			{
    				ans+=T[0].query(T[0].root[fa[rt]],0,ask,0,k-L.get_dis(fa[rt],x));
    				ans-=T[1].query(T[1].root[rt],0,ask,0,k-L.get_dis(fa[rt],x));// rt 即 fa[rt] 在 x 方向上的子树
    			}
    		}
    		return ans;
    	}	
    }D;
    int main()
    {
    	int n,m,u,v,pd,x,y,ans=0,i;
    	cin>>n>>m;
    	ask=n;
    	for(i=1;i<=n;i++)
    	{
    		cin>>a[i];
    	}
    	for(i=1;i<=n-1;i++)
    	{
    		cin>>u>>v;
    		add(u,v);
    		add(v,u);
    	}
    	L.init();
    	D.init(n);
    	for(i=1;i<=n;i++)
    	{
    		D.update(i,a[i]);//将原数列看做修改
    	}
    	for(i=1;i<=m;i++)
    	{
    		cin>>pd>>x>>y;
    		x^=ans;
    		y^=ans;
    		if(pd==0)
    		{
    			ans=D.query(x,y);
    			cout<<ans<<endl;
    		}
    		else
    		{
    			D.update(x,y-a[x]);
    			a[x]=y;
    		}
    	}
    	return 0;
    }
    

luogu P1438 无聊的数列

  • 考虑对差分数组进行修改。

  • 设差分数组为 \(\{ b \}\) ,则操作 \(1\) 等价于 \(\begin{cases} b_{l}+=k \\ b_{l+1 \sim r}+=d \\ b_{r+1}-=k+(r-l)d \end{cases}\)

    点击查看代码
    ll a[100010],b[100010];
    struct SMT
    {
    	struct SegmentTree
    	{
    		ll l,r,sum,lazy;
    	}tree[400010];
    	ll lson(ll x)
    	{
    		return x*2;
    	}
    	ll rson(ll x)
    	{
    		return x*2+1;
    	}
    	void pushup(ll rt)
    	{
    		tree[rt].sum=tree[lson(rt)].sum+tree[rson(rt)].sum;
    	}
    	void build(ll rt,ll l,ll r,ll a[])
    	{
    		tree[rt].l=l;
    		tree[rt].r=r;
    		if(l==r)
    		{
    			tree[rt].sum=a[l];
    			return;
    		}
    		ll mid=(l+r)/2;
    		build(lson(rt),l,mid,a);
    		build(rson(rt),mid+1,r,a);
    		pushup(rt);
    	}
    	void pushdown(ll rt)
    	{
    		if(tree[rt].lazy!=0)
    		{
    			tree[lson(rt)].lazy+=tree[rt].lazy;
    			tree[rson(rt)].lazy+=tree[rt].lazy;
    			tree[lson(rt)].sum+=tree[rt].lazy*(tree[lson(rt)].r-tree[lson(rt)].l+1);
    			tree[rson(rt)].sum+=tree[rt].lazy*(tree[rson(rt)].r-tree[rson(rt)].l+1);
    			tree[rt].lazy=0;
    		}
    	}
    	void update(ll rt,ll x,ll y,ll val)
    	{
    		if(x<=tree[rt].l&&tree[rt].r<=y)
    		{
    			tree[rt].lazy+=val;
    			tree[rt].sum+=val*(tree[rt].r-tree[rt].l+1);
    			return;
    		}
    		pushdown(rt);
    		ll mid=(tree[rt].l+tree[rt].r)/2;
    		if(x<=mid)
    		{
    			update(lson(rt),x,y,val);
    		}
    		if(y>mid)
    		{
    			update(rson(rt),x,y,val);
    		}
    		pushup(rt);
    	}
    	ll query(ll rt,ll x,ll y)
    	{
    		if(x<=tree[rt].l&&tree[rt].r<=y)
    		{
    			return tree[rt].sum;
    		}
    		pushdown(rt);
    		ll mid=(tree[rt].l+tree[rt].r)/2,ans=0;
    		if(x<=mid)
    		{
    			ans+=query(lson(rt),x,y);
    		}
    		if(y>mid)
    		{
    			ans+=query(rson(rt),x,y);
    		}
    		return ans;
    	}
    }T;
    int main()
    {
    	ll n,m,pd,l,r,k,d,i;
    	cin>>n>>m;
    	for(i=1;i<=n;i++)
    	{
    		cin>>a[i];
    		b[i]=a[i]-a[i-1];
    	}
    	T.build(1,1,n,b);
    	for(i=1;i<=m;i++)
    	{
    		cin>>pd;
    		if(pd==1)
    		{
    			cin>>l>>r>>k>>d;
    			T.update(1,l,l,k);
    			if(l+1<=r)
    			{
    				T.update(1,l+1,r,d);
    			}
    			if(r+1<=n)
    			{
    				T.update(1,r+1,r+1,-(k+(r-l)*d));
    			}
    		}
    		else
    		{
    			cin>>l;
    			cout<<T.query(1,1,l)<<endl;
    		}
    	}
    	return 0;
    }
    

CF616E Sum of Remainders

  • 多倍经验: luogu P2261 [CQOI2007] 余数求和

    点击查看代码
    const ll p=1000000007;
    ll qpow(ll a,ll b,ll p)
    {
    	ll ans=1;
    	while(b)
    	{
    		if(b&1)
    		{
    			ans=ans*a%p;
    		}
    		b>>=1;
    		a=a*a%p;
    	}
    	return ans;
    }
    int main()
    {
    	ll n,m,l,r,ans,sum,inv=qpow(2,p-2,p);
    	cin>>n>>m;
    	ans=(n%p)*(m%p)%p;
    	for(l=1;l<=m;l=r+1)
    	{
    		r=(n/l>=1)?min(n/(n/l),m):m;
    		sum=(n/l)%p;
    		sum=sum*((r-l+1)%p)%p;
    		sum=sum*((l+r)%p)%p;
    		sum=sum*inv%p;
    		ans=(ans-sum+p)%p;
    	}
    	cout<<ans<<endl;
    	return 0;
    }
    

9.27

闲话

  • 早上跑操时因昨天扣分太多,被年级部要求加圈;侯操时被告知仍有内务问题,跑完应该跑的一圈后被班主任全体留下在 \(5 \min\) 的时间内回宿舍整理内务,于是加跑的一圈就不用跑了。
  • 下午因为前三节是奥赛联排,所以大课间没下去练团体操。
  • 晚三的时候班主任说了下周日早上有体活,让 \(7:15\) 到位,还说以后如果他没有单独通知的话都按照这个执行。

做题纪要

luogu P10603 BZOJ4372 烁烁的游戏

  • luogu P6329 【模板】点分树 | 震波 的修改/查询互换即可。

  • 区间修改、单点查询的动态开点线段树可以考虑标记永久化。

    点击查看代码
    struct node
    {
    	int nxt,to;
    }e[200010];
    int head[200010],ask,cnt=0;
    void add(int u,int v)
    {
    	cnt++;
    	e[cnt].nxt=head[u];
    	e[cnt].to=v;
    	head[u]=cnt;
    }
    struct LCA
    {
    	int siz[200010],fa[200010],dep[200010],son[200010],top[200010];
    	void init()
    	{
    		dfs1(1,0);
    		dfs2(1,1);
    	}
    	void dfs1(int x,int father)
    	{
    		siz[x]=1;
    		fa[x]=father;
    		dep[x]=dep[father]+1;
    		for(int i=head[x];i!=0;i=e[i].nxt)
    		{
    			if(e[i].to!=father)
    			{
    				dfs1(e[i].to,x);
    				siz[x]+=siz[e[i].to];
    				son[x]=(siz[e[i].to]>siz[son[x]])?e[i].to:son[x];
    			}
    		}
    	}
    	void dfs2(int x,int id)
    	{
    		top[x]=id;
    		if(son[x]!=0)
    		{
    			dfs2(son[x],id);
    			for(int i=head[x];i!=0;i=e[i].nxt)
    			{
    				if(e[i].to!=son[x]&&e[i].to!=fa[x])
    				{
    					dfs2(e[i].to,e[i].to);
    				}
    			}
    		}
    	}
    	int lca(int u,int v)
    	{
    		while(top[u]!=top[v])
    		{
    			if(dep[top[u]]>dep[top[v]])
    			{
    				u=fa[top[u]];
    			}
    			else
    			{
    				v=fa[top[v]];
    			}
    		}
    		return (dep[u]<dep[v])?u:v;
    	}
    	int get_dis(int x,int y)
    	{
    		return dep[x]+dep[y]-2*dep[lca(x,y)];
    	}
    }L;
    struct SMT
    {
    	int root[200010],rt_sum=0;
    	struct SegmentTree
    	{
    		int ls,rs,lazy;
    	}tree[200010<<5];
    	#define lson(rt) (tree[rt].ls)
    	#define rson(rt) (tree[rt].rs)
    	int build_rt()
    	{
    		rt_sum++;
    		lson(rt_sum)=rson(rt_sum)=tree[rt_sum].lazy=0;
    		return rt_sum;
    	}
    	void update(int &rt,int l,int r,int x,int y,int val)
    	{	
    		if(rt==0)
    		{
    			rt=build_rt();
    		}
    		if(x<=l&&r<=y)
    		{		
    			tree[rt].lazy+=val;
    			return;
    		}
    		int mid=(l+r)/2;
    		if(x<=mid)
    		{
    			update(lson(rt),l,mid,x,y,val);
    		}
    		if(y>mid)
    		{
    			update(rson(rt),mid+1,r,x,y,val);
    		}
    	}
    	int query(int rt,int l,int r,int pos)
    	{
    		if(rt==0)
    		{
    			return 0;
    		}
    		if(l==r)
    		{
    			return tree[rt].lazy;
    		}
    		int mid=(l+r)/2;
    		if(pos<=mid)
    		{
    			return query(lson(rt),l,mid,pos)+tree[rt].lazy;
    		}
    		else
    		{
    			return query(rson(rt),mid+1,r,pos)+tree[rt].lazy;
    		}
    	}
    }T[2];
    struct Divide_On_Tree
    {
    	int siz[200010],weight[200010],vis[200010],fa[200010],center=0;
    	void init(int n)
    	{
    		center=0;
    		get_center(1,0,n);
    		get_siz(center,0);
    		build(center);
    	}
    	void get_center(int x,int fa,int n)
    	{
    		siz[x]=1;
    		weight[x]=0;
    		for(int i=head[x];i!=0;i=e[i].nxt)
    		{
    			if(e[i].to!=fa&&vis[e[i].to]==0)
    			{
    				get_center(e[i].to,x,n);
    				siz[x]+=siz[e[i].to];
    				weight[x]=max(weight[x],siz[e[i].to]);
    			}
    		}
    		weight[x]=max(weight[x],n-siz[x]);
    		if(weight[x]<=n/2)
    		{
    			center=x;
    		}
    	}
    	void get_siz(int x,int fa)
    	{
    		siz[x]=1;
    		for(int i=head[x];i!=0;i=e[i].nxt)
    		{
    			if(e[i].to!=fa&&vis[e[i].to]==0)
    			{
    				get_siz(e[i].to,x);
    				siz[x]+=siz[e[i].to];
    			}
    		}
    	}
    	void build(int x)
    	{
    		vis[x]=1;
    		for(int i=head[x];i!=0;i=e[i].nxt)
    		{
    			if(vis[e[i].to]==0)
    			{
    				center=0;
    				get_center(e[i].to,0,siz[e[i].to]);
    				get_siz(center,0);
    				fa[center]=x;
    				build(center);
    			}
    		}
    	}
    	void update(int x,int k,int val)
    	{
    		T[0].update(T[0].root[x],0,ask,0,k,val);
    		for(int rt=x;fa[rt]!=0;rt=fa[rt])
    		{
    			if(L.get_dis(fa[rt],x)<=k)
    			{
    				T[0].update(T[0].root[fa[rt]],0,ask,0,k-L.get_dis(fa[rt],x),val);
    				T[1].update(T[1].root[rt],0,ask,0,k-L.get_dis(fa[rt],x),val);
    			}
    		}
    	}
    	int query(int x)
    	{
    		int ans=T[0].query(T[0].root[x],0,ask,0);
    		for(int rt=x;fa[rt]!=0;rt=fa[rt])
    		{
    			ans+=T[0].query(T[0].root[fa[rt]],0,ask,L.get_dis(fa[rt],x));
    			ans-=T[1].query(T[1].root[rt],0,ask,L.get_dis(fa[rt],x));
    		}
    		return ans;
    	}
    }D;
    int main()
    {
    	int n,m,u,v,x,d,w,i;
    	char pd;
    	cin>>n>>m;
    	ask=n;
    	for(i=1;i<=n-1;i++)
    	{
    		cin>>u>>v;
    		add(u,v);
    		add(v,u);
    	}
    	L.init();
    	D.init(n);
    	for(i=1;i<=m;i++)
    	{
    		cin>>pd;
    		if(pd=='Q')
    		{
    			cin>>x;
    			cout<<D.query(x)<<endl;
    		}
    		else
    		{
    			cin>>x>>d>>w; 
    			D.update(x,d,w);
    		}
    	}
    	return 0;
    }
    

9.28

闲话

  • 早操时年级主任重申近期南水北调工程有一个部分要检修,恰好就经过衡水,导致水压降低,供水可能出现问题,现在衡水市已经调用了备用水,让我们不要恐慌,节约用水,且停掉了我们洗澡用水。
  • 上午阅览课看杂志,里面部分的散文内容过于逆天。
  • 下午第八节课的化学自习得知了国庆大约每科都有一张卷子,化学作业因为回来那天按照补课系列就我们班多一节课,遂可以带回家写也可以等那节课写。第九、十课各年级被拉下去排练运动会开幕式,直接上看台了,然后因为班内人员有调动,将身体不协调的“发配”到了看台几个,我们原在看台上的就要下去替他们跳,于是通过石头剪刀布成功票走了两个当时因人员过多而导致跟我一起上看台的人;高一负责团体操,高二、教师代表、学生家长代表负责走方阵,高三直接会操表演。会操表演时,我在 @jijidawang 的指引下成功找到 @APJifengc ,我还被 @jijidawang “强制”向 @APJifengc 挥手试图吸引其注意,然后貌似 @APJifengc 貌似就看见我和 @jijidawang 了。
  • 晚上前两节课是阅览课(奥赛班调课有点抽象),班主任鉴于我们上午已经上过一节了,所以改成了三节公自联排,遂找班主任请假去机房打 \(ABC\) 了。
  • 详见 2024 CSP-S 游记 9.28

做题纪要

luogu P4093 [HEOI2016/TJOI2016] 序列

  • \(CDQ\) 分治优化 \(DP\) 板子。

    • luogu P3374 【模板】树状数组 1 一题中已经讲过若修改之间不相互独立,则处理 \([l,mid]\) 内的修改对 \([mid+1,r]\) 的询问的影响必须放在 cdq(l,mid)cdq(mid+1,r) 之间,从而保证修改严格按照时间顺序进行。
    • 本质上是中序遍历的一个过程。
  • 记第 \(i\) 个数能变成的最大/小的数为 \(val_{i,0/1}\)

  • \(f_{i}\) 表示以 \(i\) 结尾的的合法序列的最长长度,状态转移方程为 \(f_{i}=\max\limits_{j=1}^{i-1}\{f_{j} \times [val_{j,0} \le a_{i} \land a_{j} \le val_{i,1}] \} +1\)

  • \(CDQ\) 分治维护三维偏序即可。

  • 具体地,以 \((a_{i},val_{i,0},val_{i,1})\) 作为 \(i\) 的三维。因分治时需处理 \([l,mid]\)\([mid+1,r]\) 进行转移,可以让 \([l,mid]\)\([mid+1,r]\) 分别以 \(a_{i}\)\(val_{i,1}\) 升序排序,将 \(val_{i,0}\) 作为树状数组的下标,查询时查询 \([1,a_{i}]\) 的取 \(\max\) 即可。接着恢复 \([mid+1,r]\) 的原序然后递归处理 \([mid+1,r]\)

    点击查看代码
    struct node
    {
    	int a,l,r,id;
    }q[100010];
    int f[100010];
    bool cmpa(node a,node b)
    {
    	return a.a<b.a;
    }
    bool cmpl(node a,node b)
    {
    	return a.l<b.l;
    }
    bool cmpid(node a,node b)
    {
    	return a.id<b.id;
    }
    struct BIT
    {
    	int c[100010];
    	int lowbit(int x)
    	{
    		return (x&(-x));
    	}
    	void add(int n,int x,int val)
    	{
    		for(int i=x;i<=n;i+=lowbit(i))
    		{
    			c[i]=max(c[i],val);
    		}
    	}
    	void del(int n,int x)
    	{
    		for(int i=x;i<=n;i+=lowbit(i))
    		{
    			c[i]=0;
    		}
    	}
    	int getsum(int x)
    	{
    		int ans=0;
    		for(int i=x;i>=1;i-=lowbit(i))
    		{ 
    			ans=max(ans,c[i]);
    		}
    		return ans;
    	}
    }T;
    void cdq(int l,int r,int k)
    {
    	if(l==r)
    	{
    		return;
    	}
    	int mid=(l+r)/2,x,y;
    	cdq(l,mid,k);
    	sort(q+l,q+mid+1,cmpa);
    	sort(q+mid+1,q+r+1,cmpl);
    	for(x=l,y=mid+1;y<=r;y++)
    	{
    		for(;q[x].a<=q[y].l&&x<=mid;x++)
    		{
    			T.add(k,q[x].r,f[q[x].id]);
    		}
    		f[q[y].id]=max(f[q[y].id],T.getsum(q[y].a)+1);
    	}
    	x--;
    	for(int i=l;i<=x;i++)
    	{
    		T.del(k,q[i].r);
    	}
    	sort(q+mid+1,q+r+1,cmpid);
    	cdq(mid+1,r,k);
    }
    int main()
    {
    	int n,m,x,y,ans=0,i;
    	cin>>n>>m;
    	for(i=1;i<=n;i++)
    	{
    		cin>>q[i].a;
    		q[i].l=q[i].r=q[i].a;
    		q[i].id=i;
    		f[i]=1;
    	}
    	for(i=1;i<=m;i++)
    	{
    		cin>>x>>y;
    		q[x].l=min(q[x].l,y);
    		q[x].r=max(q[x].r,y);
    	}
    	cdq(1,n,n);
    	for(i=1;i<=n;i++)
    	{
    		ans=max(ans,f[i]);
    	}	
    	cout<<ans<<endl;
    	return 0;
    }
    

[ABC373A] September

  • 基础字符串。

    点击查看代码
    string s;
    int main()
    {
    	int ans=0,i;
    	for(i=1;i<=12;i++)
    	{
    		cin>>s;
    		ans+=(s.size()==i);
    	}
    	cout<<ans<<endl;
    	return 0;
    }
    

[ABC373B] 1D Keyboard

  • 模拟。

    点击查看代码
    int a[30];
    string s;
    int main()
    {
    	int ans=0,i;
    	cin>>s;
    	for(i=0;i<s.size();i++)
    	{
    		a[s[i]-'A'+1]=i+1;
    	}
    	for(i=2;i<=26;i++)
    	{
    		ans+=abs(a[i]-a[i-1]);
    	}
    	cout<<ans<<endl;
    	return 0;
    }
    

[ABC373C] Max Ai+Bj

  • 贪心。

    点击查看代码
    ll a[500010],b[500010];
    int main()
    {
    	ll n,i;
    	cin>>n;
    	for(i=1;i<=n;i++)
    	{
    		cin>>a[i];
    	}
    	for(i=1;i<=n;i++)
    	{
    		cin>>b[i];
    	}
    	sort(a+1,a+1+n);
    	sort(b+1,b+1+n);
    	cout<<a[n]+b[n]<<endl;
    	return 0;
    }
    

[ABC373D] Hidden Weights

  • 多倍经验: [ABC087D] People on a Line

  • 带权并查集或差分约束。

    点击查看代码
    struct node
    {
    	ll nxt,to,w;
    }e[600010];
    ll head[600010],vis[600010],dis[600010],cnt=0;
    void add(ll u,ll v,ll w)
    {
    	cnt++;
    	e[cnt].nxt=head[u];
    	e[cnt].to=v;
    	e[cnt].w=w;
    	head[u]=cnt;
    }
    void spfa(ll s)
    {
    	ll i,x;
    	memset(vis,0,sizeof(vis));
    	memset(dis,0x3f,sizeof(dis));
    	queue<ll>q;
    	q.push(s);
    	dis[s]=0;
    	vis[s]=1;
    	while(q.empty()==0)
    	{
    		x=q.front();
    		for(i=head[x];i!=0;i=e[i].nxt)
    		{
    			if(dis[e[i].to]>dis[x]+e[i].w)
    			{
    				dis[e[i].to]=dis[x]+e[i].w;
    				if(vis[e[i].to]==0)
    				{
    					q.push(e[i].to);
    					vis[e[i].to]=1;
    				}
    			}
    		}
    		vis[x]=0;
    		q.pop();
    	}
    }
    int main()
    {
    	ll n,m,u,v,w,i;
    	cin>>n>>m;
    	for(i=1;i<=n;i++)
    	{
    		add(0,i,0);
    	}
    	for(i=1;i<=m;i++)
    	{
    		cin>>u>>v>>w;
    		add(v,u,-w);
    		add(u,v,w);
    	}
    	spfa(0);
    	for(i=1;i<=n;i++)
    	{
    		cout<<dis[i]<<" ";
    	}
    	return 0;
    }
    

[ABC373F] Knapsack with Diminishing Values

  • 分组背包写个常数小点的加 \(C++20\) 加手动开 \(O3\) 就过了(赛时直接把火车头粘上了),算下来 \(AT\) 神机能跑 \(1e10\)

    点击查看代码
    #include<bits/stdc++.h>
    #pragma GCC optimize(3)
    #pragma GCC target("avx")
    #pragma GCC optimize("Ofast")
    #pragma GCC optimize("inline")
    #pragma GCC optimize("-fgcse")
    #pragma GCC optimize("-fgcse-lm")
    #pragma GCC optimize("-fipa-sra")
    #pragma GCC optimize("-ftree-pre")
    #pragma GCC optimize("-ftree-vrp")
    #pragma GCC optimize("-fpeephole2")
    #pragma GCC optimize("-ffast-math")
    #pragma GCC optimize("-fsched-spec")
    #pragma GCC optimize("unroll-loops")
    #pragma GCC optimize("-falign-jumps")
    #pragma GCC optimize("-falign-loops")
    #pragma GCC optimize("-falign-labels")
    #pragma GCC optimize("-fdevirtualize")
    #pragma GCC optimize("-fcaller-saves")
    #pragma GCC optimize("-fcrossjumping")
    #pragma GCC optimize("-fthread-jumps")
    #pragma GCC optimize("-funroll-loops")
    #pragma GCC optimize("-fwhole-program")
    #pragma GCC optimize("-freorder-blocks")
    #pragma GCC optimize("-fschedule-insns")
    #pragma GCC optimize("inline-functions")
    #pragma GCC optimize("-ftree-tail-merge")
    #pragma GCC optimize("-fschedule-insns2")
    #pragma GCC optimize("-fstrict-aliasing")
    #pragma GCC optimize("-fstrict-overflow")
    #pragma GCC optimize("-falign-functions")
    #pragma GCC optimize("-fcse-skip-blocks")
    #pragma GCC optimize("-fcse-follow-jumps")
    #pragma GCC optimize("-fsched-interblock")
    #pragma GCC optimize("-fpartial-inlining")
    #pragma GCC optimize("no-stack-protector")
    #pragma GCC optimize("-freorder-functions")
    #pragma GCC optimize("-findirect-inlining")
    #pragma GCC optimize("-fhoist-adjacent-loads")
    #pragma GCC optimize("-frerun-cse-after-loop")
    #pragma GCC optimize("inline-small-functions")
    #pragma GCC optimize("-finline-small-functions")
    #pragma GCC optimize("-ftree-switch-conversion")
    #pragma GCC optimize("-foptimize-sibling-calls")
    #pragma GCC optimize("-fexpensive-optimizations")
    #pragma GCC optimize("-funsafe-loop-optimizations")
    #pragma GCC optimize("inline-functions-called-once")
    #pragma GCC optimize("-fdelete-null-pointer-checks")
    using namespace std;
    #define ll long long 
    #define ull unsigned long long
    #define sort stable_sort 
    #define endl '\n'
    ll w[3010],v[3010],f[2][3010];
    int main()
    {
    	ll n,m,ans=0,i,j,k;
    	cin>>n>>m;
    	for(i=1;i<=n;i++)
    	{
    		cin>>w[i]>>v[i];
    	}
    	for(i=1;i<=n;i++)
    	{
    		for(j=1;j<=m;j++)
    		{
    			f[i&1][j]=f[(i-1)&1][j];
    		}
    		for(k=1;k*w[i]<=m;k++)
    		{
    			for(j=k*w[i];j<=m;j++)
    			{
    				f[i&1][j]=max(f[i&1][j],f[(i-1)&1][j-k*w[i]]+k*v[i]-k*k);
    			}
    		}
    	}
    	for(i=1;i<=m;i++)
    	{
    		ans=max(ans,f[n&1][i]);
    	}
    	cout<<ans<<endl;
    	return 0;
    }
    

9.29

闲话

  • 因为明天国庆开始放假,所以补周一的课。
  • 下午大课间后是奥赛联排,但因为有团体操彩排,所以被强制拉下去练操,班主任也救不了我们。上看台见习后又被班主任叫下来问就彩排十多分钟我们还坐在上面干啥,让直接去各自奥赛教室准备上课。遂跑去机房了,然后发现 @Charlie_ljk 早就跑到机房了。
  • 详见 2024 CSP-S 游记 9.29
  • 晚上有运动会开幕式,所以让 \(18:13\) 去吃饭, \(18:40\) 到各自制定地点集合。
  • 开幕式时拿着物理、化学的假期作业上看台去见习了,感觉开幕式跟去年的差不多,但多了艺术特长生在主席团前跳舞、高三年级会操(再次看到 @APJifengc )、在没有提前通知的情况下在操场弯道处放烟花(之前因为空气污染问题所以是找人放礼炮;貌似还轰下了台摄影用的无人机),高二的饰品(包括头上带/戴的和手里拿的)、家长方队场上临时指挥有点抽象。

做题纪要

牛客 NC278307 搬家

  • [ABC370F] Cake Division 的子问题,倍增处理即可。

    点击查看代码
    ll a[100010],sum[100010],f[100010][20],N;
    int main()
    {
    	ll t,n,m,k,ans,l,r,mid,pos,i,j,h;
    	cin>>t;
    	for(h=1;h<=t;h++)
    	{
    		cin>>n>>m>>k;
    		N=log2(n)+1;
    		ans=0;
    		for(i=1;i<=n;i++)
    		{
    			cin>>a[i];
    			sum[i]=sum[i-1]+a[i];
    		}
    		for(i=1;i<=n;i++)
    		{
    			l=i;
    			r=n;
    			pos=i;
    			while(l<=r)
    			{
    				mid=(l+r)/2;
    				if(sum[mid]-sum[i-1]<=k)
    				{
    					pos=mid;
    					l=mid+1;
    				}
    				else
    				{
    					r=mid-1;
    				}
    			}
    			f[i][0]=pos+1;
    		}
    		f[n+1][0]=n+1;
    		for(j=1;j<=N;j++)
    		{
    			for(i=1;i<=n+1;i++)
    			{
    				f[i][j]=f[f[i][j-1]][j-1];
    			}
    		}
    		for(i=1;i<=n;i++)
    		{
    			pos=i;
    			for(j=N;j>=0;j--)
    			{
    				if((m>>j)&1)
    				{
    					pos=f[pos][j];
    				}
    			}
    			ans=max(ans,pos-i);
    		}
    		cout<<ans<<endl;
    	}
    	return 0;
    }
    

9.30

闲话

做题纪要

luogu P3364 Cool loves touli

  • \(CDQ\) 分治优化 \(DP\) 板子。

    点击查看代码
    struct node
    {
    	int l,s,w,a,id;
    }q[100010];
    struct BIT
    {
    	int c[100000010];
    	int lowbit(int x)
    	{
    		return (x&(-x));
    	}
    	void add(int n,int x,int val)
    	{
    		for(int i=x;i<=n;i+=lowbit(i))
    		{
    			c[i]=max(c[i],val);
    		}
    	}
    	int getsum(int x)
    	{
    		int ans=0;
    		for(int i=x;i>=1;i-=lowbit(i))
    		{
    			ans=max(ans,c[i]);
    		}
    		return ans;
    	}
    	void del(int n,int x)
    	{
    		for(int i=x;i<=n;i+=lowbit(i))
    		{
    			c[i]=0;
    		}
    	}
    }T;
    int f[100010];
    bool cmpl(node a,node b)
    {
    	return a.l<b.l;
    }
    bool cmps(node a,node b)
    {
    	return a.s<b.s;
    }
    bool cmpa(node a,node b)
    {
    	return a.a<b.a;
    }
    bool cmpid(node a,node b)
    {
    	return a.id<b.id;
    }
    void cdq(int l,int r,int k)
    {
    	if(l==r)
    	{
    		return;
    	}
    	int mid=(l+r)/2,x,y;
    	cdq(l,mid,k);
    	sort(q+l,q+mid+1,cmpa);
    	sort(q+mid+1,q+r+1,cmps);
    	for(x=l,y=mid+1;y<=r;y++)
    	{
    		for(;q[x].a<=q[y].s&&x<=mid;x++)
    		{
    			T.add(k,q[x].w,f[q[x].id]);
    		}
    		f[q[y].id]=max(f[q[y].id],T.getsum(q[y].a)+1);
    	}
    	x--;
    	for(int i=l;i<=x;i++)
    	{
    		T.del(k,q[i].w);
    	}
    	sort(q+mid+1,q+r+1,cmpid);
    	cdq(mid+1,r,k);
    }
    int main()
    {
    	int n,ans=0,i;
    	cin>>n;
    	for(i=1;i<=n;i++)
    	{
    		cin>>q[i].l>>q[i].s>>q[i].w>>q[i].a;
    		f[i]=1;
    	}
    	sort(q+1,q+1+n,cmpl);
    	for(i=1;i<=n;i++)
    	{
    		q[i].id=i;
    	}
    	cdq(1,n,100000000);
    	for(i=1;i<=n;i++)
    	{
    		ans=max(ans,f[i]);
    	}
    	cout<<ans<<endl;
    	return 0;
    }
    

CF855E Salazar Slytherin's Locket

  • 观察到 \(2\le b \le 10\) ,直接状压跑数位 \(DP\) 即可。

  • 注意前导零不参与运算。

    点击查看代码
    ll a[25],f[15][65][(1<<10)+10];
    ll divide(ll n,ll a[],ll b)
    {
    	ll len=0;
    	while(n)
    	{
    		len++;
    		a[len]=n%b;
    		n/=b;
    	}
    	return len;
    }
    ll dfs(ll pos,ll state,ll lead,ll limit,ll b)
    {
    	if(pos<=0)
    	{
    		return (state==0);
    	}
    	if(f[b][pos][state]!=-1&&lead==0&&limit==0)
    	{
    		return f[b][pos][state];
    	}
    	ll ans=0,maxx=(limit==0)?b-1:a[pos],i;
    	for(i=0;i<=maxx;i++)
    	{
    		ans+=dfs(pos-1,((i==0)*lead?0:(state^(1<<i))),(i==0)*lead,(i==maxx)*limit,b);
    	}
    	return (lead==0&&limit==0)?f[b][pos][state]=ans:ans;
    }
    ll ask(ll n,ll b)
    {
    	ll len=divide(n,a,b);
    	return dfs(len,0,1,1,b);
    }
    int main()
    {
    	ll t,b,l,r,i;
    	cin>>t;
    	memset(f,-1,sizeof(f));
    	for(i=1;i<=t;i++)
    	{
    		cin>>b>>l>>r;
    		cout<<ask(r,b)-ask(l-1,b)<<endl;
    	}
    	return 0;
    }
    
posted @ 2024-09-21 19:44  hzoi_Shadow  阅读(79)  评论(1编辑  收藏  举报
扩大
缩小