高一上十月上旬日记

10.1

闲话

做题纪要

10.2

闲话

  • 起床后和家长谈了下后面的集训安排。然后把英语作业剩下的抄完了;数学作业还剩两个大题,抄的过程中怀疑自己是否真的学过基本不等式对数的放缩。
  • 下午接着颓。
  • 详见 2024 CSP-S 游记 10.2
  • 详见 【闲话】高一上运动会

做题纪要

10.3

闲话

  • 返校后分别去宿舍和教室收拾了下东西,然后就去机房了。
  • 详见 2024 CSP-S 游记 10.3
  • 临近 \(18:00\)\(huge\) 突然进来跟我们说他跟高二的说让下去活动 \(20 \min\) ,问我们想不想下去,但是只能在科教馆附近(至远到超市或菜园子),遂跟着下去了。在楼下转悠的过程中看见了 @chancelong ,和其交流过程中得知了他们自愿留校的事情(我们初三的时候也有这个政策,但学生没有执行下去),说今年我们和高二的怎么说也得超过他们那一届,不然再过几年 \(HZOI\) 就真的完蛋了;然后他问了下今年零基础生奥的人数,说今年 \(HZ\) 生奥考得也不咋样,学校肯定得想办法把成绩拉回去,信奥估计就没这个福气了;我又问了下为啥没在运动会彩排及开幕式上看见他,于是便有了 【闲话】高一上运动会 中的片段;他走之前跟我说他假期要写魔怔闲话,让我一定要看。
  • \(huge\) 说手机等人齐了再收,但晚上临下课时 \(miaomiao\) 也没收(估计是以为 \(huge\) 收了?)。
  • 晚上 @wang54321 “操纵” @xrlong 刷博客园的评论,被 \(huge\) 全程窥屏并关机了,然后 \(huge\) 就进来 \(D\) 我们,说我们想刷论坛回家刷去,刚才让下去活动已经很通融了,怎么现在状态还怎么差,并声称他刚才关了两个人的电脑,但最后都没找到了另一个被关机的人。
  • 晚休 \(huge\) 查宿。

做题纪要

luogu P3241 [HNOI2015] 开店

  • 不难发现两个点在点分树上的 \(\operatorname{LCA}\) 是一个求距离的好的分割点,考虑点分树。

  • 暂且不考虑 \([l,r]\) 的限制,因为只是一个限制范围的查找。

  • \(siz_{x}\) 表示点分树上以 \(x\) 为根的子树大小, \(sum_{0/1,x}\) 表示点分数上以 \(x\) 为根的子树内的所有节点到 \(x\) / \(x\) 在点分树上的父亲节点的距离和。

  • 设当前开店节点为 \(x\) ,在点分树上跳到了 \(rt\)

  • 仍考虑枚举 \(\operatorname{LCA}\) ,除去 \(rt\) 方向上的贡献,此时对答案产生的贡献为 \(sum_{0,fa_{rt}}-sum_{1,rt}+(siz_{fa_{rt}}-siz_{rt}) \times dis_{x,fa_{rt}}\)

  • 对于年龄 \(\in [l,r]\) 的限制,套一个支持单点插入、区间查询的数据结构即可。

  • 离散化后使用动态开点线段树貌似空间还是会爆炸,又因为没有修改操作,所以使用 vector 代替即可。具体地,原区间查询距离和转化为二分端点后的两个前缀和相减,原查询大小和转化为二分端点后两个端点相减。

    点击查看代码
    struct node
    {
    	ll nxt,to,w;
    }e[300010];
    ll head[300010],a[300010],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;
    }
    struct LCA
    {
    	ll siz[300010],fa[300010],dep[300010],son[300010],top[300010],dis[300010];
    	void init()
    	{
    		dfs1(1,0);
    		dfs2(1,1);
    	}
    	void dfs1(ll x,ll father)
    	{
    		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)
    	{
    		return dis[x]+dis[y]-2*dis[lca(x,y)];
    	}
    }L;
    struct SMT
    {
    	struct quality
    	{
    		ll val,sum;
    		bool operator < (const quality &another) const
    		{
    			return val<another.val;
    		}
    	};
    	vector<quality>e[300010];
    	void update(ll x,ll val,ll sum)
    	{
    		e[x].push_back((quality){val,sum});
    	}
    	void init(ll n)
    	{
    		for(ll i=1;i<=n;i++)
    		{
    			sort(e[i].begin(),e[i].end());
    			for(ll j=1;j<e[i].size();j++)
    			{
    				e[i][j].sum+=e[i][j-1].sum;
    			}
    		}
    	}
    	ll query_sum(ll x,ll l,ll r)
    	{
    		l=lower_bound(e[x].begin(),e[x].end(),(quality){l,0})-e[x].begin();
    		r=upper_bound(e[x].begin(),e[x].end(),(quality){r,0})-e[x].begin()-1;
    		ll ans=0;
    		if(0<=r&&r<e[x].size())
    		{
    			ans+=e[x][r].sum;
    		}
    		if(0<=l-1&&l-1<e[x].size())
    		{
    			ans-=e[x][l-1].sum;
    		}
    		return ans;
    	}
    	ll query_siz(ll x,ll l,ll r)
    	{
    		l=lower_bound(e[x].begin(),e[x].end(),(quality){l,0})-e[x].begin();
    		r=upper_bound(e[x].begin(),e[x].end(),(quality){r,0})-e[x].begin()-1;
    		return r-(l-1);
    	}
    }T[2];
    struct Divide_On_Tree
    {
    	ll siz[300010],weight[300010],vis[300010],fa[300010],center=0;
    	void init(ll n)
    	{
    		center=0;
    		get_center(1,0,n);
    		get_siz(center,0);
    		build(center);
    	}	
    	void get_center(ll x,ll fa,ll n)
    	{
    		siz[x]=1;
    		weight[x]=0;
    		for(ll 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(ll x,ll fa)
    	{
    		siz[x]=1;
    		for(ll 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(ll x)
    	{
    		vis[x]=1;
    		for(ll 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);
    				fa[center]=x;
    				build(center);
    			}
    		}
    	}
    	void update(ll x)
    	{
    		T[0].update(x,a[x],0);
    		for(ll rt=x;fa[rt]!=0;rt=fa[rt])
    		{
    			T[0].update(fa[rt],a[x],L.get_dis(fa[rt],x));
    			T[1].update(rt,a[x],L.get_dis(fa[rt],x));
    		}
    	}
    	ll query(ll x,ll l,ll r)
    	{
    		ll ans=T[0].query_sum(x,l,r);
    		for(ll rt=x;fa[rt]!=0;rt=fa[rt])
    		{
    			ans+=T[0].query_sum(fa[rt],l,r)-T[1].query_sum(rt,l,r);
    			ans+=(T[0].query_siz(fa[rt],l,r)-T[0].query_siz(rt,l,r))*L.get_dis(fa[rt],x);
    		}
    		return ans;
    	}
    }D;
    int main()
    {
    	ll n,m,mod,u,v,w,x,l,r,ans=0,i;
    	cin>>n>>m>>mod;
    	for(i=1;i<=n;i++)
    	{
    		cin>>a[i];
    	}
    	for(i=1;i<=n-1;i++)
    	{
    		cin>>u>>v>>w;
    		add(u,v,w);
    		add(v,u,w);
    	}
    	L.init();
    	D.init(n);
    	for(i=1;i<=n;i++)
    	{
    		D.update(i);
    	}
    	T[0].init(n);
    	T[1].init(n);
    	for(i=1;i<=m;i++)
    	{
    		cin>>x>>l>>r;
    		l=(l+ans)%mod;
    		r=(r+ans)%mod;
    		if(l>r)
    		{
    			swap(l,r);
    		}
    		ans=D.query(x,l,r);
    		cout<<ans<<endl;
    	}
    	return 0;
    }
    

10.4

闲话

  • 上午 \(7:40 \sim 11:40\) \(miaomiao\) 安排了一场模拟赛。
  • 到中午吃饭的时候手机还是没收。
  • 下午 @HANGRY_sol 在手机充电过程中忘关机了,然后突然就有电话打了过来,接着就被 \(miaomiao\) 发现了,于是 \(miaomiao\) 就想起来我们手机还没收。交手机的时候问 \(miaomiao\) 笔记本电脑用不用交,他说放电脑机箱旁边就行,遂没交电脑。
  • 吃晚饭的时候发现西藏班的进校了。
  • 晚上化奥教练查宿。

做题纪要

luogu P4211 [LNOI2014] LCA

  • 考虑点分树。

  • 对于点分树上经过分治中心 \(x\) 的路径 \(u \to v\) ,若 \(\operatorname{LCA}(x,u)=x\)\(\operatorname{LCA}(u,v)\) 一定在 \(x \to v\) 的路径上,处理出每个点与分治中心的 \(\operatorname{LCA}\) 的深度做前缀和即可;否则 \(\operatorname{LCA}(u,v)\) 一定在 \(u \to x\) 的路径上,二分统计合法点的数量即可。

    点击查看代码
    const ll p=201314;
    struct node
    {
    	ll nxt,to;
    }e[100010];
    ll head[100010],cnt=0;
    void add(ll u,ll v)
    {
    	cnt++;
    	e[cnt].nxt=head[u];
    	e[cnt].to=v;
    	head[u]=cnt;
    }
    struct LCA
    {
    	ll siz[100010],fa[100010],dep[100010],son[100010],top[100010];
    	void init()
    	{
    		dfs1(1,0);
    		dfs2(1,1);
    	}
    	void dfs1(ll x,ll father)
    	{
    		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)
    			{
    				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)
    	{
    		return dep[x]+dep[y]-2*dep[lca(x,y)];
    	}
    }L;
    struct SMT
    {
    	struct quality
    	{
    		ll val,sum;
    		bool operator < (const quality &another) const
    		{
    			return val<another.val;
    		}
    	};
    	vector<quality>e[100010];
    	void update(ll x,ll val,ll sum)
    	{
    		e[x].push_back((quality){val,sum});
    	}
    	void init(ll n)
    	{
    		for(ll i=1;i<=n;i++)
    		{
    			sort(e[i].begin(),e[i].end());
    			for(ll j=1;j<e[i].size();j++)
    			{
    				e[i][j].sum+=e[i][j-1].sum;
    			}
    		}
    	}
    	ll query_sum(ll x,ll l,ll r)
    	{
    		l=lower_bound(e[x].begin(),e[x].end(),(quality){l,0})-e[x].begin();
    		r=upper_bound(e[x].begin(),e[x].end(),(quality){r,0})-e[x].begin()-1;
    		ll ans=0;
    		if(0<=r&&r<e[x].size())
    		{
    			ans+=e[x][r].sum;
    		}
    		if(0<=l-1&&l-1<e[x].size())
    		{
    			ans-=e[x][l-1].sum;
    		}
    		return ans;
    	}
    	ll query_siz(ll x,ll l,ll r)
    	{
    		l=lower_bound(e[x].begin(),e[x].end(),(quality){l,0})-e[x].begin();
    		r=upper_bound(e[x].begin(),e[x].end(),(quality){r,0})-e[x].begin()-1;
    		return r-(l-1);
    	}
    }T[2];
    struct Divide_On_Tree
    {
    	ll siz[100010],weight[100010],vis[100010],fa[100010],center=0;
    	void init(ll n)
    	{
    		center=0;
    		get_center(1,0,n);
    		get_siz(center,0);
    		build(center);
    	}	
    	void get_center(ll x,ll fa,ll n)
    	{
    		siz[x]=1;
    		weight[x]=0;
    		for(ll 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(ll x,ll fa)
    	{
    		siz[x]=1;
    		for(ll 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(ll x)
    	{
    		vis[x]=1;
    		for(ll 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);
    				fa[center]=x;
    				build(center);
    			}
    		}
    	}
    	void update(ll x)
    	{
    		T[0].update(x,x,L.dep[x]);
    		for(ll rt=x;fa[rt]!=0;rt=fa[rt])
    		{
    			T[0].update(fa[rt],x,L.dep[L.lca(fa[rt],x)]);
    			T[1].update(rt,x,L.dep[L.lca(fa[rt],x)]);
    		}
    	}
    	ll query(ll x,ll l,ll r)
    	{
    		ll ans=T[0].query_sum(x,l,r);
    		for(ll rt=x;fa[rt]!=0;rt=fa[rt])
    		{
    			if(L.lca(x,fa[rt])==fa[rt])
    			{	
    				ans=(ans+T[0].query_sum(fa[rt],l,r)-T[1].query_sum(rt,l,r))%p;
    			}
    			else
    			{
    				ans=(ans+L.dep[L.lca(x,fa[rt])]*(T[0].query_siz(fa[rt],l,r)-T[0].query_siz(rt,l,r))%p)%p;
    			}
    		}
    		return ans;
    	}
    }D;
    int main()
    {
    	ll n,m,u,v,x,l,r,i;
    	cin>>n>>m;
    	for(i=2;i<=n;i++)
    	{
    		cin>>u;
    		u++;
    		v=i;
    		add(u,v);
    		add(v,u);
    	}
    	L.init();
    	D.init(n);
    	for(i=1;i<=n;i++)
    	{
    		D.update(i);
    	}
    	T[0].init(n);
    	T[1].init(n);
    	for(i=1;i<=m;i++)
    	{
    		cin>>l>>r>>x;
    		l++;
    		r++;
    		x++;
    		cout<<D.query(x,l,r)<<endl;
    	}
    	return 0;
    }
    
  • \(dis_{u,v}=dis_{rt,u}+dis_{rt,v}-2*dis_{rt,\operatorname{LCA}(u,v)}\) 移项得 \(dis_{rt,\operatorname{LCA}(u,v)}=\frac{dis_{rt,u}+dis_{rt,v}-dis_{u,v}}{2}\) ,接着做法同 luogu P3241 [HNOI2015] 开店

    点击查看代码
    const ll p=201314;
    struct node
    {
    	ll nxt,to;
    }e[100010];
    ll head[100010],sum[100010],cnt=0;
    void add(ll u,ll v)
    {
    	cnt++;
    	e[cnt].nxt=head[u];
    	e[cnt].to=v;
    	head[u]=cnt;
    }
    struct LCA
    {
    	ll siz[100010],fa[100010],dep[100010],son[100010],top[100010];
    	void init()
    	{
    		dfs1(1,0);
    		dfs2(1,1);
    	}
    	void dfs1(ll x,ll father)
    	{
    		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)
    			{
    				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)
    	{
    		return dep[x]+dep[y]-2*dep[lca(x,y)];
    	}
    }L;
    struct SMT
    {
    	struct quality
    	{
    		ll val,sum;
    		bool operator < (const quality &another) const
    		{
    			return val<another.val;
    		}
    	};
    	vector<quality>e[100010];
    	void update(ll x,ll val,ll sum)
    	{
    		e[x].push_back((quality){val,sum});
    	}
    	void init(ll n)
    	{
    		for(ll i=1;i<=n;i++)
    		{
    			sort(e[i].begin(),e[i].end());
    			for(ll j=1;j<e[i].size();j++)
    			{
    				e[i][j].sum+=e[i][j-1].sum;
    			}
    		}
    	}
    	ll query_sum(ll x,ll l,ll r)
    	{
    		l=lower_bound(e[x].begin(),e[x].end(),(quality){l,0})-e[x].begin();
    		r=upper_bound(e[x].begin(),e[x].end(),(quality){r,0})-e[x].begin()-1;
    		ll ans=0;
    		if(0<=r&&r<e[x].size())
    		{
    			ans+=e[x][r].sum;
    		}
    		if(0<=l-1&&l-1<e[x].size())
    		{
    			ans-=e[x][l-1].sum;
    		}
    		return ans;
    	}
    	ll query_siz(ll x,ll l,ll r)
    	{
    		l=lower_bound(e[x].begin(),e[x].end(),(quality){l,0})-e[x].begin();
    		r=upper_bound(e[x].begin(),e[x].end(),(quality){r,0})-e[x].begin()-1;
    		return r-(l-1);
    	}
    }T[2];
    struct Divide_On_Tree
    {
    	ll siz[100010],weight[100010],vis[100010],fa[100010],center=0;
    	void init(ll n)
    	{
    		center=0;
    		get_center(1,0,n);
    		get_siz(center,0);
    		build(center);
    	}	
    	void get_center(ll x,ll fa,ll n)
    	{
    		siz[x]=1;
    		weight[x]=0;
    		for(ll 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(ll x,ll fa)
    	{
    		siz[x]=1;
    		for(ll 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(ll x)
    	{
    		vis[x]=1;
    		for(ll 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);
    				fa[center]=x;
    				build(center);
    			}
    		}
    	}
    	void update(ll x)
    	{
    		T[0].update(x,x,0);
    		for(ll rt=x;fa[rt]!=0;rt=fa[rt])
    		{
    			T[0].update(fa[rt],x,L.get_dis(fa[rt],x));
    			T[1].update(rt,x,L.get_dis(fa[rt],x));
    		}
    	}
    	ll query(ll x,ll l,ll r)
    	{
    		ll ans=T[0].query_sum(x,l,r);
    		for(ll rt=x;fa[rt]!=0;rt=fa[rt])
    		{
    			ans+=T[0].query_sum(fa[rt],l,r)-T[1].query_sum(rt,l,r);
    			ans+=(T[0].query_siz(fa[rt],l,r)-T[0].query_siz(rt,l,r))*L.get_dis(fa[rt],x);
    		}
    		return (L.dep[x]*(r-l+1)+sum[r]-sum[l-1]-ans)/2%p;
    	}
    }D;
    int main()
    {
    	ll n,m,u,v,x,l,r,i;
    	cin>>n>>m;
    	for(i=2;i<=n;i++)
    	{
    		cin>>u;
    		u++;
    		v=i;
    		add(u,v);
    		add(v,u);
    	}
    	L.init();
    	D.init(n);
    	for(i=1;i<=n;i++)
    	{
    		sum[i]=sum[i-1]+L.dep[i];
    		D.update(i);
    	}
    	T[0].init(n);
    	T[1].init(n);
    	for(i=1;i<=m;i++)
    	{
    		cin>>l>>r>>x;
    		l++;
    		r++;
    		x++;
    		cout<<D.query(x,l,r)<<endl;
    	}
    	return 0;
    }
    

P294. 挤压

P295. 工地难题

P296. 星空遗迹

10.5

闲话

  • \(miaomiao\) 说今天没有模拟赛,所以做 \(Vjudge\) 专题。
  • 中午吃饭的时候遇到了 @xuany | @midsu ,并与 @xrlong@wkh2008 探讨他是 @xuany 还是 @midsu 还是 @Rolling_star
  • 吃完午饭回宿舍的路上看见德育主任和生物老师(貌似是某班班主任加年级干事)进高二宿舍楼了,可能是因为西藏班的也住在里面(?)。
  • 午休物理和数学教练查宿。
  • 晚上临下课的时候 \(feifei\) 进来跟我们说明天早上有体活,但是我现在忘了他说让几点到机房。
  • 晚休 \(field\) 查宿(甚至还在下午问我们宿舍在哪里,然后就透露了学校那边让教练查宿的情况)。

做题纪要

luogu P6492 [COCI2010-2011#6] STEP

luogu P7735 [NOI2021] 轻重边

CF718C Sasha and Array

CF19D Points

CF946G Almost Increasing Array

[ABC374A] Takahashi san 2

  • 基础字符串。

    点击查看代码
    int main()
    {
    	int n;
    	char s[50];
    	scanf("%s",s+1);
    	n=strlen(s+1);
    	if(s[n-2]=='s'&&s[n-1]=='a'&&s[n]=='n')
    	{
    		cout<<"Yes"<<endl;
    	}
    	else
    	{
    		cout<<"No"<<endl;
    	}
    	return 0;
    }	
    

[ABC374B] Unvarnished Report

  • 循环结构。

    点击查看代码
    char s[150],t[150];
    int main()
    {
    	int n,m,i,ans=0;
    	scanf("%s%s",s+1,t+1);
    	n=strlen(s+1);
    	m=strlen(t+1);
    	for(i=1;i<=max(n,m);i++)
    	{
    		if(s[i]!=t[i])
    		{
    			ans=i;
    			break;
    		}
    	}
    	cout<<ans<<endl;
    	return 0;
    }
    

[ABC374C] Separated Lunch

  • 超大背包但不需要 \(meet in middle\)

    点击查看代码
    ll a[20],ans=0x7f7f7f7f;
    void dfs(ll pos,ll n,ll sum1,ll sum2)
    {
    	if(pos==n+1)
    	{
    		ans=min(ans,max(sum1,sum2));
    	}
    	else
    	{
    		dfs(pos+1,n,sum1+a[pos],sum2);
    		dfs(pos+1,n,sum1,sum2+a[pos]);
    	}
    }
    int main()
    {
    	ll n,i;
    	cin>>n;
    	for(i=1;i<=n;i++)
    	{
    		cin>>a[i];
    	}
    	dfs(1,n,0,0);
    	cout<<ans<<endl;
    	return 0;
    }
    

[ABC374D] Laser Marking

  • 模拟。

    点击查看代码
    int a[10],b[10],c[10],d[10],vis[20],p[20];
    double s,t,ans=0x7f7f7f7f;
    double work(double x1,double y1,double x2,double y2)
    {
    	return sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1));
    }
    int x(int p)
    {
    	return p%2==0?a[p/2+1]:c[p/2+1];
    }
    int y(int p)
    {
    	return p%2==0?b[p/2+1]:d[p/2+1];
    }
    void dfs(int pos,int n,int pre)
    {
    	if(pos==2*n)
    	{
    		double sum=work(0,0,x(p[1]),y(p[1]))/s;
    		for(int i=2;i<=2*n;i++)
    		{
    			if(i%2==1)
    			{
    				sum+=work(x(p[i-1]),y(p[i-1]),x(p[i]),y(p[i]))/s;
    			}
    			else
    			{
    				sum+=work(x(p[i-1]),y(p[i-1]),x(p[i]),y(p[i]))/t;
    			}
    		}
    		ans=min(ans,sum);
    		return;
    	}
    	else
    	{
    		if(vis[pre^1]==1)
    		{
    			for(int i=0;i<=2*n-1;i++)
    			{
    				if(vis[i]==0)
    				{
    					vis[i]=1;
    					p[pos+1]=i;
    					dfs(pos+1,n,i);
    					vis[i]=0;
    				}
    			}
    		}
    		else
    		{
    			vis[pre^1]=1;
    			p[pos+1]=pre^1;
    			dfs(pos+1,n,pre^1);
    			vis[pre^1]=0;
    		}
    	}
    }
    int main()
    {
    	int n,i;
    	cin>>n>>s>>t;
    	for(i=1;i<=n;i++)
    	{
    		cin>>a[i]>>b[i]>>c[i]>>d[i];
    	}
    	for(i=0;i<=2*n-1;i++)
    	{
    		p[1]=i;
    		vis[i]=1;
    		dfs(1,n,i);
    		vis[i]=0;
    	}
    	printf("%.8lf\n",ans);
    	return 0;
    }
    

10.6

闲话

  • 因为忘了 \(feifei\) 说早上几点到机房,所以起床后直接去机房了。
  • 上午 \(7:40 \sim 12:10\) 打 accoders NOI 的 \(NOIP2024A\) 层模拟赛, \(8:00 \sim 12:00\) 打学校模拟赛。中途去厕所的路上看见 \(huge\)\(feifei\) 已经把 \(CSP-J/S\) 第二轮的倒计时展牌贴在了墙上,还听见他们在吐槽学校的做工劣质。
  • 吃午饭的时候遇见 @xuany@chancelong 了。
  • 下午因为有 Codeforces Round 977 (Div. 2, based on COMPFEST 16 - Final Round) ,所以 \(huge\) 进来问我们有没有打的,比赛结束后组织了对 \(C2\) 的讲题。
  • 晚上讲学校模拟赛的中途被拉去讲 accoders NOI 的 \(NOIP2024A\) 层模拟赛(因为上午交题交太快了,吃晚饭时被 \(field\) 叫住要求讲 \(T1,T2\) ),把讲 \(T1\) 的任务交给 @wlesq 了,我只讲了 \(T2\) 。然后还有下午 \(CF\) 的讲题,但鉴于我们基本没打所以没去听。
  • 晚休数奥和化奥教练查宿。

做题纪要

CF193D Two Segments

T1075. 邻面合并

10.7

闲话

  • 上午 \(7:40 \sim 11:40\) 打 accoders NOI 的 \(NOIP2024A\) 层模拟赛。
  • 高一剩下的学生上午都返校了,国庆假期集训结束了。
  • 临吃午饭的时候 \(miaomiao\) 跟我们说接下来就开始停课集训了,早上跑完操就直接来机房,原语文/英语早读改成了奥赛早预备(可能 \(huge\)\(feifei\) 哪天想起来就让我们上一次早读);现在就先去教室收拾下书桌,不然后面考试布置考场的时候就没时间收拾了;发的资料晚新闻我们出一个代表去班里拿,并分发给众人,保证每人都拿到自己的资料,别像上一届一样把资料拿回来就堆在机房,现在还在各处找这些资料。遂去教室把书包放到了教室里。

做题纪要

T1076. 光线追踪

P343. 五彩斑斓(colorful)

P344. 错峰旅行(travel)

P346. 量子隧穿问题(experiment)

luogu P2502 [HAOI2006] 旅行

10.8

闲话

  • 侯操
    • 听隔壁 \(1\) 部年级主任爆典。
      • “特长生不是特权生,同样需要遵守学校的规章制度,不允许携带行李箱进校。”
        • 上次我们还被德育主任 \(D\) “某些奥赛班的行李箱太多了”。
      • “特长生的手机等电子产品一律上交班主任保管。”
        • 特长生训练时一般是随身携带手机、耳机的,方便和教练实时联系。初二的时候好像也整过这个规定,但在下一次放假回来之后就得不到丁点儿的落实。
    • 然后是 \(2\) 部颁发流动红旗,以前起码还是发奖状呢(尽管都没写班号),或许真是应了英语老师说的现在 \(HZ\) 各校区都在能节约就节约,现在已经免掉了为实验班学生翻印的教辅,语文、英语习字本印刷两次间的间隔也比之前长了不少。
  • 早上跟着高二的一起去吃饭了(提前了 \(5 \min\) ),回到机房后就被告知以后每天上午 \(7:30\) 开打模拟赛,那提前下去吃饭就“合法”了。
  • 上午 \(7:30 \sim 11:30\) 打学校模拟赛。
  • 中午不管 \(feifei\) 跟高二说的 \(12:17\) 去吃饭,直接 \(12:00\) 去吃饭。
  • 下午到机房后放 \(huge\) 的每日一歌《蜂鸟》,然后开始讲题。
  • 晚上 \(feifei\) 因我们 闲聊 讨论时间过长、声音过大,说他给隔壁已经实行了铃声分辨是否是讨论时间,我们这个机房之所以不放是因为我们比较听话,而且人比较少(实际上估计是因为我们已经适应了初中 \(bobo\) 散养的管理模式,突然接受 \(huge\) 的管理可能不太适应),让我们在讨论时间再去讨论。

做题纪要

luogu P5494 【模板】线段树分裂

  • 线段树分裂板子。

    • 线段树分裂实质上是线段树合并的逆过程。
    • 线段树分裂只能维护有序的序列,常用于维护连续序列或动态权值线段树。
    • 设要把一颗区间为 \([1,n]\) 的线段树分裂出 \([x,y]\) ,并新建一棵树。
      • 从根节点开始递归分裂,当节点不存在时直接返回。
      • \([l,r]\)\([x,y]\) 有交集时新建节点(非严格意义新建)。
      • \([x,y]\) 包含于 \([l,r]\) ,将当前节点连到新的树下面,并断掉这条旧边。
      • 递归分裂左右子树后进行 pushup
    • 单次分裂的时间复杂度为 \(O(\log n)\) 。空间复杂度为 \(O(m \log n)\)
    • 当线段树分裂和合并同时存在时删除节点时需要清空其左右儿子及其他额外信息,且空间时间复杂度带 \(2\) 倍常数。
  • 本题中因空间略小需要回收垃圾节点。

    点击查看代码
    ll a[200010];
    struct SMT
    {
    	ll root[200010],rt_sum;
    	stack<ll>s;
    	struct SegmentTree
    	{
    		ll ls,rs,sum;
    	}tree[200010<<5];
    	#define lson(rt) (tree[rt].ls)
    	#define rson(rt) (tree[rt].rs)
    	ll build_rt()
    	{
    		ll rt;
    		if(s.empty()==0)
    		{
    			rt=s.top();
    			s.pop();
    		}
    		else
    		{
    			rt_sum++;
    			rt=rt_sum;
    		}
    		lson(rt)=rson(rt)=tree[rt].sum=0;
    		return rt;
    	}
    	void del_rt(ll &rt)
    	{
    		lson(rt)=rson(rt)=tree[rt].sum=0;
    		s.push(rt);
    		rt=0;//清空集合(本题中因为保证 t 不会再出现所以这一行可以省略)
    	}
    	void pushup(ll rt)
    	{
    		tree[rt].sum=tree[lson(rt)].sum+tree[rson(rt)].sum;
    	}
    	void build(ll &rt,ll l,ll r)
    	{
    		rt=build_rt();
    		if(l==r)
    		{
    			tree[rt].sum=a[l];
    			return;
    		}
    		ll mid=(l+r)/2;
    		build(lson(rt),l,mid);
    		build(rson(rt),mid+1,r);
    		pushup(rt);
    	}
    	void update(ll &rt,ll l,ll r,ll pos,ll val)
    	{
    		rt=(rt==0)?build_rt():rt;
    		if(l==r)
    		{
    			tree[rt].sum+=val;
    			return;
    		}
    		ll 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);
    	}
    	void merge(ll &rt1,ll &rt2,ll l,ll r)
    	{
    		if(rt1==0||rt2==0)
    		{
    			rt1+=rt2;//强制删掉 rt2 
    			return;
    		}
    		if(l==r)
    		{
    			tree[rt1].sum+=tree[rt2].sum;
    			del_rt(rt2);
    			return;
    		}
    		ll mid=(l+r)/2;
    		merge(lson(rt1),lson(rt2),l,mid);
    		merge(rson(rt1),rson(rt2),mid+1,r);
    		del_rt(rt2);
    		pushup(rt1);
    	}   
    	void split(ll &rt1,ll &rt2,ll l,ll r,ll x,ll y)
    	{
    		if(rt1==0)
    		{
    			return;
    		}
    		rt2=(rt2==0)?build_rt():rt2;
    		if(x<=l&&r<=y)
    		{
    			tree[rt2]=tree[rt1];//连接到新树上
    			rt1=0;//断掉这条旧边
    			return;
    		}
    		ll mid=(l+r)/2;
    		if(x<=mid)
    		{
    			split(lson(rt1),lson(rt2),l,mid,x,y);
    		}
    		if(y>mid)
    		{
    			split(rson(rt1),rson(rt2),mid+1,r,x,y);
    		}
    		pushup(rt1);
    		pushup(rt2);
    	}
    	ll query(ll rt,ll l,ll r,ll x,ll y)
    	{
    		if(rt==0)
    		{
    			return 0;
    		}
    		if(x<=l&&r<=y)
    		{
    			return tree[rt].sum;
    		}
    		ll 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;
    	}
    	ll kth_min(ll rt,ll l,ll r,ll k)
    	{
    		if(l==r)
    		{
    			return (tree[rt].sum>=k)?l:-1;
    		}
    		ll mid=(l+r)/2;
    		if(tree[lson(rt)].sum>=k)
    		{
    			return kth_min(lson(rt),l,mid,k);
    		}
    		else
    		{
    			return kth_min(rson(rt),mid+1,r,k-tree[lson(rt)].sum);
    		}
    	}
    }T;
    int main()
    {
    	ll n,m,pos=1,pd,p,x,y,i;
    	cin>>n>>m;
    	for(i=1;i<=n;i++)
    	{
    		cin>>a[i];
    	}
    	T.build(T.root[pos],1,n);
    	for(i=1;i<=m;i++)
    	{
    		cin>>pd;
    		if(pd==0)
    		{
    			cin>>p>>x>>y;
    			pos++;
    			T.split(T.root[p],T.root[pos],1,n,x,y);
    		}
    		if(pd==1)
    		{
    			cin>>p>>x;
    			T.merge(T.root[p],T.root[x],1,n);
    		}
    		if(pd==2)
    		{
    			cin>>p>>x>>y;
    			T.update(T.root[p],1,n,y,x);
    		}
    		if(pd==3)
    		{
    			cin>>p>>x>>y;
    			cout<<T.query(T.root[p],1,n,x,y)<<endl;
    		}
    		if(pd==4)
    		{
    			cin>>p>>x;
    			cout<<T.kth_min(T.root[p],1,n,x)<<endl;
    		}
    	}
    	return 0;
    }
    

T1042. ants

[AGC010C] Cleaning

T1072. 购物

10.9

闲话

  • 侯操时的动员中声称今天是 \(10.8\) 。因昨天早操两个体委都还在外面机构里集训,没有返校,而且把体委服都带回家了,所以实际跑操时的候补体委没有穿体委服,所以今天需要加圈(年级部规定不穿体委服就加圈)。跑完正常的一圈后尝试和班主任、年级干事交涉能不能不跑加的一圈,得到的回复是不能。
  • 上午 \(7:30 \sim 11:30\) 打 accoders NOI 的模拟赛。快 \(10:30\) 时零基础的来上奥赛课,他们遂被赶去 \(miaomiao\) 跟学校申请占用的信息课机房,路过时听见是 \(field\) 在给他们讲课。
  • 下午到机房后放 @HaneDaniko 的每日一歌《Echoism》,然后开始讲题。
  • 下午看见 \(feifei,field,huge\) 突然穿得非常正式,从他们的谈话中得知他们要去开会。
  • 临吃晚饭前 \(huge\) 进来跟我们说明天没有模拟赛,多校的安排是写 图论2(tarjan,2-SAT,欧拉路,竞赛图,图的性质,综合应用) 的题,并在明天晚上组织讨论,今天晚上和明天我们好好把模拟赛改改,另外明天我们要是没有自己的安排就遵照执行多校的安排,但我感觉里面没有一个是可做题,遂打算继续补 数据结构1
  • 晚饭时直接跟着高二的 \(18:13\) 就去吃饭了。

做题纪要

luogu P1010 [NOIP1998 普及组] 幂次方

P356.子串的子串

P357.魔法咒语

P358.表达式

10.10

闲话

  • 侯操时又被通知要求加圈了,班主任尝试和年级老师交涉但没有成功。
  • 到机房后发现隔壁在上早读,过了一会儿 \(huge\) 就进来跟我们说让我们后面也跟着高二的一起上早读吧,不然后面补 \(whk\) 的时候需要背诵的东西太多我们背不完,而且他觉得我们也不是很喜欢需要读读背背的东西。但是我们还没有把书搬来机房,机房仅存资料只有数学新学案、旧数学答题纸、旧语文作文、语文新早读资料、语文新作文学案、语文新综合作业、活页 视野。
  • 准备跟着高二的一起去吃早饭时 \(huge\) 进来问我们几点吃饭,如实回答后并表示要是打模拟赛的话吃饭时间就有点短(指仅有 \(20 \min\) ),然后 \(huge\) 跟我们说让跟着高二的一起去吃饭吧,中途要是遇见值班老师把我们拦下来了,就让我们如实说我们在集训。
  • 上午 \(miaomiao\) 跟我们说班主任说让我们每周回班收拾一趟资料,平常就不用天天出代表回班拿了。过了一会儿, \(miaomiao\) 突然神秘兮兮地进来跟我们说这周六下午第一节课数学录播课需要找人去写代码,问我们有没有想去的,说就是去玩的,接着我们就把 @wang54321 “投”了出去,然后 \(miaomiao\) 就把 @wang54321 带了出去和录播课数学老师交流写什么代码,回来还又问了一遍我们为什么不去玩一节课,代码我猜测估计是二分相关。在 @wang54321 回来后发现猜中了,具体内容是在 \(eps\)\(0.1\) 时以 \(f(x)=2^{x}+3x+7\) 为例的单调函数二分求零点,在 @wang54321 写完代码后就是他和数学老师的交流。
  • \(12:00\) 就直接去吃饭了,出机房后发现 \(huge,feifei\) 就站在外面,经过一番询问后就放我们走了。
  • 下午到机房后放 @lhx 的每日一歌《夜曲》。
  • 晚饭时 \(huge\) 让我们也跟着高二去吃饭。
  • 晚上和多校的一起讨论 图论2(tarjan,2-SAT,欧拉路,竞赛图,图的性质,综合应用) ,听到一半就听不下去了,遂去写题解和闲话了。

做题纪要

luogu P2824 [HEOI2016/TJOI2016] 排序

  • 考虑将每一个数单独插入一棵权值线段树里,珂朵莉树记录一段区间升序/降序。

  • 于是区间排序转化为权值线段树合并、分裂即可。

    点击查看代码
    int a[100010];
    struct SMT
    {
    	int root[100010],rt_sum;
    	struct SegmentTree
    	{
    		int ls,rs,sum;
    	}tree[100010<<6];
    	#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].sum=0;
    		return rt_sum;
    	}
    	void del_rt(int &rt)
    	{
    		lson(rt)=rson(rt)=tree[rt].sum=0;
    		rt=0;
    	}
    	void pushup(int rt)
    	{
    		tree[rt].sum=tree[lson(rt)].sum+tree[rson(rt)].sum;
    	}
    	void update(int &rt,int l,int r,int pos,int val)
    	{
    		rt=(rt==0)?build_rt():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);
    	}
    	void merge(int &rt1,int &rt2,int l,int r)
    	{
    		if(rt1==0||rt2==0)
    		{
    			rt1+=rt2;
    			return;
    		}
    		if(l==r)
    		{
    			tree[rt1].sum+=tree[rt2].sum;
    			del_rt(rt2);
    			return;
    		}
    		int mid=(l+r)/2;
    		merge(lson(rt1),lson(rt2),l,mid);
    		merge(rson(rt1),rson(rt2),mid+1,r);
    		del_rt(rt2);
    		pushup(rt1);
    	}
    	void split(int &rt1,int &rt2,int l,int r,int k,int pd)
    	{
    		if(rt1==0||tree[rt1].sum==k)
    		{
    			return;
    		}
    		rt2=(rt2==0)?build_rt():rt2;
    		tree[rt2].sum=tree[rt1].sum-k;
    		tree[rt1].sum=k;
    		int mid=(l+r)/2;
    		if(pd==1)
    		{
    			if(k<=tree[rson(rt1)].sum)
    			{
    				split(rson(rt1),rson(rt2),mid+1,r,k,pd);
    				lson(rt2)=lson(rt1);
    				lson(rt1)=0;
    			}
    			else
    			{
    				split(lson(rt1),lson(rt2),l,mid,k-tree[rson(rt1)].sum,pd);
    			}
    		}
    		else
    		{
    			if(k<=tree[lson(rt1)].sum)
    			{
    				split(lson(rt1),lson(rt2),l,mid,k,pd);
    				rson(rt2)=rson(rt1);
    				rson(rt1)=0;
    			}
    			else
    			{
    				split(rson(rt1),rson(rt2),mid+1,r,k-tree[lson(rt1)].sum,pd);
    			}
    		}
    		pushup(rt1);
    		pushup(rt2);
    	}
    	int query(int rt,int l,int r,int k)
    	{
    		if(l==r)
    		{
    			return (tree[rt].sum>=k)?l:-1;
    		}
    		int mid=(l+r)/2;
    		if(tree[lson(rt)].sum>=k)
    		{
    			return query(lson(rt),l,mid,k);
    		}
    		else
    		{
    			return query(rson(rt),mid+1,r,k-tree[lson(rt)].sum);
    		}
    	}
    }T;
    struct ODT
    {
    	struct node
    	{
    		int l,r;
    		mutable int col;
    		bool operator < (const node &another) const
    		{
    			return l<another.l;
    		}
    	};
    	set<node>s;
    	void init(int n)
    	{
    		for(int i=1;i<=n;i++)
    		{
    			s.insert((node){i,i,0});
    		}
    	}
    	set<node>::iterator split(int pos,int n)
    	{
    		set<node>::iterator it=s.lower_bound((node){pos,0,0});
    		if(it!=s.end()&&it->l==pos)
    		{
    			return it;
    		}
    		it--;
    		if(it->r<pos)
    		{
    			return s.end();
    		}
    		int l=it->l,r=it->r,col=it->col;
    		s.erase(it);
    		T.split(T.root[l],T.root[pos],1,n,(pos-1)-l+1,col);
    		s.insert((node){l,pos-1,col});
    		return s.insert((node){pos,r,col}).first;
    	}
    	void assign(int l,int r,int col,int n)
    	{
    		set<node>::iterator itr=split(r+1,n),itl=split(l,n);
    		itl++;
    		for(set<node>::iterator it=itl;it!=itr;it++)
    		{
    			T.merge(T.root[l],T.root[it->l],1,n);
    		}
    		itl--;
    		s.erase(itl,itr);
    		s.insert((node){l,r,col});
    	}
    	int query(int pos,int n)
    	{
    		split(pos+1,n);
    		split(pos,n);
    		return T.query(T.root[pos],1,n,1);
    	}
    }O;
    int main()
    {
    	int n,m,pd,l,r,i;
    	cin>>n>>m;
    	for(i=1;i<=n;i++)
    	{
    		cin>>a[i];
    		T.update(T.root[i],1,n,a[i],1);
    	}
    	O.init(n);
    	for(i=1;i<=m;i++)
    	{
    		cin>>pd>>l>>r;
    		O.assign(l,r,pd,n);
    	}
    	cin>>l;
    	cout<<O.query(l,n)<<endl;
    	return 0;
    }
    

CF558E A Simple Task

  • 将字符离散成 \([1,n]\) 的排列,然后就和 luogu P2824 [HEOI2016/TJOI2016] 排序 一样了。

    点击查看代码
    pair<char,int>a[100010],b[100010];
    struct SMT
    {
    	int root[100010],rt_sum;
    	struct SegmentTree
    	{
    		int ls,rs,sum;
    	}tree[100010<<6];
    	#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].sum=0;
    		return rt_sum;
    	}
    	void del_rt(int &rt)
    	{
    		lson(rt)=rson(rt)=tree[rt].sum=0;
    		rt=0;
    	}
    	void pushup(int rt)
    	{
    		tree[rt].sum=tree[lson(rt)].sum+tree[rson(rt)].sum;
    	}
    	void update(int &rt,int l,int r,int pos,int val)
    	{
    		rt=(rt==0)?build_rt():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);
    	}
    	void merge(int &rt1,int &rt2,int l,int r)
    	{
    		if(rt1==0||rt2==0)
    		{
    			rt1+=rt2;
    			return;
    		}
    		if(l==r)
    		{
    			tree[rt1].sum+=tree[rt2].sum;
    			del_rt(rt2);
    			return;
    		}
    		int mid=(l+r)/2;
    		merge(lson(rt1),lson(rt2),l,mid);
    		merge(rson(rt1),rson(rt2),mid+1,r);
    		del_rt(rt2);
    		pushup(rt1);
    	}
    	void split(int &rt1,int &rt2,int l,int r,int k,int pd)
    	{
    		if(rt1==0||tree[rt1].sum==k)
    		{
    			return;
    		}
    		rt2=(rt2==0)?build_rt():rt2;
    		tree[rt2].sum=tree[rt1].sum-k;
    		tree[rt1].sum=k;
    		int mid=(l+r)/2;
    		if(pd==0)
    		{
    			if(k<=tree[rson(rt1)].sum)
    			{
    				split(rson(rt1),rson(rt2),mid+1,r,k,pd);
    				lson(rt2)=lson(rt1);
    				lson(rt1)=0;
    			}
    			else
    			{
    				split(lson(rt1),lson(rt2),l,mid,k-tree[rson(rt1)].sum,pd);
    			}
    		}
    		else
    		{
    			if(k<=tree[lson(rt1)].sum)
    			{
    				split(lson(rt1),lson(rt2),l,mid,k,pd);
    				rson(rt2)=rson(rt1);
    				rson(rt1)=0;
    			}
    			else
    			{
    				split(rson(rt1),rson(rt2),mid+1,r,k-tree[lson(rt1)].sum,pd);
    			}
    		}
    		pushup(rt1);
    		pushup(rt2);
    	}
    	int query(int rt,int l,int r,int k)
    	{
    		if(l==r)
    		{
    			return (tree[rt].sum>=k)?l:-1;
    		}
    		int mid=(l+r)/2;
    		if(tree[lson(rt)].sum>=k)
    		{
    			return query(lson(rt),l,mid,k);
    		}
    		else
    		{
    			return query(rson(rt),mid+1,r,k-tree[lson(rt)].sum);
    		}
    	}
    }T;
    struct ODT
    {
    	struct node
    	{
    		int l,r;
    		mutable int col;
    		bool operator < (const node &another) const
    		{
    			return l<another.l;
    		}
    	};
    	set<node>a;
    	void init(int n)
    	{
    		for(int i=1;i<=n;i++)
    		{
    			a.insert((node){i,i,0});
    		}
    	}
    	set<node>::iterator split(int pos,int n)
    	{
    		set<node>::iterator it=a.lower_bound((node){pos,0,0});
    		if(it!=a.end()&&it->l==pos)
    		{
    			return it;
    		}
    		it--;
    		if(it->r<pos)
    		{
    			return a.end();
    		}
    		int l=it->l,r=it->r,col=it->col;
    		a.erase(it);
    		T.split(T.root[l],T.root[pos],1,n,(pos-1)-l+1,col);
    		a.insert((node){l,pos-1,col});
    		return a.insert((node){pos,r,col}).first;
    	}
    	void assign(int l,int r,int col,int n)
    	{
    		set<node>::iterator itr=split(r+1,n),itl=split(l,n);
    		itl++;
    		for(set<node>::iterator it=itl;it!=itr;it++)
    		{
    			T.merge(T.root[l],T.root[it->l],1,n);
    		}
    		itl--;
    		a.erase(itl,itr);
    		a.insert((node){l,r,col});
    	}
    }O;
    int main()
    {
    	int n,m,l,r,pd,i;
    	cin>>n>>m;
    	for(i=1;i<=n;i++)
    	{
    		cin>>a[i].first;
    		a[i].second=i;
    		b[i]=a[i];
    	}
    	sort(b+1,b+1+n);
    	b[0].second=unique(b+1,b+1+n)-(b+1);
    	for(i=1;i<=n;i++)
    	{
    		T.update(T.root[i],1,n,lower_bound(b+1,b+1+b[0].second,a[i])-b,1);
    	}
    	O.init(n);
    	for(i=1;i<=m;i++)
    	{
    		cin>>l>>r>>pd;
    		O.assign(l,r,pd,n);
    	}
    	for(i=1;i<=n;i++)
    	{
    		O.split(i,n);
    	}
    	for(i=1;i<=n;i++)
    	{
    		cout<<b[T.query(T.root[i],1,n,1)].first;
    	}
    	return 0;
    }
    

luogu P3812 【模板】线性基

  • 异或线性基板子。

    点击查看代码
    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long 
    #define ull unsigned long long
    #define sort stable_sort 
    #define endl '\n'
    ll d[70];
    void insert(ll x)
    {
    	for(ll i=60;i>=0;i--)
    	{
    		if((x>>i)&1)
    		{
    			if(d[i]==0)
    			{
    				d[i]=x;
    				break;
    			}
    			x^=d[i];
    		}
    	}
    }
    int main()
    {
    	ll n,x,ans=0,i;
    	cin>>n;
    	for(i=1;i<=n;i++)
    	{
    		cin>>x;
    		insert(x);
    	}
    	for(i=60;i>=0;i--)
    	{
    		ans=max(ans,ans^d[i]);
    	}
    	cout<<ans<<endl;
    	return 0;
    }
    /*
    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long 
    #define ull unsigned long long
    #define sort stable_sort 
    #define endl '\n'
    ll x[70],d[70];
    ll gauss(ll n)
    {
    	ll row=1;
    	for(ll i=1;i<=n;i++)
    	{
    		d[i]=x[i];
    	}
    	for(ll col=60;col>=0&&row<=n;col--)
    	{
    		for(ll i=row;i<=n;i++)
    		{
    			if((d[i]>>col)&1)
    			{
    				swap(d[row],d[i]);
    				break;
    			}
    		}
    		if((d[row]>>col)&1)
    		{
    			for(ll i=1;i<=n;i++)
    			{
    				if(i!=row&&((d[i]>>col)&1))
    				{
    					d[i]^=d[row];
    				}
    			}
    			row++;
    		}
    	}
    	row--;
    	return row;
    }
    int main()
    {
    	ll n,row,ans=0,i;
    	cin>>n;
    	for(i=1;i<=n;i++)
    	{
    		cin>>x[i];
    	}
    	row=gauss(n);
    	for(i=1;i<=row;i++)
    	{
    		ans^=d[i];
    	}
    	cout<<ans<<endl;
    	return 0;
    }
    */
    

T3673. 欧几里得的噩梦

luogu P3857 [TJOI2008] 彩灯

  • 把控制彩灯看成 \(2\) 进制下的每一位,等价于询问异或和集合的大小。

  • 设得到的线性基大小为 \(siz\) ,那么 \(2^{siz}\) 即为所求。

    点击查看代码
    const ll p=2008;
    ll d[60],siz=0;
    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;
    }
    void insert(ll x,ll m)
    {
    	for(ll i=m-1;i>=0;i--)
    	{
    		if((x>>i)&1)
    		{
    			if(d[i]==0)
    			{
    				siz++;
    				d[i]=x;
    				break;
    			}
    			x^=d[i];
    		}   
    	}
    }
    int main()
    {
    	ll n,m,x,i,j;
    	char pd;
    	cin>>m>>n;
    	for(i=1;i<=n;i++)
    	{
    		x=0;
    		for(j=0;j<=m-1;j++)
    		{
    			cin>>pd;
    			if(pd=='O')
    			{
    				x|=(1ll<<j);
    			}
    		}
    		insert(x,m);
    	}
    	cout<<qpow(2,siz,p)<<endl;
    	return 0;
    }
    

luogu P4570 [BJWC2011] 元素

  • 将魔力值降序排序后,判断是否能插入线性基,若能插入则统计贡献。

    点击查看代码
    ll d[60];
    pair<ll,ll>a[1010];
    ll insert(ll x)
    {
    	for(ll i=60;i>=0;i--)
    	{
    		if((x>>i)&1)
    		{
    			if(d[i]==0)
    			{
    				d[i]=x;
    				return 1;
    			}
    			x^=d[i];
    		}
    	}
    	return 0;
    }
    int main()
    {
    	ll n,ans=0,i;
    	cin>>n;
    	for(i=1;i<=n;i++)
    	{
    		cin>>a[i].second>>a[i].first;
    	}
    	sort(a+1,a+1+n);
    	for(i=n;i>=1;i--)
    	{
    		ans+=a[i].first*insert(a[i].second);
    	}
    	cout<<ans<<endl;
    	return 0;
    }
    

luogu P4301 [CQOI2013] 新Nim游戏

  • \(Nim\) 游戏必胜当且仅当 \(\bigoplus\limits_{i=1}^{m}a_{i} \ne 0\)

  • 使得第二回合开始时只有线性基里的元素即可,同时也解释了为什么一定有解。

    点击查看代码
    ll a[40],d[40];
    ll insert(ll x)
    {
    	for(ll i=32;i>=0;i--)
    	{
    		if((x>>i)&1)
    		{
    			if(d[i]==0)
    			{
    				d[i]=x;
    				return 0;
    			}
    			x^=d[i];
    		}
    	}
    	return 1;
    }
    int main()
    {
    	ll n,ans=0,i;
    	cin>>n;
    	for(i=1;i<=n;i++)
    	{
    		cin>>a[i];
    	}
    	sort(a+1,a+1+n);
    	for(i=n;i>=1;i--)
    	{
    		ans+=a[i]*insert(a[i]);
    	}
    	cout<<ans<<endl;
    	return 0;
    }
    

luogu P4151 [WC2011] 最大XOR和路径

  • 多倍经验: CF845G Shortest Path Problem?

  • 因为一条边走两遍等价于不走,但扩大了可选择异或的范围。

  • 答案显然由 \(1 \sim n\) 的一条链(主路径)与许多环组成。

  • 主路径随便选一条即可,因为若有其他的主路径,则一定会在异或环的贡献中被计算。

  • 由于线性基的性质,不用特别处理环套环的情况。

    点击查看代码
    struct node
    {
    	ll nxt,to,w;
    }e[200010];
    ll head[200010],sum[200010],vis[200010],d[70],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 insert(ll x)
    {
    	for(ll i=60;i>=0;i--)
    	{
    		if((x>>i)&1)
    		{
    			if(d[i]==0)
    			{
    				d[i]=x;
    				break;
    			}
    			x^=d[i];
    		}
    	}
    }
    void dfs(ll x,ll tmp)
    {
    	vis[x]=1;
    	sum[x]=tmp;
    	for(ll i=head[x];i!=0;i=e[i].nxt)
    	{
    		if(vis[e[i].to]==0)
    		{
    			dfs(e[i].to,tmp^e[i].w);
    		}
    		else
    		{
    			insert(tmp^e[i].w^sum[e[i].to]);
    		}
    	}
    }
    int main()
    {
    	ll n,m,ans,u,v,w,i;
    	cin>>n>>m;
    	for(i=1;i<=m;i++)
    	{
    		cin>>u>>v>>w;
    		add(u,v,w);
    		add(v,u,w);
    	}
    	dfs(1,0);
    	ans=sum[n];
    	for(i=60;i>=0;i--)
    	{
    		ans=max(ans,ans^d[i]);
    	}
    	cout<<ans<<endl;
    	return 0;
    }
    

HDU3949 XOR

  • 通过样例发现在存在插入失败的线性基内,第 \(1\) 小的数是 \(0\) ,而线性基中是不允许有异或和为 \(0\) 的存在。考虑求出线性基的第 \(k-1\) 小的数;否则求出线性基的第 \(k\) 小的数。

  • 令每一个二进制位仅在它这一位的基底上出现,其他位上的基底直接异或掉。

  • \(k\) 二进制分组后取每一位的基底异或起来即可。

    点击查看代码
    ll d[70];
    vector<ll>dd;
    ll insert(ll x)
    {
    	for(ll i=60;i>=0;i--)
    	{
    		if((x>>i)&1)
    		{
    			if(d[i]==0)
    			{
    				d[i]=x;
    				return 0;
    			}
    			x^=d[i];
    		}
    	}
    	return 1;
    }
    ll ask(ll x,ll flag)
    {
    	if(flag==1)
    	{
    		if(x==1)
    		{
    			return 0;
    		}
    		x--;
    	}
    	if(x>(1ll<<dd.size())-1)
    	{
    		return -1;
    	}
    	ll ans=0;
    	for(ll i=0;i<dd.size();i++)
    	{
    		if((x>>i)&1)
    		{
    			ans^=dd[i];
    		}
    	}
    	return ans;
    }
    int main()
    {
    	ll t,n,m,x,flag,i,j,k;
    	cin>>t;
    	for(k=1;k<=t;k++)
    	{
    		memset(d,0,sizeof(d));
    		dd.clear();
    		cin>>n;
    		flag=0;
    		for(i=1;i<=n;i++)
    		{
    			cin>>x;
    			flag|=insert(x);
    		}
    		for(i=0;i<=60;i++)
    		{
    			if(d[i]!=0)
    			{
    				for(j=0;j<=i-1;j++)
    				{
    					if((d[i]>>j)&1)
    					{
    						d[i]^=d[j];
    					}
    				}
    				dd.push_back(d[i]);
    			}
    		}
    		cin>>m;
    		cout<<"Case #"<<k<<":"<<endl;
    		for(i=1;i<=m;i++)
    		{
    			cin>>x;
    			cout<<ask(x,flag)<<endl;
    		}
    	}
    	return 0;
    }
    

luogu P3265 [JLOI2015] 装备购买

  • 实数线性基板子。

  • 仿照异或线性基消去最高位的写法(直接异或实际上是省去了每位都进行异或),实数线性基用初等行变换消去最高位(类似高斯消元的写法)。

  • 注意精度误差和 eps 的选择。

    点击查看代码
    const double eps=1e-5;
    struct node
    {
    	int w;
    	double p[510];
    }a[510];
    int d[510];
    bool cmp(node a,node b)
    {
    	return a.w<b.w;
    }
    int insert(int x,int m)
    {
    	for(int i=m;i>=1;i--)
    	{
    		if(abs(a[x].p[i])>=eps)
    		{
    			if(d[i]==0)
    			{
    				d[i]=x;
    				return 1;
    			}
    			double tmp=a[x].p[i]/a[d[i]].p[i];
    			for(int j=i;j>=1;j--)
    			{
    				a[x].p[j]-=tmp*a[d[i]].p[j];
    			}
    		}
    	}
    	return 0;
    }
    int main()
    {
    	int n,m,siz=0,ans=0,i,j;
    	cin>>n>>m;
    	for(i=1;i<=n;i++)
    	{
    		for(j=1;j<=m;j++)
    		{
    			cin>>a[i].p[j];
    		}
    	}
    	for(i=1;i<=n;i++)
    	{
    		cin>>a[i].w;
    	}
    	sort(a+1,a+1+n,cmp);
    	for(i=1;i<=n;i++)
    	{
    		if(insert(i,m)==1)
    		{
    			siz++;
    			ans+=a[i].w;
    		}
    	}
    	cout<<siz<<" "<<ans<<endl;
    	return 0;
    }
    

[ABC283G] Partial Xor Enumeration

  • 观察到 \(r-l \le 2 \times 10^{5}\) ,和 HDU3949 XOR 一样做即可。

    点击查看代码
    ll d[70];
    vector<ll>dd;
    void insert(ll x)
    {
    	for(ll i=60;i>=0;i--)
    	{
    		if((x>>i)&1)
    		{
    			if(d[i]==0)
    			{
    				d[i]=x;
    				break;
    			}
    			x^=d[i];
    		}
    	}
    }
    ll ask(ll x)
    {
    	x--;
    	ll ans=0;
    	for(ll i=0;i<dd.size();i++)
    	{
    		if((x>>i)&1)
    		{
    			ans^=dd[i];
    		}
    	}
    	return ans;
    }
    int main()
    {
    	ll n,l,r,x,i,j;
    	cin>>n>>l>>r;
    	for(i=1;i<=n;i++)
    	{
    		cin>>x;
    		insert(x);
    	}
    	for(i=0;i<=60;i++)
    	{
    		if(d[i]!=0)
    		{
    			for(j=0;j<=i-1;j++)
    			{
    				if((d[i]>>j)&1)
    				{
    					d[i]^=d[j];
    				}
    			}
    			dd.push_back(d[i]);
    		}
    	}
    	for(i=l;i<=r;i++)
    	{
    		cout<<ask(i)<<" ";
    	}
    	return 0;
    }
    

CF959F Mahmoud and Ehab and yet another xor task

  • 将操作离线下来,并按 \(\{ l \}\) 升序排序,接着顺序插入线性基并处理询问。

  • 对于未成功插入线性基的元素 \(k\) 一定能被线性基内选出若干元素得到。故在 \(x\) 能被表出的情况下,设 \(1 \sim l\) 中成功插入线性基的元素个数为 \(siz\) ,对于剩下 \(l-siz\) 个元素选出若干个总方案数为 \(2^{l-siz}\) ,这 \(2^{l-siz}\) 种方案都存在一种在线性基中选出若干个元素使得异或起来等于 \(x\)

  • \(2^{l-siz}\) 即为所求。

    点击查看代码
    const ll p=1000000007;
    struct node
    {
    	ll l,x,id;
    }q[100010];
    ll a[100010],d[30],ans[100010];
    bool cmp(node a,node b)
    {
    	return a.l<b.l;
    }
    ll insert(ll x)
    {
    	for(ll i=20;i>=0;i--)
    	{
    		if((x>>i)&1)
    		{
    			if(d[i]==0)
    			{
    				d[i]=x;
    				return 1;
    			}
    			x^=d[i];
    		}
    	}
    	return 0;
    }
    bool check(ll x)
    {
    	for(ll i=20;i>=0;i--)
    	{
    		if((x>>i)&1)
    		{
    			if(d[i]==0)
    			{
    				return 0;
    			}
    			x^=d[i];
    		}
    	}
    	return 1;
    }
    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,siz=0,i,j;
    	cin>>n>>m;
    	for(i=1;i<=n;i++)
    	{
    		cin>>a[i];
    	}
    	for(i=1;i<=m;i++)
    	{
    		cin>>q[i].l>>q[i].x;
    		q[i].id=i;
    	}
    	sort(q+1,q+1+m,cmp);
    	for(i=1,j=1;i<=m;i++)
    	{
    		while(j<=q[i].l)
    		{
    			siz+=insert(a[j]);
    			j++;
    		}
    		ans[q[i].id]=(check(q[i].x))*qpow(2,q[i].l-siz,p);
    	}
    	for(i=1;i<=m;i++)
    	{
    		cout<<ans[i]<<endl;
    	}
    	return 0;
    }
    

CF707D Persistent Bookcase

  • 操作树板子。

    • 操作树在一定程度上可以替代可持久化结构,常需要离线下来统一处理询问。
    • 对于非回溯操作,我们发现它都可以从自己的上一个操作转移过来。如果建成一棵树的话,则需要当前操作与上次操作进行连边,顺序处理时从树根到这个节点的路径的操作即可得到询问答案。
    • 对于回溯操作,我们直接和要求回溯到的操作连边,然后就和上面一样处理了。
  • bitset 处理 \(01\) 反转即可。

    点击查看代码
    struct node
    {
    	int nxt,to;
    }e[100010];
    struct ask
    {
    	int pd,x,y,id;  
    }q[100010];
    int head[100010],ans[100010],cnt=0;
    bitset<1010>a[1010],one;
    void add(int u,int v)
    {
    	cnt++;
    	e[cnt].nxt=head[u];
    	e[cnt].to=v;
    	head[u]=cnt;
    }
    void dfs(int x,int sum)
    {
    	bool tmp;
    	if(q[x].pd==1)
    	{
    		tmp=a[q[x].x][q[x].y];
    		sum+=(a[q[x].x][q[x].y]==0);
    		a[q[x].x][q[x].y]=1;
    	}
    	if(q[x].pd==2)
    	{
    		tmp=a[q[x].x][q[x].y];
    		sum-=(a[q[x].x][q[x].y]==1);
    		a[q[x].x][q[x].y]=0;
    	}
    	if(q[x].pd==3)
    	{
    		sum-=a[q[x].x].count();
    		a[q[x].x]^=one;
    		sum+=a[q[x].x].count();
    	}
    	ans[q[x].id]=sum;
    	for(int i=head[x];i!=0;i=e[i].nxt)
    	{
    		dfs(e[i].to,sum);
    	}
    	if(q[x].pd<=2)
    	{
    		a[q[x].x][q[x].y]=tmp;
    	}
    	if(q[x].pd==3)
    	{
    		a[q[x].x]^=one;
    	}
    }
    int main()
    {
    	int n,m,t,i,j;
    	cin>>n>>m>>t;
    	for(i=1;i<=m;i++)
    	{
    		one[i]=1;
    	}
    	for(i=1;i<=t;i++)
    	{
    		cin>>q[i].pd>>q[i].x;
    		q[i].id=i;
    		if(q[i].pd<=2)
    		{
    			cin>>q[i].y;
    		}
    		if(q[i].pd<=3)
    		{
    			add(i-1,i);
    		}
    		else
    		{
    			add(q[i].x,i);
    		}
    	}
    	dfs(0,0);
    	for(i=1;i<=t;i++)
    	{
    		cout<<ans[i]<<endl;
    	}
    	return 0;
    }
    

luogu P7394 「TOCO Round 1」History

posted @ 2024-10-04 06:29  hzoi_Shadow  阅读(93)  评论(10编辑  收藏  举报
扩大
缩小