高一上八月下旬日记

8.21

闲话

  • 上午 \(7:00 \sim 11:10\) \(miaomiao\) 安排了一场模拟赛。
  • 午休颓《平凡的世界》。
  • 下午讲完题给发了苹果。\(miaomiao\) 跟我们说了大概在 \(8.25\) 下午放假;问我们集训了一整个暑假累不累,我们怕像 \(huge\) 一样 坑蒙拐骗 去学 \(whk\) 所以没说啥,只说了还差不多,毕竟去年也集训了一整个暑假;为打破尴尬,我问 \(miaomiao\) 开了学还用军训吗, \(miaomiao\) 说要军训,顺便换换心情、状态。
  • 体活的时候 \(feifei\) 进来问 \(miaomiao\) 为什么我们不去上体活,然后 \(miaomiao\) 跟他说我们不想上体活。
  • 吃完饭的时候看见原初三德育主任了。

做题纪要

luogu P4690 [Ynoi2016] 镜中的昆虫

2022牛客OI赛前集训营-提高组(第四场) A 博弈

[ABC366D] Cuboid Sum Query

luogu P2325 [SCOI2005] 王室联邦

CF1559E Mocha and Stars

[ABC367F] Rearrange Query

  • 排序后匹配等价于元素各出现次数相同。

  • 开桶暴力统计的话空间复杂度会爆炸,发现可以转化成两个多重集的匹配。考虑集合哈希。

  • 异或哈希的话造点重复元素就卡掉了,考虑和哈希,实现原理同异或哈希。中途时刻对 \(2^{64}\) 取模。

    点击查看代码
    ull f[200010],a[200010],b[200010],suma[200010],sumb[200010];
    mt19937_64 rng(random_device{}());
    int main()
    {
    	ull n,m,l1,r1,l2,r2,i;
    	cin>>n>>m;
    	for(i=1;i<=n;i++)
    	{
    		f[i]=rng();
    	}
    	for(i=1;i<=n;i++)
    	{
    		cin>>a[i];
    		suma[i]=suma[i-1]+f[a[i]];
    	}
    	for(i=1;i<=n;i++)
    	{
    		cin>>b[i];
    		sumb[i]=sumb[i-1]+f[b[i]];
    	}
    	for(i=1;i<=m;i++)
    	{
    		cin>>l1>>r1>>l2>>r2;
    		if(suma[r1]-suma[l1-1]==sumb[r2]-sumb[l2-1])
    		{
    			cout<<"Yes"<<endl;
    		}
    		else
    		{
    			cout<<"No"<<endl;
    		}
    	}
    	return 0;
    }
    

8.22

闲话

  • 上午 \(7:00 \sim 11:10\) @H_Kaguya | @KafuuChinocpp 学长安排了一场模拟赛。
  • 中午给家里打电话时得知了周日放假时要把所有行李带回去的消息。午休 \(feifei\) 查宿;颓《平凡的世界》。
  • 下午讲题前 \(miaomiao\) 隔空提醒我们讲题时讲讲部分分,要讲详细点;讲题后发了个梨。讲完题就在听 \(huge\) 给隔壁的说明天放假的事情,夹杂着部分 \(D\) 他们的话,貌似 \(huge\) 对他们打着“回家学奥赛”的旗号回家“疯玩”而不在学校补 \(whk\) 很不满。
  • 然后 \(huge\) 说要报名 \(CSP\) 的事情,把全网开了,光速上 \(w3\) 扒下照片然后又光速上 \(163\) 邮箱拿验证码然后就注册账号了,过了一会儿就看见教练给报好了 \(CSP-S\) 。拿 \(QQ\) 邮箱的需要验证码所以 \(huge\) 把手机发了下来,还对我们没有发手机感到很疑惑,注册完 \(huge\) 还催促我们把手机交上来(看来是数了手机数量)。
  • \(feifei\)\(huge\) 说要不把全网关了,但不知道 \(huge\) 为啥坚持把全网开着,可能是欲擒故纵。
  • 晚上 \(feifei\) 又往 \(ftp\) 里放了“读物”。
  • 晚上临下课的时候 \(miaomiao\) 跟我们说了下隔壁有高二上学期的预科班,讲的都是重点、难点,因一部分人大部分人不想学 \(whk\) 且机房要锁门(但我们不锁)就只好回家了, \(miaomiao\) 称一开始他也是反对补 \(whk\) 的但在反思前几届有一届 \(noip\) 后要准备 \(WC\) 但知识点只学到 \(noip\) 级别然后学生就着急了,和年级部还干了几仗要求听课,拉着“年轻气盛”的他及另外的教练也跟着年级部打了几仗的事情,觉得隔壁屋学生总以为自己的想法比教练、学校的想法更适合自己很不明智,考完 \(noip\) 后的听课时间太短了,不方便快速适应 \(whk\) 生活,不像我们才新高一,时间还多着呢。然后 \(miaomiao\) 说集训期间我们表现也算不错,没有什么特别过分的事情(没发生像初三寒假偷手机的事情),看我们集训也挺累的,加上其他奥赛的高一也是 \(8.25\) 放假,所以让我们 \(8.25\) 下午放假,不要因为隔壁明天放假就心里不痛快等(担心有点多余)。然后他说今天夏令营结束,基本也指望不上高一零基础的了,只能靠我们了。还有明天学校有假期的突击检查,让我们按照在 HS 内务的最高标准来整, \(7:20\) 到机房就行。明天没有模拟赛让修整一天, @CuFeO4 顺势阐明不想打模拟赛了,以前讲的专题都写不完, \(miaomiao\) 称写不完专题是正常现象,以后会给时间写的, \(miaomiao\) 问我们是不是不想打模拟赛了,我们也就嗯了下来, \(miaomiao\) 也就把明、后天的模拟赛毙了,然后给我们展示 \(18,20\) 届模拟赛打得有多么多。
  • 回宿舍的路上听隔壁的说晚上要查“联欢”,教练要跟年级部学 \(4\) 个教练轮流值班查夜聊(联欢的定义原来已经降到夜聊了)。实际上只有 \(feifei\) 来查宿,还巨唐无比。

做题纪要

P236.线性只因

CF1990A Submission Bait

  • 存在一个数的出现次数为奇数时 Alice 必胜。

    点击查看代码
    int cnt[100];
    int main()
    {
    	int t,n,x,flag,i,j;
    	cin>>t;
    	for(j=1;j<=t;j++)
    	{
    		flag=0;
    		memset(cnt,0,sizeof(cnt));
    		cin>>n;
    		for(i=1;i<=n;i++)
    		{
    			cin>>x;
    			cnt[x]++;
    		}
    		for(i=1;i<=n;i++)
    		{
    			flag|=(cnt[i]%2==1);
    		}
    		if(flag==1)
    		{
    			cout<<"YES"<<endl;
    		}
    		else
    		{
    			cout<<"NO"<<endl;
    		}
    	}
    	return 0;
    }
    

P238. 可持久化字符串

P234.金箱子

CF163E e-Government

  • 一次性将所有模式串加入 \(AC\) 自动机,然后处理加入和删除,考虑单次操作对答案的贡献。

  • 因为模式串 \(T\) 在文本串 \(S\) 中出现的次数之和等价于 \(T\)\(S\) 的所有前缀中作为后缀出现的次数之和。这就很和 \(fail\) 树上跳到根节点的性质相符。

  • 加入/删除分别对应末尾标记 \(ed\)\(+1/-1\)

  • 单点修改加路径区间查询可以转化为子树修改加单点查询。

  • 转成 \(DFS\) 序后树状数组维护即可。

    点击查看代码
    struct node
    {
    	int nxt,to;
    }e[1000010];
    int head[1000010],dfn[1000010],out[1000010],del[1000010],tot=0,cnt=0;
    char s[1000010];
    void add(int u,int v)
    {
    	cnt++;
    	e[cnt].nxt=head[u];
    	e[cnt].to=v;
    	head[u]=cnt;
    }
    struct BIT
    {
    	int c[1000010];
    	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]+=val;
    		}
    	}
    	void update(int n,int l,int r,int val)
    	{
    		add(n,l,val);
    		add(n,r+1,-val);
    	}
    	int getsum(int x)
    	{
    		int ans=0;
    		for(int i=x;i>=1;i-=lowbit(i))
    		{
    			ans+=c[i];
    		}
    		return ans;
    	}
    }B;
    struct ACM
    {
    	int ed[1000010],fail[1000010],rt_sum;
    	struct trie
    	{
    		int ch[27];
    	}tree[1000010];
    	int val(char x)
    	{
    		return x-'a'+1;
    	}
    	void insert(char s[],int len,int id)
    	{
    		int x=0;
    		for(int i=1;i<=len;i++)
    		{
    			if(tree[x].ch[val(s[i])]==0)
    			{
    				rt_sum++;
    				tree[x].ch[val(s[i])]=rt_sum;
    			}
    			x=tree[x].ch[val(s[i])];
    		}
    		ed[id]=x;
    	}
    	void build()
    	{
    		queue<int>q;
    		for(int i=1;i<=26;i++)
    		{
    			if(tree[0].ch[i]!=0)
    			{
    				fail[tree[0].ch[i]]=0;
    				add(0,tree[0].ch[i]);
    				q.push(tree[0].ch[i]);
    			}
    		}
    		while(q.empty()==0)
    		{
    			int x=q.front();
    			q.pop();
    			for(int i=1;i<=26;i++)
    			{
    				if(tree[x].ch[i]==0)
    				{
    					tree[x].ch[i]=tree[fail[x]].ch[i];
    				}
    				else
    				{
    					fail[tree[x].ch[i]]=tree[fail[x]].ch[i];
    					add(fail[tree[x].ch[i]],tree[x].ch[i]);
    					q.push(tree[x].ch[i]);
    				}
    			}
    		}	
    	}
    	int query(char s[],int len)
    	{
    		int x=0,ans=0;
    		for(int i=1;i<=len;i++)
    		{
    			x=tree[x].ch[val(s[i])];
    			ans+=B.getsum(dfn[x]);
    		}
    		cerr<<endl;
    		return ans;
    	}
    }A;
    void dfs(int x)
    {
    	tot++;
    	dfn[x]=tot;
    	for(int i=head[x];i!=0;i=e[i].nxt)
    	{
    		dfs(e[i].to);
    	}
    	out[x]=tot;
    }
    int main()
    {
    	int q,n,x,i;
    	char pd;
    	cin>>q>>n;
    	for(i=1;i<=n;i++)
    	{
    		cin>>(s+1);
    		A.insert(s,strlen(s+1),i);
    	}
    	A.build();
    	dfs(0);
    	for(i=1;i<=n;i++)
    	{
    		B.update(tot,dfn[A.ed[i]],out[A.ed[i]],1);
    	}
    	for(i=1;i<=q;i++)
    	{
    		cin>>pd;
    		if(pd=='?')
    		{
    			cin>>(s+1);
    			cout<<A.query(s,strlen(s+1))<<endl;
    		}
    		if(pd=='+')
    		{
    			cin>>x;
    			if(del[x]==1)
    			{
    				del[x]=0;
    				B.update(tot,dfn[A.ed[x]],out[A.ed[x]],1);
    			}
    		}
    		if(pd=='-')
    		{
    			cin>>x;
    			if(del[x]==0)
    			{
    				del[x]=1;
    				B.update(tot,dfn[A.ed[x]],out[A.ed[x]],-1);
    			}
    		}
    	}
    	return 0;
    }
    

8.23

闲话

  • 早上简单收拾了下内务。
  • 来机房后 \(miaomiao\) 颇为“正式”地(其实主要是 \(feifei\) 在一旁“颇为得意地炫耀成果”的缘故)问我们昨晚上谁夜聊了,然后我们就“全军覆没”地全站起来了,然后说让我们去收拾 \(501\) 四机房和楼下 \(403\) 机房,还以为多大的事呢。到 \(501\) 四机房后看见了 @FLORIZ 的蓝书,让 @wang54321 交给 \(miaomiao\) 了。楼下 \(403\) 机房貌似成杂物间了,出现了高招会、听课记录本、教案本、婚礼请柬、喜糖、耳机等一系列东西, \(huge\) 说有用的就自己留着,否则直接扔掉。
  • 上午隔壁的基本都走光了。
  • \(10:40 \sim 11:20\) 左右 \(huge\) 跟我们说外面有领导视察,让我们在机房别出声,也就别去厕所了。
  • 下午还有领导视察。
  • 吃晚饭的路上看见原初三年级主任了。
  • 晚上 \(miaomiao\) 回来时问我们今天都在哪里刷的题,刷的什么题,说这两天没有模拟赛但我们不能乱搞。

做题纪要

luogu P2414 [NOI2011] 阿狸的打字机

  • 同上题。等价于查询 \(fail\) 树上从一个点到根节点这条路径上特定点的的个数。

  • \(x\) 的单点修改加 \(y\) 的路径查询转化为 \(x\) 的子树查询和 \(y\) 的单点修改。

  • 将询问离线下来,方便支持加入和删除一个字符串。然后 \(DFS\) 序加树状数组维护即可。

    点击查看代码
    int ed[100010],dfn[100010],out[100010],ans[100010],tot=0;
    vector<int>e[100010];
    vector<pair<int,int> >q[100010];
    char s[100010];
    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]+=val;
    		}
    	}
    	int getsum(int x)
    	{
    		int ans=0;
    		for(int i=x;i>=1;i-=lowbit(i))
    		{
    			ans+=c[i];
    		}
    		return ans;
    	}
    	int query(int l,int r)
    	{
    		return getsum(r)-getsum(l-1);
    	}
    }B;
    struct ACM
    {
    	int fa[100010],fail[100010],rt_sum;
    	struct trie
    	{
    		int ch[27];
    	}tree[100010];
    	int val(char x)
    	{
    		return x-'a'+1;
    	}
    	void add(int &x,char s)
    	{
    		if(tree[x].ch[val(s)]==0)
    		{
    			rt_sum++;
    			tree[x].ch[val(s)]=rt_sum;
    			fa[tree[x].ch[val(s)]]=x;
    		}
    		x=tree[x].ch[val(s)];
    	}
    	void del(int &x)
    	{
    		x=fa[x];
    	}
    	void build()
    	{
    		queue<int>q;
    		for(int i=1;i<=26;i++)
    		{
    			if(tree[0].ch[i]!=0)
    			{
    				fail[tree[0].ch[i]]=0;
    				e[0].push_back(tree[0].ch[i]);
    				q.push(tree[0].ch[i]);
    			}
    		}
    		while(q.empty()==0)
    		{
    			int x=q.front();
    			q.pop();
    			for(int i=1;i<=26;i++)
    			{
    				if(tree[x].ch[i]==0)
    				{
    					tree[x].ch[i]=tree[fail[x]].ch[i];
    				}
    				else
    				{
    					fail[tree[x].ch[i]]=tree[fail[x]].ch[i];
    					e[fail[tree[x].ch[i]]].push_back(tree[x].ch[i]);
    					q.push(tree[x].ch[i]);
    				}
    			}
    		}
    	}
    }A;
    void dfs(int x)
    {
    	tot++;
    	dfn[x]=tot;
    	for(int i=0;i<e[x].size();i++)
    	{
    		dfs(e[x][i]);
    	}
    	out[x]=tot;
    }
    int main()
    {
    	int n,m,x,y,cnt,i,j;
    	cin>>(s+1)>>m;
    	n=strlen(s+1);
    	for(i=1,cnt=x=0;i<=n;i++)
    	{
    		if('a'<=s[i]&&s[i]<='z')
    		{
    			A.add(x,s[i]);
    		}
    		if(s[i]=='B')
    		{
    			A.del(x);
    		}
    		if(s[i]=='P')
    		{
    			cnt++;
    			ed[cnt]=x;
    		}
    	}
    	A.build();
    	dfs(0);
    	for(i=1;i<=m;i++)
    	{
    		cin>>x>>y;
    		q[y].push_back(make_pair(x,i));
    	}
    	for(i=1,cnt=x=0;i<=n;i++)
    	{
    		if('a'<=s[i]&&s[i]<='z')
    		{
    			A.add(x,s[i]);
    			B.add(tot,dfn[x],1);
    		}
    		if(s[i]=='B')
    		{
    			B.add(tot,dfn[x],-1);
    			A.del(x);
    		}
    		if(s[i]=='P')
    		{
    			cnt++;
    			for(j=0;j<q[cnt].size();j++)
    			{
    				ans[q[cnt][j].second]=B.query(dfn[ed[q[cnt][j].first]],out[ed[q[cnt][j].first]]);
    			}
    		}
    	}
    	for(i=1;i<=m;i++)
    	{
    		cout<<ans[i]<<endl;	
    	}
    	return 0;
    }
    

BZOJ3221 Obserbing the tree树上询问

  • \(u \to v\) 的路径拆成 \(u \to \operatorname{LCA}(u,v),\operatorname{LCA}(u,v) \to v\) 两部分。

  • \(u \to \operatorname{LCA}(u,v)\) 为例,设当前节点为 \(x\) ,则增加量为 \(a+b(dep_{u}-dep_{x})=-b \times dep_{x}+a+b \times dep_{u}\)

  • 等差数列可以化成 \(kx+b\) 的形式,主席树维护 \(k,b\) 的懒惰标记,最后标记永久化处理即可。

    点击查看代码
    struct node
    {
    	int nxt,to;
    }e[200010];
    int head[100010],fa[100010],siz[100010],son[100010],top[100010],dfn[100010],pos[100010],cnt=0,tot=0,c_cnt=0;
    ll dep[100010],sum[100010];
    void add(int u,int v)
    {
    	cnt++;
    	e[cnt].nxt=head[u];
    	e[cnt].to=v;
    	head[u]=cnt;
    }
    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;
    	tot++;
    	dfn[x]=tot;
    	pos[tot]=x;
    	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);
    			}
    		}
    	}
    }
    struct PDS_SMT
    {
    	int root[100010],rt_sum=0;
    	struct SegmentTree
    	{
    		int ls,rs;
    		ll sum,lazyk,lazyb;
    	}tree[100010<<7];
    	#define lson(rt) tree[rt].ls
    	#define rson(rt) tree[rt].rs
    	ll build_rt()
    	{
    		rt_sum++;
    		return rt_sum;
    	}
    	void build_tree(int &rt,ll l,ll r)
    	{
    		rt=build_rt();
    		if(l==r)
    		{
    			return;
    		}
    		ll mid=(l+r)/2;
    		build_tree(lson(rt),l,mid);
    		build_tree(rson(rt),mid+1,r);
    	}
    	void update(int pre,int &rt,ll l,ll r,ll x,ll y,ll k,ll b)
    	{
    		rt=build_rt();
    		tree[rt]=tree[pre];
    		tree[rt].sum+=(sum[min(r,y)]-sum[max(l,x)-1])*k+(min(r,y)-max(l,x)+1)*b;
    		if(x<=l&&r<=y)
    		{
    			tree[rt].lazyk+=k;
    			tree[rt].lazyb+=b;
    			return;
    		}
    		ll mid=(l+r)/2;
    		if(x<=mid)
    		{
    			update(lson(pre),lson(rt),l,mid,x,y,k,b);
    		}
    		if(y>mid)
    		{
    			update(rson(pre),rson(rt),mid+1,r,x,y,k,b);
    		}
    	}
    	ll query(int rt,ll l,ll r,ll x,ll y)
    	{
    		if(x<=l&&r<=y)
    		{
    			return tree[rt].sum;
    		}
    		ll mid=(l+r)/2,ans=(sum[min(r,y)]-sum[max(l,x)-1])*tree[rt].lazyk+(min(r,y)-max(l,x)+1)*tree[rt].lazyb;
    		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;
    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]?v:u;
    }
    void update(int u,int v,ll a,ll b,int pos,int n)
    {
    	int rt=lca(u,v);
    	pair<ll,ll>val1=make_pair(-b,a+dep[u]*b),val2=make_pair(b,a+(dep[u]-2*dep[rt])*b);
    	while(top[u]!=top[rt])
    	{
    		T.update(T.root[pos],T.root[pos],1,n,dfn[top[u]],dfn[u],val1.first,val1.second);
    		u=fa[top[u]];
    	}
    	T.update(T.root[pos],T.root[pos],1,n,dfn[rt],dfn[u],val1.first,val1.second);
    	while(top[v]!=top[rt])
    	{
    		T.update(T.root[pos],T.root[pos],1,n,dfn[top[v]],dfn[v],val2.first,val2.second);
    		v=fa[top[v]];
    	}
    	if(rt!=v)
    	{
    		T.update(T.root[pos],T.root[pos],1,n,dfn[rt]+1,dfn[v],val2.first,val2.second);	
    	}
    }
    ll query(int u,int v,int pos,int n)
    {
    	ll ans=0;
    	while(top[u]!=top[v])
    	{
    		if(dep[top[u]]>dep[top[v]])
    		{
    			ans+=T.query(T.root[pos],1,n,dfn[top[u]],dfn[u]);
    			u=fa[top[u]];
    		}
    		else
    		{
    			ans+=T.query(T.root[pos],1,n,dfn[top[v]],dfn[v]);
    			v=fa[top[v]];
    		}
    	}
    	if(dep[u]>dep[v])
    	{
    		ans+=T.query(T.root[pos],1,n,dfn[v],dfn[u]);
    	}
    	else
    	{
    		ans+=T.query(T.root[pos],1,n,dfn[u],dfn[v]);
    	}
    	return ans;
    }
    int main()
    {
    	int n,m,u,v,last=0,i;
    	ll x,y,a,b,ans=0;
    	char pd;
    	cin>>n>>m;
    	for(i=1;i<=n-1;i++)
    	{
    		cin>>u>>v;
    		add(u,v);
    		add(v,u);
    	}
    	dfs1(1,0);
    	dfs2(1,1);
    	for(i=1;i<=n;i++)
    	{
    		sum[i]=sum[i-1]+dep[pos[i]];
    	}
    	T.build_tree(T.root[0],1,n);
    	for(i=1;i<=m;i++)
    	{
    		cin>>pd;
    		if(pd=='c')
    		{
    			cin>>x>>y>>a>>b;
    			x^=ans;
    			y^=ans;
    			c_cnt++;
    			T.root[c_cnt]=T.root[last];
    			last=c_cnt;
    			update(x,y,a,b,c_cnt,n);
    		}
    		if(pd=='q')
    		{
    			cin>>x>>y;
    			x^=ans;
    			y^=ans;
    			ans=query(x,y,last,n);
    			cout<<ans<<endl;
    		}
    		if(pd=='l')
    		{
    			cin>>x;
    			x^=ans;
    			last=x;
    		}
    	}
    	return 0;
    }
    

luogu P3567 [POI2014] KUR-Couriers

  • 多倍经验: luogu P7261 [COCI2009-2010#3] PATULJCI | luogu P7252 [JSOI2011] 棒棒糖 | SP5652 PATULJCI - Snow White and the N dwarfs

  • 递归左右子树时判断一下是否可能存在解,主席树维护即可。

    点击查看代码
    int a[500010];
    struct PDS_SMT
    {
    	int root[500010],rt_sum;
    	struct SegmentTree
    	{
    		int ls,rs,sum;
    	}tree[500010<<5];
    	#define lson(rt) tree[rt].ls
    	#define rson(rt) tree[rt].rs
    	int build_rt()
    	{
    		rt_sum++;
    		return rt_sum;
    	}
    	void build_tree(int &rt,int l,int r)
    	{
    		rt=build_rt();
    		if(l==r)
    		{
    			return;
    		}		
    		int mid=(l+r)/2;
    		build_tree(lson(rt),l,mid);
    		build_tree(rson(rt),mid+1,r);
    	}
    	void update(int pre,int &rt,int l,int r,int pos)
    	{
    		rt=build_rt();
    		tree[rt]=tree[pre];
    		tree[rt].sum++;
    		if(l==r)
    		{
    			return;
    		}
    		int mid=(l+r)/2;
    		if(pos<=mid)
    		{
    			update(lson(pre),lson(rt),l,mid,pos);
    		}
    		else
    		{
    			update(rson(pre),rson(rt),mid+1,r,pos);
    		}
    	}
    	int query(int rt1,int rt2,int l,int r,int k)
    	{
    		if(l==r)
    		{
    			return l;
    		}
    		int mid=(l+r)/2;
    		if(tree[lson(rt2)].sum-tree[lson(rt1)].sum>k)
    		{
    			return query(lson(rt1),lson(rt2),l,mid,k);
    		}
    		if(tree[rson(rt2)].sum-tree[rson(rt1)].sum>k)
    		{
    			return query(rson(rt1),rson(rt2),mid+1,r,k);
    		}
    		return 0;
    	}
    }T;
    int main()
    {
    	int n,m,l,r,i;
    	scanf("%d%d",&n,&m);
    	T.build_tree(T.root[0],1,n);
    	for(i=1;i<=n;i++)
    	{
    		scanf("%d",&a[i]);
    		T.update(T.root[i-1],T.root[i],1,n,a[i]);
    	}
    	for(i=1;i<=m;i++)
    	{
    		scanf("%d%d",&l,&r);
    		printf("%d\n",T.query(T.root[l-1],T.root[r],1,n,(r-l+1)/2));
    	}
    	return 0;
    }
    

UVA12299 RMQ with Shifts

  • 观察到操作长度不超过 \(30\) ,暴力进行单点修改即可。

    点击查看代码
    int a[100010];
    vector<int>q;
    string s;
    struct SMT
    {
    	struct SegmentTree
    	{
    		int l,r,minn;
    	}tree[400010];
    	int lson(int x)
    	{
    		return x*2;
    	}
    	int rson(int x)
    	{
    		return x*2+1;
    	}
    	void pushup(int rt)
    	{
    		tree[rt].minn=min(tree[lson(rt)].minn,tree[rson(rt)].minn);
    	}
    	void build(int rt,int l,int r)
    	{
    		tree[rt].l=l;
    		tree[rt].r=r;
    		if(l==r)
    		{
    			tree[rt].minn=a[l];
    			return;
    		}
    		int mid=(l+r)/2;
    		build(lson(rt),l,mid);
    		build(rson(rt),mid+1,r);
    		pushup(rt);
    	}
    	void update(int rt,int pos,int val)
    	{
    		if(tree[rt].l==tree[rt].r)
    		{ 
    			tree[rt].minn=val;
    			return;
    		}
    		int mid=(tree[rt].l+tree[rt].r)/2;
    		if(pos<=mid)
    		{
    			update(lson(rt),pos,val);
    		}
    		else
    		{
    			update(rson(rt),pos,val);	
    		}
    		pushup(rt);
    	}
    	int query(int rt,int x,int y)
    	{
    		if(x<=tree[rt].l&&tree[rt].r<=y)
    		{
    			return tree[rt].minn;
    		}
    		int mid=(tree[rt].l+tree[rt].r)/2,ans=0x7f7f7f7f;
    		if(x<=mid)
    		{
    			ans=min(ans,query(lson(rt),x,y));
    		}
    		if(y>mid)
    		{
    			ans=min(ans,query(rson(rt),x,y));
    		}
    		return ans;
    	}
    }T;
    int main()
    {
    	int n,m,tmp,i,j;
    	cin>>n>>m;
    	for(i=1;i<=n;i++)
    	{
    		cin>>a[i];
    	}
    	T.build(1,1,n);
    	for(i=1;i<=m;i++)
    	{
    		cin>>s;
    		q.clear();
    		tmp=0;
    		for(j=6;j<s.size();j++)
    		{
    			if('0'<=s[j]&&s[j]<='9')
    			{
    				tmp=tmp*10+s[j]-'0';
    			}
    			else
    			{
    				q.push_back(tmp);
    				tmp=0;
    			}
    		}
    		if(s[0]=='q')
    		{
    			cout<<T.query(1,q[0],q[1])<<endl;
    		}
    		else
    		{
    			tmp=a[q[0]];
    			for(j=0;j<q.size()-1;j++)
    			{
    				T.update(1,q[j],a[q[j+1]]);
    				a[q[j]]=a[q[j+1]];
    			}
    			T.update(1,q[q.size()-1],tmp);
    			a[q[q.size()-1]]=tmp;
    		}
    	}
    	return 0;
    }
    

牛客 NC275617 材料打印

牛客 NC275619 %%%

牛客 NC275634 迷宫

牛客 NC275683 又是一年毕业季

牛客 NC275713 多米诺骨牌

CF1514D Cut and Stick

  • 若区间内不存在绝对众数,直接保持这一段即可。

  • 若存在绝对众数,贪心地想肯定要尽可能地把其分开还要限制出其他数使其不成为绝对众数。容易发现设绝对众数出现次数为 \(cnt\) ,取 \(cnt-1\) 个其他数和绝对众数配对最优。但可能其他数不够 \(cnt\) 个,就只能让多余的绝对众数各成一组了。最终答案即为 \(2cnt-(r-l+1)\)

  • 主席树求下是否存在绝对众数及其出现次数即可。

    点击查看代码
    int a[300010];
    struct PDS_SMT
    {
    	int root[300010],rt_sum;
    	struct SegmentTree
    	{
    		int ls,rs,sum;
    	}tree[300010<<5];
    	#define lson(rt) tree[rt].ls
    	#define rson(rt) tree[rt].rs
    	int build_rt()
    	{
    		rt_sum++;
    		return rt_sum;
    	}
    	void build_tree(int &rt,int l,int r)
    	{
    		rt=build_rt();
    		if(l==r)
    		{
    			return;
    		}		
    		int mid=(l+r)/2;
    		build_tree(lson(rt),l,mid);
    		build_tree(rson(rt),mid+1,r);
    	}
    	void update(int pre,int &rt,int l,int r,int pos)
    	{
    		rt=build_rt();
    		tree[rt]=tree[pre];
    		tree[rt].sum++;
    		if(l==r)
    		{
    			return;
    		}
    		int mid=(l+r)/2;
    		if(pos<=mid)
    		{
    			update(lson(pre),lson(rt),l,mid,pos);
    		}
    		else
    		{
    			update(rson(pre),rson(rt),mid+1,r,pos);
    		}
    	}
    	int query(int rt1,int rt2,int l,int r,int k)
    	{
    		if(l==r)
    		{
    			return tree[rt2].sum-tree[rt1].sum;
    		}
    		int mid=(l+r)/2;
    		if(tree[lson(rt2)].sum-tree[lson(rt1)].sum>k)
    		{
    			return query(lson(rt1),lson(rt2),l,mid,k);
    		}
    		if(tree[rson(rt2)].sum-tree[rson(rt1)].sum>k)
    		{
    			return query(rson(rt1),rson(rt2),mid+1,r,k);
    		}
    		return 0;
    	}
    }T;
    int main()
    {
    	int n,m,l,r,cnt,i;
    	scanf("%d%d",&n,&m);
    	T.build_tree(T.root[0],1,n);
    	for(i=1;i<=n;i++)
    	{
    		scanf("%d",&a[i]);
    		T.update(T.root[i-1],T.root[i],1,n,a[i]);
    	}
    	for(i=1;i<=m;i++)
    	{
    		scanf("%d%d",&l,&r);
    		cnt=T.query(T.root[l-1],T.root[r],1,n,(r-l+1)/2.0);
    		if(cnt==0)
    		{
    			printf("1\n");
    		}
    		else
    		{
    			printf("%d\n",2*cnt-(r-l+1));
    		}
    	}
    	return 0;
    }
    

8.24

闲话

  • 中午给家里打电话的时候得知了明天下午 \(5:00\) 放假的消息。

  • 学校 \(OJ\) 上抽象的一言。

  • 下午看 \(miaomiao\) 一直在捣鼓对面的电脑,貌似还把 ssh 给毙了,希望能用电脑增加的内存给弥补,免得死机了只能重启。

  • 体活时 \(miaomiao\) 应该是默认我们不上体活了,干脆没说上体活的事情。

做题纪要

牛客 NC275719 自爆机器人

牛客 NC275735 大鱼吃小鱼

luogu P4137 Rmq Problem / mex

  • 每个数记录下最后一次出现的位置。

  • 查询时等价于查询 \([1,r]\) 中最后一次出现位置 \(\le l-1\) 的最小数。

  • 主席树节点额外记录最小值即可。

    点击查看代码
    int a[200010];
    struct PDS_SMT
    {
    	int root[200010],rt_sum;
    	struct SegmentTree
    	{
    		int ls,rs,minn;
    	}tree[200010<<5];
    	#define lson(rt) tree[rt].ls
    	#define rson(rt) tree[rt].rs
    	int build_rt()
    	{
    		rt_sum++;
    		return rt_sum;
    	}
    	void pushup(int rt)
    	{
    		tree[rt].minn=min(tree[lson(rt)].minn,tree[rson(rt)].minn);
    	}
    	void build_tree(int &rt,int l,int r)
    	{
    		rt=build_rt();
    		if(l==r)
    		{
    			return;
    		}
    		int mid=(l+r)/2;
    		build_tree(lson(rt),l,mid);
    		build_tree(rson(rt),mid+1,r);
    	}
    	void update(int pre,int &rt,int l,int r,int pos,int val)
    	{
    		rt=build_rt();
    		tree[rt]=tree[pre];
    		if(l==r)
    		{
    			tree[rt].minn=val;
    			return;
    		}
    		int mid=(l+r)/2;
    		if(pos<=mid)
    		{
    			update(lson(pre),lson(rt),l,mid,pos,val);
    		}
    		else
    		{
    			update(rson(pre),rson(rt),mid+1,r,pos,val);
    		}
    		pushup(rt);
    	}
    	int query(int rt,int l,int r,int k)
    	{
    		if(l==r)
    		{
    			return l;
    		}
    		int mid=(l+r)/2;
    		if(tree[lson(rt)].minn<=k)
    		{
    			return query(lson(rt),l,mid,k);
    		}
    		else
    		{
    			return query(rson(rt),mid+1,r,k);
    		}
    	}
    }T;
    int main()
    {
    	int n,m,l,r,i;
    	cin>>n>>m;
    	T.build_tree(T.root[0],0,200001);
    	for(i=1;i<=n;i++)
    	{
    		cin>>a[i];
    		T.update(T.root[i-1],T.root[i],0,200001,a[i],i);
    	}
    	for(i=1;i<=m;i++)
    	{
    		cin>>l>>r;
    		cout<<T.query(T.root[r],0,200001,l-1)<<endl;
    	}
    	return 0;
    }
    

luogu P10690 Fotile 模拟赛 L

  • 考虑分块预处理整块的答案,散块直接暴力。

  • \(f_{i,j}\) 表示以 \(i\) 所在的块的左端点为左端点, \(j\) 为右端点的最大异或和,可持久化 \(01Trie\) 维护即可。

    • 本题中这种写法比处理整块到整块的答案更容易处理些。
  • 整块的答案直接继承,枚举散块内的点判断即可。

  • 理论块长取 \(\frac{n\sqrt{m \log_{2}{V}}}{m}\) ,但实测取 \(176\) 最快。

    点击查看代码
    struct PDS_Trie
    {
    	int root[12010],rt_sum=0;
    	struct Trie
    	{
    		int cnt,ch[2];
    	}tree[12010*40];
    	int build()
    	{
    		rt_sum++;
    		return rt_sum;
    	}
    	void insert(int pre,int &rt,int s)
    	{
    		rt=build();
    		int p=rt,q=pre;
    		tree[p].cnt=tree[q].cnt+1;
    		for(int i=31;i>=0;i--)
    		{
    			for(int j=0;j<=1;j++)
    			{
    				tree[p].ch[j]=tree[q].ch[j];
    			}
    			tree[p].ch[(s>>i)&1]=build();
    			p=tree[p].ch[(s>>i)&1];
    			q=tree[q].ch[(s>>i)&1];
    			tree[p].cnt=tree[q].cnt+1;
    		}
    	}
    	int query(int rt1,int rt2,int s)
    	{
    		int ans=0;
    		for(int i=31;i>=0;i--)
    		{
    			if(tree[rt2].ch[((s>>i)&1)^1]-tree[rt1].ch[((s>>i)&1)^1]>=1)
    			{
    				ans|=(1<<i);
    				rt1=tree[rt1].ch[((s>>i)&1)^1];
    				rt2=tree[rt2].ch[((s>>i)&1)^1];
    			}
    			else
    			{
    				rt1=tree[rt1].ch[(s>>i)&1];
    				rt2=tree[rt2].ch[(s>>i)&1];
    			}
    		}
    		return ans;
    	}
    	int ask(int l,int r,int s)
    	{
    		l++;
    		r++;
    		return query(root[l-1],root[r],s);
    	}
    }T;
    int L[12010],R[12010],pos[12010],a[12010],sum[12010],f[900][12010],klen,ksum;
    void init(int n,int m)
    {
    	klen=n*sqrt(m*32)/m+1;
    	ksum=n/klen;
    	for(int i=1;i<=ksum;i++)
    	{
    		L[i]=R[i-1]+1;
    		R[i]=R[i-1]+klen;
    	}
    	if(R[ksum]<n)
    	{
    		ksum++;
    		L[ksum]=R[ksum-1]+1;
    		R[ksum]=n;
    	}
    	for(int i=1;i<=ksum;i++)
    	{
    		for(int j=L[i];j<=R[i];j++)
    		{
    			pos[j]=i;
    		}
    		for(int j=L[i];j<=n;j++)
    		{
    			f[i][j]=max(f[i][j-1],T.ask(L[i],j,sum[j]));
    		}
    	}
    }
    int query(int l,int r)
    {
    	int ans=0;
    	if(pos[l]==pos[r])
    	{
    		for(int i=l;i<=r;i++)
    		{
    			ans=max(ans,T.ask(l,r,sum[i]));
    		}
    	}
    	else
    	{
    		ans=f[pos[l]+1][r];
    		for(int i=l;i<=R[pos[l]];i++)
    		{
    			ans=max(ans,T.ask(l,r,sum[i]));
    		}
    	}
    	return ans;
    }
    int main()
    {
    	int n,m,ans=0,pos=1,l,r,i;
    	cin>>n>>m;
    	T.insert(T.root[0],T.root[1],0);
    	for(i=1;i<=n;i++)
    	{
    		cin>>a[i];
    		sum[i]=sum[i-1]^a[i];
    		pos++;
    		T.insert(T.root[pos-1],T.root[pos],sum[i]);
    	}
    	init(n,m);
    	for(i=1;i<=m;i++)
    	{
    		cin>>l>>r;
    		l=(l%n+ans%n)%n+1;
    		r=(r%n+ans%n)%n+1;
    		if(l>r)
    		{
    			swap(l,r);
    		}
    		ans=query(l-1,r);
    		cout<<ans<<endl;
    	}
    	return 0;
    }
    

luogu P4094 [HEOI2016/TJOI2016] 字符串

  • 等价于查询 \(\max\limits_{w=a}^{b} \{ \min(b-a+1,d-c+1,\operatorname{LCP}(s_{w \sim b },s_{c \sim d})) \}\)

  • \(\operatorname{LCP}\) 转化成 \(height\) 的取 \(\min\) ,观察到 \(\min\) 具有单调不增性,所以让 \([\min(rk_{w},rk_{c})+1,\max(rk_{w},rk_{c})]\) 长度尽可能小显然取 \(rk_{c}\) 的前驱/后继时取到最优情况。

  • 上述做法仅针对右端点为 \(n\) 的情况,对于更为一般的情况不适用。

  • 观察到答案具有单调性,考虑二分答案。设当前二分出的答案为 \(mid\) ,在 \([a,b-mid+1]\) 里选择前驱/后继判断最后的 \(\operatorname{LCP}\) 是否 \(\ge mid\) 即可。

  • 判断是否存在前驱/后继。

    点击查看代码
    int sa[100010],rk[200010],oldrk[200010],id[100010],cnt[100010],key[100010],height[100010];
    char s[100010];
    int val(char x)
    {
    	return (int)x;
    }
    void counting_sort(int n,int m)
    {
    	memset(cnt,0,sizeof(cnt));
    	for(int i=1;i<=n;i++)
    	{
    		cnt[key[i]]++;
    	}
    	for(int i=1;i<=m;i++)
    	{
    		cnt[i]+=cnt[i-1];
    	}
    	for(int i=n;i>=1;i--)
    	{
    		sa[cnt[key[i]]]=id[i];
    		cnt[key[i]]--;
    	}
    }
    void init_sa(char s[],int len)
    {
    	int m=127,tot=0,num=0;
    	for(int i=1;i<=len;i++)
    	{
    		rk[i]=val(s[i]);
    		id[i]=i;
    		key[i]=rk[id[i]];
    	}
    	counting_sort(len,m);
    	for(int w=1;tot!=len;w<<=1,m=tot)
    	{
    		num=0;
    		for(int i=len;i>=len-w+1;i--)
    		{
    			num++;
    			id[num]=i;
    		}
    		for(int i=1;i<=len;i++)
    		{
    			if(sa[i]>w)
    			{
    				num++;
    				id[num]=sa[i]-w;
    			}
    		}
    		for(int i=1;i<=len;i++)
    		{
    			key[i]=rk[id[i]];
    		}
    		counting_sort(len,m);
    		for(int i=1;i<=len;i++)
    		{
    			oldrk[i]=rk[i];
    		}
    		tot=0;
    		for(int i=1;i<=len;i++)
    		{
    			tot+=(oldrk[sa[i]]!=oldrk[sa[i-1]]||oldrk[sa[i]+w]!=oldrk[sa[i-1]+w]);
    			rk[sa[i]]=tot;
    		}
    	}
    	for(int i=1,j=0;i<=len;i++)
    	{
    		j-=(j>=1);
    		while(s[i+j]==s[sa[rk[i]-1]+j])
    		{
    			j++;
    		}
    		height[rk[i]]=j;
    	}
    }
    struct PDS_SMT
    {
    	int root[100010],rt_sum;
    	struct SegmentTree
    	{
    		int ls,rs,sum;
    	}tree[100010<<5];
    	#define lson(rt) tree[rt].ls
    	#define rson(rt) tree[rt].rs
    	int build_rt()
    	{
    		rt_sum++;
    		return rt_sum;
    	}
    	void build_tree(int &rt,int l,int r)
    	{
    		rt=build_rt();
    		if(l==r)
    		{
    			return;
    		}
    		int mid=(l+r)/2;
    		build_tree(lson(rt),l,mid);
    		build_tree(rson(rt),mid+1,r);
    	}
    	void update(int pre,int &rt,int l,int r,int pos)
    	{
    		rt=build_rt();
    		tree[rt]=tree[pre];
    		tree[rt].sum++;
    		if(l==r)
    		{
    			return;
    		}
    		int mid=(l+r)/2;
    		if(pos<=mid)
    		{
    			update(lson(pre),lson(rt),l,mid,pos);
    		}
    		else
    		{
    			update(rson(pre),rson(rt),mid+1,r,pos);
    		}
    	}
    	int query_rk(int rt1,int rt2,int l,int r,int x,int y)
    	{
    		if(x<=l&&r<=y)
    		{
    			return tree[rt2].sum-tree[rt1].sum;
    		}
    		int mid=(l+r)/2,ans=0;
    		if(x<=mid)
    		{
    			ans+=query_rk(lson(rt1),lson(rt2),l,mid,x,y);
    		}
    		if(y>mid)
    		{
    			ans+=query_rk(rson(rt1),rson(rt2),mid+1,r,x,y);
    		}
    		return ans;
    	}
    	int kth_min(int rt1,int rt2,int l,int r,int k)
    	{
    		if(l==r)
    		{
    			return l;
    		}
    		int mid=(l+r)/2;
    		if(k<=tree[lson(rt2)].sum-tree[lson(rt1)].sum)
    		{
    			return kth_min(lson(rt1),lson(rt2),l,mid,k);
    		}
    		else
    		{
    			return kth_min(rson(rt1),rson(rt2),mid+1,r,k-(tree[lson(rt2)].sum-tree[lson(rt1)].sum));
    		}
    	}
    	int query_pre(int rt1,int rt2,int l,int r,int k)
    	{
    		int tmp=query_rk(rt1,rt2,l,r,0,k-1);
    		if(tmp==0)
    		{
    			return 0;
    		}
    		else
    		{
    			return kth_min(rt1,rt2,l,r,tmp);
    		}		
    	}
    	int query_nxt(int rt1,int rt2,int l,int r,int k,int n)
    	{
    		int tmp=query_rk(rt1,rt2,l,r,0,k)+1;
    		if(tmp==tree[rt2].sum-tree[rt1].sum+1)
    		{
    			return n+1;
    		}
    		else
    		{
    			return kth_min(rt1,rt2,l,r,tmp);
    		}
    	}
    }T;
    struct ST
    {
    	int fminn[100010][25];
    	void init(int n,int a[])
    	{
    		memset(fminn,0x3f,sizeof(fminn));
    		for(int i=1;i<=n;i++)
    		{
    			fminn[i][0]=a[i];
    		}
    		for(int j=1;j<=log2(n);j++)
    		{
    			for(int i=1;i+(1<<j)-1<=n;i++)
    			{
    				fminn[i][j]=min(fminn[i][j-1],fminn[i+(1<<(j-1))][j-1]);
    			}
    		}
    	}
    	int query(int l,int r,int n)
    	{
    		if(l==r)
    		{
    			return n-sa[l]+1;
    		}
    		if(l>r)
    		{
    			swap(l,r);
    		}
    		l++;
    		int t=log2(r-l+1);
    		return min(fminn[l][t],fminn[r-(1<<t)+1][t]);
    	}
    }S;
    bool check(int a,int b,int c,int d,int mid,int n)
    {
    	int x=T.query_pre(T.root[a-1],T.root[b-mid+1],1,n,rk[c]);
    	int y=T.query_nxt(T.root[a-1],T.root[b-mid+1],1,n,rk[c],n);
    	if(x!=0)
    	{
    		if(y!=n+1)
    		{
    			return max(min(min(S.query(x,rk[c],n),b-sa[x]+1),d-c+1),min(min(S.query(y,rk[c],n),b-sa[y]+1),d-c+1))>=mid;//和区间长度取 min
    		}
    		else
    		{
    			return min(min(S.query(x,rk[c],n),b-sa[x]+1),d-c+1)>=mid;//和区间长度取 min
    		}
    	}
    	else
    	{
    		return min(min(S.query(y,rk[c],n),b-sa[y]+1),d-c+1)>=mid;//和区间长度取 min
    	}
    }	
    int main()
    {
    	int n,m,a,b,c,d,l,r,mid,ans,i;
    	cin>>n>>m>>(s+1);
    	init_sa(s,n);
    	S.init(n,height);
    	T.build_tree(T.root[0],1,n);
    	for(i=1;i<=n;i++)
    	{
    		T.update(T.root[i-1],T.root[i],1,n,rk[i]);
    	}
    	for(i=1;i<=m;i++)
    	{
    		cin>>a>>b>>c>>d;
    		l=1;//从 1 开始防止 b-mid+1>b
    		r=min(b-a+1,d-c+1);
    		ans=0;
    		while(l<=r)
    		{
    			mid=(l+r)/2;
    			if(check(a,b,c,d,mid,n)==true)
    			{
    				ans=mid;
    				l=mid+1;
    			}
    			else
    			{
    				r=mid-1;
    			}
    		}
    		cout<<ans<<endl;
    	}
    	return 0;
    }
    

[ABC368A] Cut

  • 模拟。

    点击查看代码
    int a[110];
    int main()
    {
    	int n,k,i,j;
    	cin>>n>>k;
    	for(i=1;i<=n;i++)
    	{
    		cin>>a[i];
    	}
    	for(i=n-k+1;i<=n;i++)
    	{
    		cout<<a[i]<<" ";
    	}
    	for(i=1;i<=n-k;i++)
    	{
    		cout<<a[i]<<" ";
    	}
    	return 0;
    }
    

8.25

闲话

  • 早上来机房后发现全机房电脑(除 @Pursuing_OIer 之外)被重启成了 Windows , Linux 被卡在了登录界面。被迫用 Windows ,点开浏览器后发现上不了新学校 \(OJ\) ,可能是因为 @白简 遗产的问题(?),找 \(field\) 开开了。中途发现全网关了。过了一会儿, \(field\) 说他把另外一半电脑修好了,让我们“迁徙”至另外一半电脑,于是我占用了 @K8He 的电脑,把键盘也顺带着拿过去了。
  • 上午 \(miaomiao\) 说了开了学我们应该就在 HZ 上课了,但不住在现在集训用的是宿舍,不用带走的行李下午直接拿到机房就行。
  • 临吃午饭的时候 \(field\) 又强调了一遍这个问题。
  • 午休 \(field\) 查宿,因打铃后我们仍在收拾行李,他没说啥,只是说收拾完早点休息。
  • 下午放假。
    • 爆典现场

    miaomiao :你不是走了吗

    jijidawang:我其实走了,你现在看见的是我的幻影

    (过了一会儿)

    miaomiao (仔细回味着说):嗯,幻影

做题纪要

T2742. DP搬运工2

  • 预设性 \(DP\)

  • \(f_{i,j}\) 表示当前已经填了 \([1,i]\) 共有 \(j\) 个合法点的方案数。边界为 \(\begin{cases} f_{1,0}=1 \\ f_{2,0}=2 \end{cases}\)

  • 对填入的第 \(i\) 个数的位置进行分讨。

    • 合法点数量不变
      • \(i\) 插入到原 \(j\) 个合法点的两边,所选择插入位置的点不再符合题意,但 \(i\) 合法,使得合法点数量不变。
      • 可填入的位置算上首尾共有 \(2j+2\) 种,故 \(f_{i,j}+=f_{i-1,j} \times (2j+2)\)
    • 增加一个合法点
      • \(i\) 插入原不合法点的两边(特判首尾),合法点数量加一。
      • 可填入的位置共有 \(i-1-1-2(j-1)=i-2j\) 种,故 \(f_{i,j}+=f_{i-1,j-1} \times (i-2j)\)
  • 最终有 \(f_{n,k}\) 即为所求。

    点击查看代码
    const ll p=998244353;
    ll f[2010][2010];
    int main()
    {
    	ll n,k,i,j;
    	cin>>n>>k;
    	f[1][0]=1;
    	f[2][0]=2;
    	for(i=3;i<=n;i++)
    	{
    		for(j=0;j<=k;j++)
    		{
    			f[i][j]=f[i-1][j]*(2*j+2)%p;
    			if(j-1>=0&&i-2*j>=0)
    			{
    				f[i][j]=(f[i][j]+f[i-1][j-1]*(i-2*j)%p)%p;
    			}
    		}
    	}
    	cout<<f[n][k]<<endl;
    	return 0;
    }
    

SP10502 VIDEO - Video game combos

T2743. DP搬运工3

  • 观察到对于任意一个 \(\{ b \}\) 符合题意的 \(\{ a \}\) 的数量是相同的。

    • 交换顺序即可证明。
  • 考虑固定 \(b_{i}=i(i \in [1,n])\) ,然后求出符合题意的 \(\{ a \}\) 的数量,最后再乘 \(n!\) 即可。

  • \(f_{i,j,k}\) 表示当前填到了 \(i\) ,前 \(i\) 位共有 \(j\) 个空位,前 \(i\) 位的贡献为 \(k\) 的方案数。边界为 \(f_{0,0,0}=1\)

  • 对填入的第 \(i\) 个数的位置进行分讨。

    • 增加一个空位
      • \(i\) 只能填到 \((i,n]\) 中,对贡献不产生影响,有 \(f_{i,j,k}+=f_{i-1,j-1,k}\)
    • 减少一个空位
      • \(i\) 填入原空位中的一个位置,且原空位中的一个数填到 \(i\) 中,对贡献产生 \(2i\) 的影响,有 \(f_{i,j,k}+=f_{i-1,j+1,k-2i} \times (j+1) ^{2}\)
    • 空位数量不变
      • \(i\) 填入原空位中的一个位置,且原空位中的任何数都不填到 \(i\) 中,对贡献产生 \(i\) 的影响,有 \(f_{i,j,k}+=f_{i-1,j,k-i} \times j\)
      • \(i\) 填入位置 \(i\) ,且原空位中的任何数都不填到 \(i\) 中,对贡献产生 \(i\) 的影响,有 \(f_{i,j,k}+=f_{i-1,j,k-i}\)
      • \(i\) 填到 \((i,n]\) 中,且原空位中的一个数填到 \(i\) 中,对贡献产生 \(i\) 的影响,有 \(f_{i,j,k}+=f_{i-1,j,k-i} \times j\)
  • 最终有 \(n!\sum\limits_{i=m}^{n^{2}}f_{n,0,i}\) 即为所求。

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

luogu P10993 【MX-J3-T0】37

  • 顺序结构。

    点击查看代码
    int main()
    {
    	ll x,last=-1;
    	cin>>x;
    	x*=37;
    	while(x)
    	{
    		if(last!=-1)
    		{
    			if(last!=x%10)
    			{
    				cout<<"No";
    				return 0;
    			}
    		}
    		last=x%10;
    		x/=10;
    	}
    	cout<<"Yes"<<endl;
    	return 0;
    }
    

luogu P10994 【MX-J3-T1】Seats

  • 模拟。

    点击查看代码
    char s[100010];
    int main()
    {
    	int a,b,n,cnt1=0,cnt2=0,i;
    	cin>>a>>b>>(s+1);
    	n=strlen(s+1);
    	for(i=1;i<=n;i++)
    	{
    		cnt1+=(s[i]=='S');
    		cnt2+=(s[i]=='T');
    	}
    	if(a+b<=n)
    	{
    		cout<<max(a-cnt1,max(b-cnt2,0))<<endl;
    	}
    	else
    	{
    		cout<<-1<<endl;
    	}
    	return 0;
    }
    

[ABC368B] Decrease 2 max elements

  • 观察到值域很小,直接冲过去即可。

    点击查看代码
    int a[110];
    int main()
    {
    	int n,ans=0,sum=0,i;
    	cin>>n;
    	for(i=1;i<=n;i++)
    	{
    		cin>>a[i];
    	}
    	while(1)
    	{
    		sum=0;
    		for(i=1;i<=n;i++)
    		{
    			sum+=(a[i]>=1);
    		}
    		if(sum<=1)
    		{
    			break;
    		}
    		ans++;
    		sort(a+1,a+1+n);
    		a[n]--;
    		a[n-1]--;
    	}
    	cout<<ans<<endl;
    	return 0;
    }
    

[ABC368C] Triple Attack

  • 观察到掉血具有周期性,大段 \([1,1,3]\) 直接处理,小段暴力模拟。

    点击查看代码
    int main()
    {
    	ll n,x,t=0,i;
    	cin>>n;
    	for(i=1;i<=n;i++)
    	{
    		cin>>x;
    		t+=x/5*3;
    		x%=5;
    		while(x>0)
    		{
    			t++;
    			if(t%3==0)
    			{
    				x-=3;
    			}
    			else
    			{
    				x--;
    			}
    		}
    	}
    	cout<<t<<endl;
    	return 0;
    }
    

[ABC368D] Minimum Steiner Tree

  • 强化版: luogu P9340 [JOISC 2023 Day3] Tourism

    点击查看代码
    struct quality
    {
    	int pre,nxt,pos;
    }x;
    vector<quality>s;
    struct node
    {
    	int nxt,to;
    }e[400010];
    int head[200010],dep[200010],rk[200010],dfn[200010],a[200010],pos[200010],L[200010],R[200010],ans[200010],num[200010],pre[200010],nxt[2000010],tot=0,cnt=0,klen,ksum;
    struct ask
    {
    	int l,r,id;
    }q[200010];
    bool q_cmp(ask a,ask b)
    {
    	return (pos[a.l]==pos[b.l])?(a.r>b.r):(a.l<b.l);
    }
    void add(int u,int v)
    {
    	cnt++;
    	e[cnt].nxt=head[u];
    	e[cnt].to=v;
    	head[u]=cnt;
    }
    int sx_min(int x,int y)
    {
    	return dfn[x]<dfn[y]?x:y;
    }
    struct ST
    {
    	int fminn[25][200010];
    	void init(int n)
    	{
    		for(int j=1;j<=__lg(n);j++)
    		{
    			for(int i=1;i+(1<<j)-1<=n;i++)
    			{
    				fminn[j][i]=sx_min(fminn[j-1][i],fminn[j-1][i+(1<<(j-1))]);	
    			}
    		}
    	}
    	int query(int l,int r)
    	{
    		int t=__lg(r-l+1);
    		return sx_min(fminn[t][l],fminn[t][r-(1<<t)+1]);
    	}
    }T;
    void dfs(int x,int fa)
    {
    	tot++;
    	dfn[x]=tot;
    	T.fminn[0][tot]=fa;
    	rk[tot]=x;
    	dep[x]=dep[fa]+1;
    	for(int i=head[x];i!=0;i=e[i].nxt)
    	{
    		if(e[i].to!=fa)
    		{
    			dfs(e[i].to,x);
    		}
    	}
    }
    int lca(int u,int v)
    {
    	if(dfn[u]>dfn[v])
    	{	
    		swap(u,v);
    	}
    	return (dfn[u]==dfn[v])?u:T.query(dfn[u]+1,dfn[v]);
    }
    int dis(int x,int y)
    {
    	x=rk[x];
    	y=rk[y];
    	return dep[x]+dep[y]-2*dep[lca(x,y)];
    }
    void init(int n,int m)
    {
    	klen=n/sqrt(m)+1;
    	ksum=n/klen;
    	for(int i=1;i<=ksum;i++)
    	{
    		L[i]=R[i-1]+1;
    		R[i]=R[i-1]+klen;
    	}
    	if(R[ksum]<n)
    	{
    		ksum++;
    		L[ksum]=R[ksum-1]+1;
    		R[ksum]=n;
    	}
    	for(int i=1;i<=ksum;i++)
    	{
    		for(int j=L[i];j<=R[i];j++)
    		{
    			pos[j]=i;
    		}
    	}
    }
    void del1(int x,int &sum)
    {
    	num[x]--;
    	if(num[x]==0)
    	{
    		sum-=dis(pre[x],x);
    		sum-=dis(x,nxt[x]);
    		sum+=dis(pre[x],nxt[x]);
    		pre[nxt[x]]=pre[x];
    		nxt[pre[x]]=nxt[x];
    	}
    }
    void del2(int x,int &sum)
    {
    	num[x]--;
    	s.push_back((quality){pre[x],nxt[x],x});
    	if(num[x]==0)
    	{
    		sum-=dis(pre[x],x);
    		sum-=dis(x,nxt[x]);
    		sum+=dis(pre[x],nxt[x]);
    		pre[nxt[x]]=pre[x];
    		nxt[pre[x]]=nxt[x];
    	}
    }
    int main()
    {
    	int n,m,k,u,v,sum=0,tmp,l=1,r,last,i,j;
    	cin>>n>>m;
    	k=1;
    	for(i=1;i<=n-1;i++)
    	{
    		cin>>u>>v;
    		add(u,v);
    		add(v,u);
    	}
    	dfs(1,0);
    	T.init(n);
    	init(m,k);
    	for(i=1;i<=m;i++)
    	{
    		cin>>a[i];
    	}
    	for(i=1;i<=k;i++)
    	{
    		q[i].l=1;
    		q[i].r=m;
    		q[i].id=i;
    	}
    	sort(q+1,q+1+k,q_cmp);
    	for(i=1;i<=k;i++)
    	{
    		if(pos[q[i].l]!=pos[q[i-1].l])
    		{
    			memset(num,0,sizeof(num));
    			for(j=L[pos[q[i].l]];j<=m;j++)
    			{
    				num[dfn[a[j]]]++;
    			}
    			last=0;
    			for(j=1;j<=n;j++)
    			{
    				pre[j]=last;
    				last=(num[j]==0)?last:j;
    			}
    			for(j=1;j<=n;j++)
    			{
    				pre[j]=(pre[j]==0)?last:pre[j];
    			}
    			last=0;
    			for(j=n;j>=1;j--)
    			{
    				nxt[j]=last;
    				last=(num[j]==0)?last:j;
    			}
    			for(j=n;j>=1;j--)
    			{
    				nxt[j]=(nxt[j]==0)?last:nxt[j];
    			}
    			sum=0;
    			for(j=1;j<=n;j++)
    			{
    				sum+=(num[j]!=0)*dis(pre[j],j);
    			}
    			r=m;
    		}
    		l=L[pos[q[i].l]];
    		while(r>q[i].r)
    		{
    			del1(dfn[a[r]],sum);
    			r--;
    		}
    		tmp=sum;
    		while(l<q[i].l)
    		{
    			del2(dfn[a[l]],sum);
    			l++;
    		}
    		ans[q[i].id]=sum/2+1;
    		sum=tmp;
    		while(s.empty()==0)
    		{
    			x=s.back();
    			s.pop_back();
    			if(num[x.pos]==0)
    			{
    				nxt[x.pre]=x.pos;
    				pre[x.nxt]=x.pos;
    			}
    			num[x.pos]++;
    		}
    	}
    	for(i=1;i<=k;i++)
    	{
    		cout<<ans[i]<<endl;
    	}
    	return 0;
    }
    

[ABC368F] Dividing Game

  • 多倍经验: Div/de

  • 处理出每个数的 \(SG\) 函数后,然后当做有向图游戏来做即可。

    点击查看代码
    int sg[100010];
    map<int,int>f;
    int main()
    {
    	int n,x,ans=0,i,j;
    	cin>>n;
    	sg[1]=0;
    	for(i=2;i<=100005;i++)
    	{
    		f.clear();
    		f[sg[1]]=1;
    		for(j=2;j<=sqrt(i);j++)
    		{
    			if(i%j==0)
    			{
    				f[sg[j]]=1;
    				f[sg[i/j]]=1;
    			}
    		}
    		for(j=0;j<=100005;j++)
    		{
    			if(f.find(j)==f.end())
    			{
    				sg[i]=j;
    				break;
    			}
    		}
    	}
    	for(i=1;i<=n;i++)
    	{
    		cin>>x;
    		ans^=sg[x];
    	}
    	if(ans==0)
    	{
    		cout<<"Bruno"<<endl;
    	}
    	else
    	{
    		cout<<"Anna"<<endl;
    	}
    	return 0;
    }	
    

8.26

闲话

  • 颓。

做题纪要

8.27

闲话

  • 颓。

做题纪要

8.28

闲话

  • 颓。

做题纪要

8.29

闲话

  • 颓。

做题纪要

8.30

闲话

  • 颓。
  • 被分到 \(201\) 了,鑫考云由 HS 也改成 HZ 了,班主任改成初中时的代班主任了。因班级内部同学基本不变,所以不需要再当新生去嘱咐,晚上的班会自然就取消了。

做题纪要

[ABC034C] 経路

  • \(\dbinom{n+m-2}{n-1}\) 即为所求。

    点击查看代码
    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;
    }
    ll C(ll n,ll m,ll p)
    {
        if(n>=m&&n>=0&&m>=0)
        {
            ll up=1,down=1;
            for(ll i=n-m+1;i<=n;i++)
            {
                up=up*i%p;
            }
            for(ll i=1;i<=m;i++)
            {
                down=down*i%p;
            }
            return up*qpow(down,p-2,p)%p;
        }
        else
        {
            return 0;
        }
    }
    int main()
    {
        ll n,m;
        cin>>n>>m;
        cout<<C(n+m-2,n-1,p)<<endl;
        return 0;
    }
    

8.31

闲话

  • 上午接着颓。
  • 听家长说刚开学就分奥赛班,分别是 \(201 \sim 203,301 \sim 305,401 \sim 402\) ,感觉不是很可信,毕竟夏令营刚结束就筛人不是很现实。
  • 下午返校,直接去的 HZ 。原班主任因为是奥赛中心教研室主任,且前几个月需要在各校区转悠,事情太多了就暂时不当我们班主任了,后面可能会换回来;饭卡直接领的 HZ 饭卡,临时饭卡班主任说和餐卡管理中心说一下让从西扩改到这边来;录取通知书和准考证没有收;新生进校须知上所说的“不让放行李箱”也没有照做,床下除了发的箱子外还有位置放恰好一个行李箱;班级在教学楼东头,有点远,正好挨着老师备课区(外面牌子上写的是语文、政治、历史、地理,但实际不然),成功成为 \(2\) 部在 \(2\) 楼的唯一一个班。
  • 到班后班主任说了下军训期间的安排,简单概括就是有事就说事,没事就干自己的事或者上奥赛。投影屏三原色比率不是很协调导致整个界面蓝乎乎的,然后班主任给年级部打电话让人来修,但对面说因为现在刚开学都忙着管新学生呢没空去修电脑,并得知了年级部对 \(201\) “直升班”的称呼。班主任让继承完班委后 \(miaomiao\) 就进来了,尝试修投影屏但他不会,所以又溜了。
  • 晚上班主任让外面先整理下暑假学习的东西,准备后面的奥赛学习(补补学案作业什么的),然后就忘记了给我们说 \(miaomiao\) 让我们直接来机房,于是 \(miaomiao\) 在机房等了一晚上也没人来。纪律方面因为到了 HZ ,领导居多,所以管理要比 HS 严亿点,但我们估计都能适应。
  • 课表里面没有政史地,其他杂课视情况有。

做题纪要

posted @ 2024-08-20 21:38  hzoi_Shadow  阅读(90)  评论(0编辑  收藏  举报
扩大
缩小