高一下二月下旬日记

2.21

闲话

  • 上午 7:3012:00 去新机房打学校 OJ 的模拟赛。
  • 下午放完每日一歌后 huge 说他把昨天和今天的模拟赛成绩汇总了一下放到了比赛里。因为昨天的一些事故可能导致我们发挥不太好,但要自己心里有点数,在和外校竞争的同时也是需要和本校的竞争的。现在组题不是很能保证难度贴近省选,最担心的情况就是第一天打得特别好后第二天直接炸了,我们要自己进行合理的调整。
  • 晚上讲题。

做题纪要

[ABC277Ex] Constrained Sums

  • 观察到 nm 较小,可以用 m+2 个变量刻画 ai 的取值。具体地,设 xi,j(j[0,m+1]) 表示 [aij] ,强制钦定限制 xi,0=1,xi,m+1=0

  • 然后跑 2SAT 即可。

    点击查看代码
    int dfn[2100010],low[2100010],ins[2100010],col[2100010],ans[2100010],tot=0,scc_cnt=0;
    vector<int>e[2100010];
    stack<int>s;
    void add(int u,int v)
    {
    	e[u].push_back(v);
    }
    void tarjan(int x)
    {
    	tot++;  dfn[x]=low[x]=tot;
    	s.push(x);  ins[x]=1;
    	for(int i=0;i<e[x].size();i++)
    	{
    		if(dfn[e[x][i]]==0)
    		{
    			tarjan(e[x][i]);
    			low[x]=min(low[x],low[e[x][i]]);
    		}
    		else  if(ins[e[x][i]]==1)  low[x]=min(low[x],dfn[e[x][i]]);
    	}
    	if(dfn[x]==low[x])
    	{
    		scc_cnt++;
    		int tmp=0;
    		while(tmp!=x)
    		{
    			tmp=s.top();  s.pop();
    			ins[tmp]=0;
    			col[tmp]=scc_cnt;
    		}
    	}
    }
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	int n,m,q,a,b,l,r,flag=0,i,j;
    	cin>>n>>m>>q;
    	for(i=1;i<=n;i++)
    	{
    		for(j=1;j<=m+1;j++)
    		{
    			add((i-1)*(m+2)+j+1,(i-1)*(m+2)+j);
    			add((i-1)*(m+2)+j+n*(m+2),(i-1)*(m+2)+j+1+n*(m+2));
    		}
    		add((i-1)*(m+2)+1+n*(m+2),(i-1)*(m+2)+1);
    		add((i-1)*(m+2)+m+2,(i-1)*(m+2)+m+2+n*(m+2));
    	}
    	for(j=1;j<=q;j++)
    	{
    		cin>>a>>b>>l>>r;
    		for(i=1;i<=m+2;i++)
    		{
    			if(l-(i-1)+1>=m+2)
    			{
    				add((a-1)*(m+2)+i+n*(m+2),(a-1)*(m+2)+i);
    				add((b-1)*(m+2)+i+n*(m+2),(b-1)*(m+2)+i);
    			}
    			else  if(l-(i-1)+1>=0)
    			{
    				add((a-1)*(m+2)+i+n*(m+2),(b-1)*(m+2)+l-(i-1)+1+1);
    				add((b-1)*(m+2)+i+n*(m+2),(a-1)*(m+2)+l-(i-1)+1+1);
    			}
    			if(r-(i-1)+1<0)
    			{
    				add((a-1)*(m+2)+i,(a-1)*(m+2)+i+n*(m+2));
    				add((b-1)*(m+2)+i,(b-1)*(m+2)+i+n*(m+2));
    			}
    			else  if(r-(i-1)+1<m+2)
    			{
    				add((a-1)*(m+2)+i,(b-1)*(m+2)+r-(i-1)+1+1+n*(m+2));
    				add((b-1)*(m+2)+i,(a-1)*(m+2)+r-(i-1)+1+1+n*(m+2));
    			}
    		}
    	}
    	for(i=1;i<=2*n*(m+2);i++)  if(dfn[i]==0)  tarjan(i);
    	for(i=1;i<=n;i++)
    	{
    		for(j=1;j<=m+2;j++)
    		{
    			flag|=(col[(i-1)*(m+2)+j]==col[(i-1)*(m+2)+j+n*(m+2)]);
    			if(col[(i-1)*(m+2)+j+n*(m+2)]<col[(i-1)*(m+2)+j])  
    			{
    				ans[i]=j-2;
    				break;
    			}
    		}
    	}
    	if(flag==1)  cout<<-1<<endl;
    	else  for(i=1;i<=n;i++)  cout<<ans[i]<<" ";
    	return 0;
    }
    

CF1697F Too Many Constraints

  • m+1 个变量进行刻画。

    点击查看代码
    int dfn[460010],low[460010],ins[460010],col[460010],ans[460010],tot=0,scc_cnt=0;
    vector<int>e[460010];
    stack<int>s;
    void add(int u,int v)
    {
    	e[u].push_back(v);
    }
    void tarjan(int x)
    {
    	tot++;  dfn[x]=low[x]=tot;
    	s.push(x);  ins[x]=1;
    	for(int i=0;i<e[x].size();i++)
    	{
    		if(dfn[e[x][i]]==0)
    		{
    			tarjan(e[x][i]);
    			low[x]=min(low[x],low[e[x][i]]);
    		}
    		else  if(ins[e[x][i]]==1)  low[x]=min(low[x],dfn[e[x][i]]);
    	}
    	if(dfn[x]==low[x])
    	{
    		scc_cnt++;
    		int tmp=0;
    		while(tmp!=x)
    		{
    			tmp=s.top();  s.pop();
    			ins[tmp]=0;
    			col[tmp]=scc_cnt;
    		}
    	}
    }
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	int t,n,m,q,pd,a,b,l,r,flag,i,j;
    	cin>>t;
    	for(;t>=1;t--)
    	{
    		cin>>n>>q>>m;  flag=tot=scc_cnt=0;
    		for(i=1;i<=2*n*(m+1);i++)
    		{
    			e[i].clear();
    			dfn[i]=low[i]=ins[i]=col[i]=ans[i]=0;
    		}
    		for(i=1;i<=n;i++)
    		{
    			for(j=1;j<=m;j++)
    			{
    				add((i-1)*(m+1)+j+1,(i-1)*(m+1)+j);
    				add((i-1)*(m+1)+j+n*(m+1),(i-1)*(m+1)+j+1+n*(m+1));
    			}
    			add((i-1)*(m+1)+1+n*(m+1),(i-1)*(m+1)+1);
    			add((i-1)*(m+1)+m+1,(i-1)*(m+1)+m+1+n*(m+1));
    		}
    		for(i=1;i<=n-1;i++)
    		{
    			for(j=1;j<=m+1;j++)
    			{
    				add((i-1)*(m+1)+j,i*(m+1)+j);
    				add(i*(m+1)+j+n*(m+1),(i-1)*(m+1)+j+n*(m+1));
    			}
    		}
    		for(j=1;j<=q;j++)
    		{
    			cin>>pd;
    			if(pd==1)
    			{
    				cin>>a>>l;
    				add((a-1)*(m+1)+l,(a-1)*(m+1)+l+1);
    				add((a-1)*(m+1)+l+1+n*(m+1),(a-1)*(m+1)+l+n*(m+1));
    			}
    			if(pd==2)
    			{
    				cin>>a>>b>>r;
    				for(i=1;i<=m+1;i++)
    				{
    					if(r-i<0)
    					{
    						add((a-1)*(m+1)+i,(a-1)*(m+1)+i+n*(m+1));
    						add((b-1)*(m+1)+i,(b-1)*(m+1)+i+n*(m+1));
    					}
    					else  if(r-i<m+1)
    					{
    						add((a-1)*(m+1)+i,(b-1)*(m+1)+r-i+1+n*(m+1));
    						add((b-1)*(m+1)+i,(a-1)*(m+1)+r-i+1+n*(m+1));
    					}
    				}
    			}
    			if(pd==3)
    			{
    				cin>>a>>b>>l;
    				for(i=1;i<=m+1;i++)
    				{
    					if(l-i>=m+1)
    					{
    						add((a-1)*(m+1)+i+n*(m+1),(a-1)*(m+1)+i);
    						add((b-1)*(m+1)+i+n*(m+1),(b-1)*(m+1)+i);
    					}
    					else  if(l-i>=0)
    					{
    						add((a-1)*(m+1)+i+n*(m+1),(b-1)*(m+1)+l-i+1);
    						add((b-1)*(m+1)+i+n*(m+1),(a-1)*(m+1)+l-i+1);
    					}
    				}
    			}
    		}
    		for(i=1;i<=2*n*(m+1);i++)  if(dfn[i]==0)  tarjan(i);
    		for(i=1;i<=n;i++)
    		{
    			for(j=1;j<=m+1;j++)
    			{
    				flag|=(col[(i-1)*(m+1)+j]==col[(i-1)*(m+1)+j+n*(m+1)]);
    				if(col[(i-1)*(m+1)+j+n*(m+1)]<col[(i-1)*(m+1)+j])  
    				{
    					ans[i]=j-1;
    					break;
    				}
    			}
    		}
    		if(flag==1)  cout<<-1<<endl;
    		else  
    		{
    			for(i=1;i<=n;i++)  cout<<ans[i]<<" ";
    			cout<<endl;
    		}
    	}
    	return 0;
    }
    

luogu P6378 [PA2010] Riddle

  • 前后缀优化建图。

    • 以前缀为例。
    • 对于每个前缀建立一个虚点 prei ,初始有 preiprei1,preiai 。然后连接 aiprei1
    点击查看代码
    int dfn[4000010],low[4000010],ins[4000010],col[4000010],a[4000010],pre[4000010],suf[4000010],tot=0,scc_cnt=0;
    vector<int>e[4000010];
    stack<int>s;
    void add(int u,int v)
    {
    	e[u].push_back(v);
    }
    void tarjan(int x)
    {
    	tot++;  dfn[x]=low[x]=tot;
    	s.push(x);  ins[x]=1;
    	for(int i=0;i<e[x].size();i++)
    	{
    		if(dfn[e[x][i]]==0)
    		{
    			tarjan(e[x][i]);
    			low[x]=min(low[x],low[e[x][i]]);
    		}
    		else  if(ins[e[x][i]]==1)  low[x]=min(low[x],dfn[e[x][i]]);
    	}
    	if(dfn[x]==low[x])
    	{
    		scc_cnt++;
    		int tmp=0;
    		while(tmp!=x)
    		{
    			tmp=s.top();  s.pop();
    			ins[tmp]=0;
    			col[tmp]=scc_cnt;
    		}
    	}
    }
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	int n,m,u,v,k,flag=1,id,i,j;
    	cin>>n>>m>>k;  id=2*n;
    	for(i=1;i<=m;i++)
    	{
    		cin>>u>>v;
    		add(u+n,v);  add(v+n,u);
    	}
    	for(j=1;j<=k;j++)
    	{
    		cin>>u;
    		for(i=1;i<=u;i++)
    		{
    			cin>>a[i];
    			id++;  pre[i]=id;
    			id++;  suf[i]=id;
    			add(pre[i],a[i]+n);  add(suf[i],a[i]+n);
    		}
    		for(i=2;i<=u;i++)
    		{
    			add(pre[i],pre[i-1]);  add(a[i],pre[i-1]);
    		}
    		for(i=u-1;i>=1;i--)
    		{
    			add(suf[i],suf[i+1]);  add(a[i],suf[i+1]);
    		}
    	}
    	for(i=1;i<=id;i++)  if(dfn[i]==0)  tarjan(i);
    	for(i=1;i<=n;i++)  if(col[i]==col[i+n])  flag=0;
    	cout<<(flag==1?"TAK":"NIE")<<endl;
    	return 0;
    }
    

P1025. Easy Problem

luogu P3825 [NOI2017] 游戏

  • 除了地图 x 之外 2SAT 可以直接做。

  • 因为 d8 ,不妨枚举每个 x 能够选择的两种状态。直接实现是 O(3d(n+m)) 的,观察到多枚举的一种情况实际上算重了,只枚举两次的时间复杂度为 O(2d(n+m)) 可以通过。

    点击查看代码
    int a[100010],_a[100010],b[100010],_b[100010],f[100010],dfn[100010],low[100010],ins[100010],col[100010],tot=0,scc_cnt=0;
    char t[100010];
    vector<int>e[100010];
    stack<int>s;
    void add(int u,int v)
    {
    	e[u].push_back(v);
    }
    void tarjan(int x)
    {
    	tot++;  dfn[x]=low[x]=tot;
    	s.push(x);  ins[x]=1;
    	for(int i=0;i<e[x].size();i++)
    	{
    		if(dfn[e[x][i]]==0)
    		{
    			tarjan(e[x][i]);
    			low[x]=min(low[x],low[e[x][i]]);
    		}
    		else  if(ins[e[x][i]]==1)  low[x]=min(low[x],dfn[e[x][i]]);
    	}
    	if(dfn[x]==low[x])
    	{
    		scc_cnt++;
    		int tmp=0;
    		while(tmp!=x)
    		{
    			tmp=s.top();  s.pop();
    			ins[tmp]=0;
    			col[tmp]=scc_cnt;
    		}
    	}
    }
    int rev(int x,int op)
    {
    	return (x==0||x==1)?((op==1)?2:(x^1)):op;
    }
    bool solve(int n,int d,int m)
    {
    	tot=scc_cnt=0;
    	for(int i=1;i<=2*n;i++)  e[i].clear();
    	memset(dfn,0,sizeof(dfn));
    	memset(low,0,sizeof(low));
    	memset(ins,0,sizeof(ins));
    	memset(col,0,sizeof(col));
    	for(int i=1;i<=m;i++)
    	{
    		if(t[a[i]]-'a'!=_a[i])
    		{
    			if(t[b[i]]-'a'==_b[i])  
    				add(a[i]+(rev(t[a[i]]-'a',1)==_a[i])*n,a[i]+(rev(t[a[i]]-'a',0)==_a[i])*n);
    			else
    			{
    				add(a[i]+(rev(t[a[i]]-'a',1)==_a[i])*n,b[i]+(rev(t[b[i]]-'a',1)==_b[i])*n);
    				add(b[i]+(rev(t[b[i]]-'a',0)==_b[i])*n,a[i]+(rev(t[a[i]]-'a',0)==_a[i])*n);
    			}
    		}
    	}
    	for(int i=1;i<=2*n;i++)  if(dfn[i]==0)  tarjan(i);
    	for(int i=1;i<=n;i++)  if(col[i]==col[i+n])  return false;
    	for(int i=1;i<=n;i++)  cout<<(char)('A'+rev(t[i]-'a',col[i+n]<col[i]));
    	return true;
    }
    void dfs(int pos,int n,int d,int m)
    {
    	if(pos==d+1)
    	{
    		if(solve(n,d,m)==true)  exit(0);
    	}
    	else
    	{	
    		t[f[pos]]='a';  dfs(pos+1,n,d,m);
    		t[f[pos]]='b';  dfs(pos+1,n,d,m);
    	}
    }
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	int n,m,d,cnt=0,i;
    	char pd;
    	cin>>n>>d>>(t+1)>>m;
    	for(i=1;i<=n;i++)
    	{
    		if(t[i]=='x')
    		{
    			cnt++;  f[cnt]=i;
    		}
    	}
    	for(i=1;i<=m;i++)
    	{
    		cin>>a[i]>>pd;  _a[i]=pd-'A';
    		cin>>b[i]>>pd;  _b[i]=pd-'A';
    	}
    	dfs(1,n,d,m);
    	cout<<-1<<endl;
    	return 0;
    }
    
    

2.22

闲话

  • @Qyun 分享了 luogu P7361 「JZOI-1」拜神@hh弟中弟 分享了 luogu P7361 「JZOI-1」拜神
  • feifei 说今天 accoders NOI 的模拟赛挪到了明天,顺便看看题目的质量好不好,我们如果有需要 accoders NOI 上文件的话抓紧保存,他一会儿就把 accoders NOI 关了。
  • huge 说今天没有模拟赛,要不就上午正常体活吧,我们说下午体活时间还长一点,所以保留了下午的体活。 huge 调侃到这是贪心策略。

做题纪要

CF1270G Subset with Zero Sum

  • inaii1 移项得到 1iain ,从 iiai 连一条有向边,环上的 ai 之和等于 0

    点击查看代码
    int a[1000010],v[1000010],vis[1000010];
    vector<int>ans;
    int dfs(int x)
    {
    	vis[x]=1;
    	return (vis[v[x]]==1)?x:dfs(v[x]);
    }
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	int t,n,rt,i;
    	scanf("%d",&t);
    	for(;t>=1;t--)
    	{
    		scanf("%d",&n);  ans.clear();
    		for(i=1;i<=n;i++)
    		{
    			scanf("%d",&a[i]);
    			v[i]=i-a[i];
    			vis[i]=0;
    		}
    		rt=dfs(1);  ans.push_back(rt);
    		for(i=v[rt];i!=rt;i=v[i])  ans.push_back(i);
    		printf("%d\n",ans.size());
    		for(i=0;i<ans.size();i++)  printf("%d ",ans[i]);
    		printf("\n");
    	}
    	return 0;
    }
    

P1026. Trash Problem

CF25C Roads in Berland

  • [ABC375F] Road Blocked 一样做即可。

    点击查看代码
    ll dis[310][310];
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif  
    	ll n,m,u,v,w,ans=0,i,j,k;
    	cin>>n;
    	for(i=1;i<=n;i++)
    	{
    		for(j=1;j<=n;j++)  cin>>dis[i][j];
    	}
    	cin>>m;
    	for(k=1;k<=m;k++)
    	{
    		cin>>u>>v>>w;  ans=0;
    		dis[u][v]=dis[v][u]=min(dis[u][v],w);
    		for(i=1;i<=n;i++)
    		{
    			for(j=1;j<=n;j++)  dis[i][j]=min(dis[i][j],dis[i][u]+dis[u][j]);
    		}
    		for(i=1;i<=n;i++)
    		{
    			for(j=1;j<=n;j++)  dis[i][j]=min(dis[i][j],dis[i][v]+dis[v][j]);
    		}
    		for(i=1;i<=n;i++)
    		{
    			for(j=i+1;j<=n;j++)  ans+=dis[i][j];
    		}
    		cout<<ans<<" ";
    	}
    	return 0;
    }
    

CF1553H XOR and Distance

  • 钦定二进制表示下从左往右依次为第 k1 到第 0 位。

  • ai 插到 01Trie 上,可以直接从 LCP 处考虑贡献。

  • x 这一位是 0 时查询 1 方向的最小值 x 减去 0 方向的最大值 x ;当 x 这一位是 1 时查询 0 方向的最小值 x 减去 1 方向的最大值 x

  • 考虑自下而上考虑每棵子树对二进制表示下一段后缀相同的若干 x 的贡献。枚举 x ,每一个 x 都遍历一遍整颗 Trie ,为了简便的处理,考虑继承 x1 的信息时通过翻转左右子树得到当前答案,这又需要我们尽可能快速地维护子树翻转导致的答案变化,此时就规避了 x 的影响。

  • rt 对应区间为 [lrt,rrt] ,我们可以仅维护 [lrt,rrt] 内的数距离 lrt 的最大值、最小值的差值从而交换子树时对子树内部信息没有任何影响,只通过一次 pushup 得到最终答案。

  • 朴素实现时第 i 位一共被翻转了 2ki 次,导致的遍历 Trie 数节点数量为 2ki×2ki=4ki ,无法接受。

  • 考虑优化枚举顺序,枚举 x 二进制表示下颠倒后的十进制值,使得第 i 位一共被翻转了 2i 次,遍历 Trie 数节点数量为 2i×2ki=2k ,可以接受。

    点击查看代码
    const int inf=0x3f3f3f3f;
    int a[550010],ans[550010];
    struct Tire
    {
    	int root,rt_sum=0;
    	struct node
    	{
    		int son[2],maxx,minn,len,ans;
    		node()
    		{
    			son[0]=son[1]=len=0;
    			maxx=-inf;
    			minn=ans=inf;
    		}
    	}tree[550010<<4];
    	#define lson(rt) (tree[rt].son[0])
    	#define rson(rt) (tree[rt].son[1])
    	#define dson(rt,d) (tree[rt].son[d])
    	void pushup(int rt)
    	{
    		tree[rt].maxx=max(tree[lson(rt)].maxx,tree[rson(rt)].maxx+tree[rt].len/2);
    		tree[rt].minn=min(tree[lson(rt)].minn,tree[rson(rt)].minn+tree[rt].len/2);
    		tree[rt].ans=min({tree[lson(rt)].ans,tree[rson(rt)].ans,
    		tree[rson(rt)].minn-tree[lson(rt)].maxx+tree[rt].len/2});
    	}
    	int build_rt()
    	{
    		rt_sum++;
    		return rt_sum;
    	}
    	void insert(int &rt,int bit,int val)
    	{
    		if(rt==0)  
    		{
    			rt=build_rt();
    			tree[rt].len=1<<bit;
    		}
    		if(bit==0)
    		{
    			tree[rt].maxx=tree[rt].minn=0; // 只保留必要的后缀,其他位的贡献在 pushup 时加回来
    			return;
    		}
    		insert(dson(rt,(val>>(bit-1))&1),bit-1,val);
    		pushup(rt);
    	}
    	void reverse(int rt,int bit,int _bit)
    	{
    		if(bit>_bit)
    		{
    			reverse(lson(rt),bit-1,_bit);  reverse(rson(rt),bit-1,_bit);
    		}
    		else  swap(lson(rt),rson(rt));
    		pushup(rt);
    	}
    }T;
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	int n,k,id,i,j;
    	cin>>n>>k;
    	for(i=1;i<=n;i++)
    	{
    		cin>>a[i];  T.insert(T.root,k,a[i]);
    	}
    	for(i=0;i<=(1<<k)-1;i++)
    	{ 
    		id=0;
    		for(j=0;j<=k-1&&i-1>=0;j++)
    		{
    			if((((i-1)>>(k-j-1))&1)!=((i>>(k-j-1))&1))  T.reverse(T.root,k-1,j);
    			id+=((i>>j)&1)<<(k-j-1);
    		}
    		ans[id]=T.tree[T.root].ans;
    	}
    	for(i=0;i<=(1<<k)-1;i++)  cout<<ans[i]<<" ";
    	return 0;
    }
    

2.23

闲话

  • 早上有体活。
  • 上午 7:3012:00 去新机房打学校 OJ 的模拟赛。
  • 下午讲完题后 huge 说学校突然出了个很滑稽的规定:为保证周六日学生的“充分”自主性,黑板不能有板书,周测的时候老师不能进班。这就导致盯周测的老师站在教室门口一条腿在屋内一条腿在屋外。沧州的一些非寄宿学校听说已经落实了每周双休的制度的,但衡水这边因寄宿居多,可能从原来的三周放一天假改成三周放两天假假装双休,平常再搞搞形式主义之类的。再说了要是真落实下去,除了我们这帮学奥赛普通学生在充分自主性的条件真能学得下去吗。记得他当时上学的时候每周一还有“无批评,三自嘱”日,然后就被调侃是“无自主,三批评”日,不知道我们现在广播里还放不放这个,我们说每次放的时候都听不清他说的是什么。
  • 晚上到 21:40 后就直接和 @ccxswl 一起回宿舍了,出机房后发现校园里一个学生也没有,我们以为年级统一安排了活动稍微晚下课几分钟遂没管,回到宿舍直到 22:05 仍一个人也没有,又决定回机房看看是什么情况。到机房的时候又正赶上 miaomiao 让下课,和 miaomiao 简单说明情况就走了。
  • 回到宿舍问班里同学说为了顺应全国的统一规定,晚上熄灯时间调整到了 22:30 ,但早上起床时间调整到了 6:30 ,早预备时间挪到了晚三,早读拆成了吃早饭前和吃早饭后两半。好奇什么时候会把时间调整回去。

做题纪要

CF1436E Complicated Computations

  • 考虑 mex=x 的贡献如何处理。不妨按照 x 出现的位置将 {a} 划分成若干个区间,每个区间内 [1,x1] 都出现过 x 才会成为其 mex

  • 线段树维护单点修改、区间查询最小值即可。

    点击查看代码
    int a[100010],last[100010],vis[100010];
    struct SMT
    {
    	struct SegmentTree
    	{
    		int minn;
    	}tree[400010];
    	#define lson(rt) (rt<<1)
    	#define rson(rt) (rt<<1|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].minn=0;
    		if(l==r)  return;
    		int mid=(l+r)/2;
    		build(lson(rt),l,mid);  build(rson(rt),mid+1,r);
    	}
    	void update(int rt,int l,int r,int pos,int val)
    	{
    		if(l==r)
    		{
    			tree[rt].minn=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(x<=l&&r<=y)  return tree[rt].minn;
    		int mid=(l+r)/2;
    		if(y<=mid)  return query(lson(rt),l,mid,x,y);
    		if(x>mid)  return query(rson(rt),mid+1,r,x,y);
    		return min(query(lson(rt),l,mid,x,y),query(rson(rt),mid+1,r,x,y));
    	}
    }T;
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	int n,i;
    	cin>>n;
    	T.build(1,1,n);
    	for(i=1;i<=n;i++)  
    	{
    		cin>>a[i];
    		if(a[i]!=1)  
    		{
    			vis[1]=1;
    			vis[a[i]]|=(T.query(1,1,n,1,a[i]-1)>last[a[i]]);
    		}
    		last[a[i]]=i;
    		T.update(1,1,n,a[i],i);
    	}
    	for(i=2;i<=n+1;i++)  vis[i]|=(T.query(1,1,n,1,i-1)>last[i]);
    	for(i=1;vis[i]==1;i++);
    	cout<<i<<endl;	
    	return 0;
    }
    

luogu P6018 [Ynoi2010] Fusion tree

  • 从低到高建 01Trie ,只维护儿子节点的信息,父亲节点的信息单独维护。

  • 单点修改直接在 01Trie 上删除;否则考虑进位的过程翻转左右子树。根据子树大小进行 pushup

  • 记得打懒惰标记。

    点击查看代码
    int fa[500010],a[500010];
    vector<int>e[500010];
    void add(int u,int v)
    {
    	e[u].push_back(v);
    }
    void dfs(int x,int father)
    {
    	fa[x]=father;
    	for(int i=0;i<e[x].size();i++)
    	{
    		if(e[x][i]!=father)  dfs(e[x][i],x);
    	}    
    }
    struct Trie
    {
    	int root[500010],lazy[500010],rt_sum=0;
    	struct node
    	{
    		int son[2],siz,sum;
    	}tree[500010<<5];
    	#define lson(rt) (tree[rt].son[0])
    	#define rson(rt) (tree[rt].son[1])
    	#define dson(rt,d) (tree[rt].son[d])
    	int build_rt()
    	{
    		rt_sum++;
    		lson(rt_sum)=rson(rt_sum)=tree[rt_sum].siz=tree[rt_sum].sum=0;
    		return rt_sum;
    	}
    	void pushup(int rt)
    	{
    		tree[rt].siz=tree[lson(rt)].siz+tree[rson(rt)].siz;
    		tree[rt].sum=(tree[lson(rt)].sum<<1)^((tree[rson(rt)].sum<<1)|(tree[rson(rt)].siz&1));
    	}
    	void insert(int &rt,int bit,int val)
    	{
    		rt=(rt==0)?build_rt():rt;
    		if(bit==20)
    		{
    			tree[rt].siz++;
    			return;
    		}
    		insert(dson(rt,(val>>bit)&1),bit+1,val);
    		pushup(rt);
    	}   
    	void del(int rt,int bit,int val)
    	{
    		rt=(rt==0)?build_rt():rt;
    		if(bit==20)
    		{
    			tree[rt].siz--;
    			return;
    		}
    		del(dson(rt,(val>>bit)&1),bit+1,val);
    		pushup(rt);
    	}
    	void update(int rt)
    	{
    		if(rt==0)  return;
    		swap(lson(rt),rson(rt));
    		update(lson(rt));
    		pushup(rt);
    	}
    	int query(int rt)
    	{
    		return tree[rt].sum;
    	}
    }T;
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	int n,m,pd,u,v,i;
    	cin>>n>>m;
    	for(i=1;i<=n-1;i++)
    	{
    		cin>>u>>v;
    		add(u,v);  add(v,u);
    	}
    	dfs(1,0);
    	for(i=1;i<=n;i++)
    	{
    		cin>>a[i];
    		if(fa[i]!=0)  T.insert(T.root[fa[i]],0,a[i]);
    	}
    	for(i=1;i<=m;i++)
    	{
    		cin>>pd>>u;
    		if(pd==1)
    		{
    			T.lazy[u]++;  T.update(T.root[u]);
    			if(fa[u]!=0)
    			{
    				if(fa[fa[u]]!=0)  T.del(T.root[fa[fa[u]]],0,a[fa[u]]+T.lazy[fa[fa[u]]]);
    				a[fa[u]]++;
    				if(fa[fa[u]]!=0)  T.insert(T.root[fa[fa[u]]],0,a[fa[u]]+T.lazy[fa[fa[u]]]);
    			}
    		}
    		if(pd==2)
    		{
    			cin>>v;
    			if(fa[u]!=0)  T.del(T.root[fa[u]],0,a[u]+T.lazy[fa[u]]);
    			a[u]-=v;
    			if(fa[u]!=0)  T.insert(T.root[fa[u]],0,a[u]+T.lazy[fa[u]]);
    		}
    		if(pd==3)  cout<<(T.query(T.root[u])^(a[fa[u]]+T.lazy[fa[fa[u]]]))<<endl;
    	}
    	return 0;
    }
    

luogu P6623 [省选联考 2020 A 卷] 树

  • 01Trie 合并。

    点击查看代码
    int a[530010],fa[530010];
    ll ans=0;
    vector<int>e[530010];
    void add(int u,int v)
    {
    	e[u].push_back(v);
    }
    struct Trie
    {
    	int root[530010],rt_sum;
    	struct node
    	{
    		int son[2],siz,sum;
    	}tree[530010<<5];
    	#define lson(rt) (tree[rt].son[0])
    	#define rson(rt) (tree[rt].son[1])
    	#define dson(rt,d) (tree[rt].son[d])
    	int build_rt()
    	{
    		rt_sum++;
    		lson(rt_sum)=rson(rt_sum)=tree[rt_sum].siz=tree[rt_sum].sum=0;
    		return rt_sum;
    	}
    	void pushup(int rt)
    	{
    		tree[rt].siz=tree[lson(rt)].siz+tree[rson(rt)].siz;
    		tree[rt].sum=(tree[lson(rt)].sum<<1)^((tree[rson(rt)].sum<<1)|(tree[rson(rt)].siz&1));
    	}
    	void insert(int &rt,int bit,int val)
    	{
    		rt=(rt==0)?build_rt():rt;
    		if(bit==22)
    		{
    			tree[rt].siz++;
    			return;
    		}
    		insert(dson(rt,(val>>bit)&1),bit+1,val);
    		pushup(rt);
    	}
    	int merge(int rt1,int rt2,int bit)
    	{
    		if(rt1==0||rt2==0)  return rt1+rt2;
    		if(bit==22)
    		{
    			tree[rt1].siz+=tree[rt2].siz;
    			return rt1;
    		}
    		lson(rt1)=merge(lson(rt1),lson(rt2),bit+1);
    		rson(rt1)=merge(rson(rt1),rson(rt2),bit+1);
    		pushup(rt1);
    		return rt1;
    	}
    	void update(int rt)
    	{
    		if(rt==0)  return;
    		swap(lson(rt),rson(rt));
    		update(lson(rt));
    		pushup(rt);
    	}
    	int query(int rt)
    	{
    		return tree[rt].sum;
    	}
    }T;
    void dfs(int x)
    {
    	for(int i=0;i<e[x].size();i++)
    	{
    		dfs(e[x][i]);
    		T.root[x]=T.merge(T.root[x],T.root[e[x][i]],0);
    	}
    	T.update(T.root[x]);
    	T.insert(T.root[x],0,a[x]);
    	ans+=T.query(T.root[x]);
    }
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	int n,i;
    	cin>>n;
    	for(i=1;i<=n;i++)  cin>>a[i];
    	for(i=2;i<=n;i++)
    	{
    		cin>>fa[i];
    		add(fa[i],i);
    	}
    	dfs(1);
    	cout<<ans<<endl;
    	return 0;
    }
    

luogu P7294 [USACO21JAN] Minimum Cost Paths P

luogu P3549 [POI 2013] MUL-Multidrink

2.24

闲话

  • 早上跑完操后直接去升旗,主题是学雷锋月。在 7:00 结束的时候让直接去吃饭,吃完饭到机房后发现高二的吃饭时间调整到了 7:25
  • 上午 7:4512:15 去新机房打学校 OJ 的模拟赛。
  • 下午 huge 把昨天和今天的模拟赛成绩汇总后放到了比赛里,但没当众说。

做题纪要

SP6408 KKKCT2 - Counting Triangles 2

计蒜客 T3729 MEX

luogu P3251 [JLOI2012] 时间流逝

  • 树上高斯消元板子。

    • a 降序排序后相邻状态间连边得到树形关系。此时有 fx=p(ffax+1)+1p|Son(x)|ySon(x)(fy+1)=p×ffax+1p|Son(x)|ySon(x)fy+1
    • fx=axffax+bx ,上式等价于 fx=p×ffax+1p|Son(x)|ySon(x)(ayfx+by)+1 ,移项后得到 ax=p11p|Son(x)|ySon(x)ay,bx=1+1p|Son(x)|ySon(x)by11p|Son(x)|ySon(x)ay
    • faroot 是一个不存在的值,实际的 aroot=0,broot=1+1|Son(root)|ySon(root)by11|Son(root)|ySon(root)ay 。取 broot 作为答案。
    • 本题中合法状态不会很多,可以接受。
    点击查看代码
    int d[60],n,m,id=0;
    double p,a[2000010],b[2000010];
    void dfs(int x,int pos,int sum)
    {
    	a[x]=b[x]=0;
    	if(sum>m)  return;
    	int son=n-pos+1,y;
    	double suma=0,sumb=0,val;
    	for(int i=pos;i<=n;i++)
    	{
    		id++;  y=id;
    		dfs(y,i,sum+d[i]);
    		suma+=a[y];  sumb+=b[y];
    	}
    	if(x==1)  p=0;
    	val=(1-p)/son;
    	a[x]=p/(1-val*suma);
    	b[x]=(1+val*sumb)/(1-val*suma);
    }
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	while(cin>>p>>m>>n)
    	{
    		id=1;
    		for(int i=1;i<=n;i++)  cin>>d[i];
    		sort(d+1,d+1+n,greater<int>());
    		dfs(1,1,0);
    		printf("%.3lf\n",b[1]);
    	}
    	return 0;
    }
    

CF1823F Random Walk

  • fx=[x=s]+[faxt]×ffaxdufax+ySon(x),ytfyduy 解得 {ax=1[faxt]×ffax(1ySon(x),ytayduy)bx=ySon(x),ytbyduy1ySon(x),ytayduy

    点击查看代码
    const ll p=998244353;
    struct node
    {
    	ll nxt,to;
    }e[400010];
    ll head[200010],a[200010],b[200010],f[200010],du[200010],s,t,cnt=0;
    void add(ll u,ll v)
    {
    	cnt++;  e[cnt]=(node){head[u],v};  head[u]=cnt;
    }
    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 dfs1(ll x,ll fa)
    {
    	ll suma=0,sumb=(x==s);
    	for(ll i=head[x];i!=0;i=e[i].nxt)
    	{
    		if(e[i].to!=fa)
    		{
    			dfs1(e[i].to,x);
    			suma=(suma+a[e[i].to]*qpow(du[e[i].to],p-2,p)%p)%p;
    			sumb=(sumb+b[e[i].to]*qpow(du[e[i].to],p-2,p)%p)%p;
    		}
    	}
    	suma=qpow((1-suma+p)%p,p-2,p);
    	if(fa!=t)  a[x]=suma*qpow(du[fa],p-2,p)%p;
    	b[x]=sumb*suma%p;
    }
    void dfs2(ll x,ll fa)
    {
    	if(x!=t)  f[x]=(a[x]*f[fa]%p+b[x])%p;
    	for(ll i=head[x];i!=0;i=e[i].nxt)  if(e[i].to!=fa)  dfs2(e[i].to,x);
    }
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	ll n,u,v,i;
    	cin>>n>>s>>t;
    	for(i=1;i<=n-1;i++)
    	{
    		cin>>u>>v;
    		du[u]++;  du[v]++;
    		add(u,v);  add(v,u);
    	}
    	dfs1(t,0);
    	f[t]=1;  dfs2(t,0);
    	for(i=1;i<=n;i++)  cout<<f[i]<<" ";
    	return 0;
    }
    
    

HDU4035 Maze

  • px=1kxex ,有 fx=kxf1+px(1+ffax+ySon(x)fydux)=axffax+bxf1+cx

  • 解得 {ax=pxdux1pxduxySon(x)aybx=kx+pxduxySon(x)by1pxduxySon(x)aycx=px+pxduxySon(x)cy1pxduxySon(x)ay ,最终有 c11b1 即为所求。

    点击查看代码
    struct node
    {
    	int nxt,to;
    }e[20010];
    int head[10010],du[10010],cnt=0;
    double p1[10010],p2[10010],p[10010],a[10010],b[10010],c[10010];
    void add(int u,int v)
    {
    	cnt++;  e[cnt]=(node){head[u],v};  head[u]=cnt;
    }
    bool check(int x,int fa)
    {
    	if(p2[x]!=0)  return true;
    	if(p[x]==0)  return false;
    	for(int i=head[x];i!=0;i=e[i].nxt)
    	{
    		if(e[i].to!=fa&&check(e[i].to,x)==true)  return true;
    	}
    	return false;
    }
    void dfs(int x,int fa)
    {
    	double suma=0,sumb=0,sumc=0,val=p[x]/du[x];
    	for(int i=head[x];i!=0;i=e[i].nxt)
    	{
    		if(e[i].to!=fa)
    		{
    			dfs(e[i].to,x);
    			suma+=a[e[i].to];
    			sumb+=b[e[i].to];
    			sumc+=c[e[i].to];
    		}
    	}
    	a[x]=val/(1-val*suma);
    	b[x]=(p1[x]+val*sumb)/(1-val*suma);
    	c[x]=(p[x]+val*sumc)/(1-val*suma);
    }
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	int t,n,u,v,i,j;
    	scanf("%d",&t);
    	for(j=1;j<=t;j++)
    	{
    		cnt=0;
    		memset(e,0,sizeof(e));
    		memset(head,0,sizeof(head));
    		memset(du,0,sizeof(du));
    		scanf("%d",&n);
    		for(i=1;i<=n-1;i++)
    		{
    			scanf("%d%d",&u,&v);
    			du[u]++;  du[v]++;
    			add(u,v);  add(v,u);
    		}
    		for(i=1;i<=n;i++)
    		{
    			scanf("%lf%lf",&p1[i],&p2[i]);
    			p1[i]/=100;  p2[i]/=100;
    			p[i]=1-p1[i]-p2[i];
    		}
    		printf("Case %d: ",j);
    		if(check(1,0)==false)  printf("impossible\n");
    		else
    		{
    			dfs(1,0);
    			printf("%.8lf\n",c[1]/(1-b[1]));
    		}
    	}
    	return 0;
    }
    

HDU2484 Build the Tower

luogu P6832 [Cnoi2020] 子弦

  • 诈骗题,取单个字符即可。

    点击查看代码
    int a[30];
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	char c;
    	while(cin>>c)  a[c-'a']++;
    	cout<<*max_element(a+0,a+26)<<endl;
    	return 0;
    }
    

luogu P5193 [TJOI2012] 炸弹

  • 曼哈顿转切比雪夫,用 (x+y,xy) 代替 (x,y)

  • 限制条件转化为 max(|x1x2|,|y1y2|)r 。横坐标升序排序后双指针辅助 set 维护可行范围内纵坐标构成的集合,插入时与前驱后继进行合并。

    点击查看代码
    pair<int,int>a[100010];
    set<pair<int,int> >s;
    set<pair<int,int> >::iterator it;
    struct DSU
    {
    	int fa[100010];
    	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)
    	{
    		x=find(x);  y=find(y);
    		if(x!=y)  fa[x]=y;
    	}
    }D;
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	int n,r,x,y,ans=0,i,j;
    	cin>>n>>r;
    	for(i=1;i<=n;i++)
    	{
    		cin>>x>>y;
    		a[i]=make_pair(x+y,x-y);
    	}
    	sort(a+1,a+1+n);  D.init(n);
    	for(i=j=1;i<=n;i++)
    	{
    		for(;j<=i&&a[i].first-a[j].first>r;j++)  s.erase(make_pair(a[j].second,j));
    		if(s.empty()==0)
    		{
    			it=s.lower_bound(make_pair(a[i].second,i));
    			if(it!=s.end()&&abs(it->first-a[i].second)<=r)  D.merge(i,it->second);
    			if(it!=s.begin())
    			{
    				it--;
    				if(abs(it->first-a[i].second)<=r)  D.merge(i,it->second);
    			}
    		}
    		s.insert(make_pair(a[i].second,i));
    	}
    	for(i=1;i<=n;i++)  ans+=(D.fa[i]==i);
    	cout<<ans<<endl;
    	return 0;
    }
    

SP21360 SUFEQPRE - Suffix Equal Prefix

  • 反向跳 nxt

    点击查看代码
    int nxt[1000010];
    char s[1000010];
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	int t,n,ans=0,i,j,k;
    	cin>>t;
    	for(k=1;k<=t;k++)
    	{
    		cin>>(s+1);  n=strlen(s+1);  ans=0;
    		for(i=2,nxt[1]=j=0;i<=n;i++)
    		{
    			while(j>=1&&s[i]!=s[j+1])  j=nxt[j];
    			j+=(s[i]==s[j+1]);
    			nxt[i]=j;
    		}
    		for(i=nxt[n];i>=1;i=nxt[i])  ans++;
    		cout<<"Case "<<k<<": "<<ans<<endl;
    	}
    	return 0;
    }
    

2.25

闲话

做题纪要

UVA12338 Anti-Rhyme Pairs

  • 二分哈希求 LCP

    点击查看代码
    const ll mod=1000003579,base=13331;
    ll len[100010],jc[10010];
    vector<ll>hsh[100010];
    char s[10010];
    void sx_hash(char s[],ll len,vector<ll>&v)
    {
    	v.clear();
    	v.resize(len+1);
    	for(ll i=0;i<=len;i++)  v[i]=(i==0)?0:(v[i-1]*base%mod+s[i])%mod;
    }
    ll ask_hash(ll l,ll r,vector<ll>&v)
    {
    	return (v[r]-v[l-1]*jc[r-l+1]%mod)%mod;
    }
    ll lcp(ll x,ll y)
    {
    	ll l=0,r=min(len[x],len[y]),ans=0,mid;
    	while(l<=r)
    	{
    		mid=(l+r)/2;
    		if(ask_hash(1,mid,hsh[x])==ask_hash(1,mid,hsh[y]))
    		{
    			ans=mid;
    			l=mid+1;
    		}
    		else
    		{
    			r=mid-1;
    		}
    	}
    	return ans;
    }
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	ll t,n,m,x,y,i,j;
    	for(i=0;i<=10000;i++)  jc[i]=(i==0)?1:jc[i-1]*base%mod;
    	cin>>t;
    	for(j=1;j<=t;j++)
    	{
    		cin>>n;
    		for(i=1;i<=n;i++)
    		{
    			cin>>(s+1);  len[i]=strlen(s+1);
    			sx_hash(s,len[i],hsh[i]);
    		}
    		cin>>m;
    		cout<<"Case "<<j<<":"<<endl;
    		for(i=1;i<=m;i++)
    		{
    			cin>>x>>y;
    			cout<<lcp(x,y)<<endl;
    		}
    	}
    	return 0;
    }
    

luogu P10479 匹配统计

  • exKMP

    点击查看代码
    int z[200010],p[200010],ans[200010];
    char s[200010],t[200010];
    void Z(char s[],int n)
    {
    	z[1]=n;
    	for(int i=2,l=0,r=0;i<=n;i++)
    	{
    		for(z[i]=(i<=r)?min(z[i-l+1],r-i+1):0;i+z[i]<=n&&s[i+z[i]]==s[z[i]+1];z[i]++);
    		if(i+z[i]-1>r)
    		{
    			l=i;  r=i+z[i]-1;
    		}
    	}
    }
    void exkmp(char s[],int n,char t[],int m)
    {
    	Z(s,n);
    	for(int i=1,l=0,r=0;i<=m;i++)
    	{
    		for(p[i]=(i<=r)?min(z[i-l+1],r-i+1):0;i+p[i]<=m&&t[i+p[i]]==s[p[i]+1];p[i]++);
    		if(i+p[i]-1>r)
    		{
    			l=i;  r=i+p[i]-1;
    		}
    		ans[p[i]]++;
    	}
    }
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	int n,m,q,x,i;
    	cin>>m>>n>>q>>(t+1)>>(s+1);
    	exkmp(s,n,t,m);
    	for(i=1;i<=q;i++)
    	{
    		cin>>x;
    		cout<<ans[x]<<endl;
    	}
    	return 0;
    }
    

UVA11512 GATTACA

  • height 数组相关定义可知 maxi=1n{heighti} 即为所求,构造方案只需要取第一个取到最大值的位置的 sa 后若干个字符,出现次数取此时极长的一段即可。

    点击查看代码
    char s[1010];
    struct SA
    {
    	int sa[1010],rk[2010],oldrk[2010],id[1010],cnt[1010],key[1010],height[1010];
    	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(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++)
    		{
    			for(j-=(j>=1);s[i+j]==s[sa[rk[i]-1]+j];j++);
    			height[rk[i]]=j;
    		}
    	}
    }S;
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	int t,n,maxx,pos,cnt,i;
    	cin>>t;
    	for(;t>=1;t--)
    	{
    		cin>>(s+1);  n=strlen(s+1);  maxx=pos=cnt=0;
    		S.init(s,n);
    		for(i=1;i<=n;i++)
    		{
    			if(S.height[i]>maxx)
    			{
    				maxx=S.height[i];
    				pos=S.sa[i];
    			}
    		}
    		if(maxx==0)  cout<<"No repetitions found!"<<endl;
    		else
    		{
    			for(i=S.rk[pos];i<=n&&S.height[i]==maxx;i++)  cnt++;
    			for(i=pos;i<=pos+maxx-1;i++)  cout<<s[i];
    			cout<<" "<<cnt+1<<endl;
    		}
    	}
    	return 0;
    }
    
    

UVA12191 File Recover

  • saisai1 产生了 heighti 个出现至少两次的子串。对于 heighti 单调递增的极长连续段考虑增加的本质不同子串的贡献有 i=2nmax(heightiheighti1,0) 即为所求。

    点击查看代码
    char s[100010];
    struct SA
    {
    	int sa[100010],rk[200010],oldrk[200010],id[100010],cnt[100010],key[100010],height[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(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++)
    		{
    			for(j-=(j>=1);s[i+j]==s[sa[rk[i]-1]+j];j++);
    			height[rk[i]]=j;
    		}
    	}
    }S;
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	int n,ans,i;
    	while(cin>>(s+1))
    	{
    		n=strlen(s+1);  ans=0;
    		if(n==1&&s[1]=='*')  break;
    		S.init(s,n);
    		for(i=2;i<=n;i++)  ans+=max(S.height[i]-S.height[i-1],0);
    		cout<<ans<<endl;
    	}
    	return 0;
    }
    

luogu P7361 「JZOI-1」拜神

  • 考虑按照 height 从大到小合并,并查集维护 sai1sai 之间的连通关系。

  • 观测到答案具有二分性,不妨存储 LCPmid 时每个右端点最靠左的左端点,主席树维护。

  • 启发式合并时额外维护每个点的前驱和后继进行更新即可。

    点击查看代码
    int n;
    vector<int>c[50010];
    char s[50010];
    struct SA
    {
    	int sa[50010],rk[100010],oldrk[100010],id[50010],cnt[50010],key[50010],height[50010];
    	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(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++)
    		{
    			for(j-=(j>=1);s[i+j]==s[sa[rk[i]-1]+j];j++);
    			height[rk[i]]=j;
    		}
    	}
    }S;
    struct PDS_SMT
    {
    	int root[50010],rt_sum;
    	struct SegmentTree
    	{
    		int ls,rs,maxx;
    	}tree[50010<<8];
    	#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].maxx=0;
    		return rt_sum;
    	}
    	void update(int pre,int &rt,int l,int r,int pos,int val)
    	{
    		rt=build_rt();
    		tree[rt]=tree[pre];
    		tree[rt].maxx=max(tree[rt].maxx,val);
    		if(l==r)  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);
    	}
    	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].maxx;
    		int mid=(l+r)/2,ans=0;
    		if(x<=mid)  ans=max(ans,query(lson(rt),l,mid,x,y));
    		if(y>mid)  ans=max(ans,query(rson(rt),mid+1,r,x,y));
    		return ans;
    	}
    }T;
    struct DSU
    {
    	int fa[50010];
    	set<int>s[50010];
    	set<int>::iterator it,suf;
    	void init(int n)
    	{
    		for(int i=1;i<=n;i++)
    		{
    			fa[i]=i;
    			s[i].insert(i);
    		}
    	}
    	int find(int x)
    	{
    		return fa[x]==x?x:find(fa[x]);
    	}
    	void merge(int x,int y,int &rt)
    	{
    		x=find(x);  y=find(y);
    		if(x!=y)
    		{
    			if(s[x].size()<s[y].size())  swap(x,y);
    			fa[y]=x;
    			for(it=s[y].begin();it!=s[y].end();it++)
    			{
    				suf=s[x].lower_bound(*it);
    				if(suf!=s[x].end())  T.update(rt,rt,1,n,*suf,*it);
    				if(suf!=s[x].begin())
    				{
    					--suf;  T.update(rt,rt,1,n,*it,*suf);
    				}
    			}
    			for(it=s[y].begin();it!=s[y].end();it++)  s[x].insert(*it);
    			s[y].clear();
    		}
    	}
    }D;
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	int m,st,ed,l,r,ans,mid,i,j;
    	cin>>n>>m>>(s+1);  S.init(s,n);  D.init(n);
    	for(i=1;i<=n;i++)  c[S.height[i]].push_back(i);
    	for(i=n;i>=1;i--)
    	{
    		T.root[i]=T.root[i+1];
    		for(j=0;j<c[i].size();j++)  D.merge(S.sa[c[i][j]-1],S.sa[c[i][j]],T.root[i]);
    	}
    	for(i=1;i<=m;i++)
    	{
    		cin>>st>>ed;
    		l=1;  r=n;  ans=0;
    		while(l<=r)
    		{
    			mid=(l+r)/2;
    			if(T.query(T.root[mid],1,n,st,ed-mid+1)>=st)
    			{
    				ans=mid;
    				l=mid+1;
    			}
    			else
    			{
    				r=mid-1;
    			}
    		}
    		cout<<ans<<endl;
    	}
    	return 0;
    }
    

luogu P10315 [SHUPC 2024] 原神,启动!

  • 高斯消元。

  • 特殊处理下自由元的处理。

    点击查看代码
    ll a[110][110],ans[110],p;
    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 Gauss_Jordan(ll n)
    {
    	ll r=1;
    	for(ll i=1;i<=n;i++)
    	{
    		ll val=r;
    		for(ll j=r+1;j<=n;j++)  if(a[j][i]>a[val][i])  val=j;
    		for(ll j=1;j<=n+1;j++)  swap(a[r][j],a[val][j]);
    		if(a[r][i]==0)  continue;
    		ll inv=qpow(a[r][i],p-2,p);
    		for(ll j=1;j<=n;j++)
    		{
    			if(j==r)  continue;
    			for(ll k=i+1;k<=n+1;k++)  a[j][k]=(a[j][k]-a[r][k]*a[j][i]%p*inv%p+p)%p;
    			a[j][i]=0;
    		}
    		r++;
    	}
    	if(r<=n)
    	{
    		for(ll i=r;i<=n;i++)
    		{
    			if(a[i][n+1]==0)  continue;
    			for(ll j=1;j<=n;j++)
    			{
    				if(a[i][j]==0)
    				{
    					cout<<"niuza"<<endl;
    					return;
    				}
    			}
    		}
    	}
    	r=1;
    	for(ll i=1;i<=n;i++)
    	{
    		cout<<a[r][n+1]*qpow(a[r][i],p-2,p)%p<<" ";
    		a[r][n+1]=0;
    		r+=(a[r][i+1]==0);
    	}
    }
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	ll n,k,x,i,j;
    	cin>>n>>p;
    	for(i=1;i<=n;i++)
    	{
    		a[i][i]=1;
    		cin>>k;
    		for(j=1;j<=k;j++)
    		{
    			cin>>x;  a[x][i]=1;
    		} 
    	}
    	for(i=1;i<=n;i++)  cin>>a[i][n+1];
    	for(i=1;i<=n;i++)
    	{
    		cin>>x;
    		a[i][n+1]=(x-a[i][n+1]+p)%p;
    	}
    	Gauss_Jordan(n);
    	return 0;
    }
    

luogu P3603 雪辉

  • 树剖后对序列分块 bitset 维护,块间可以再开 ST 表减小空间复杂度,但效果不明显。

    点击查看代码
    struct node
    {
    	int nxt,to;
    }e[200010];
    int head[100010],fa[100010],siz[100010],dep[100010],son[100010],dfn[100010],top[100010],L[100010],R[100010],pos[100010],a[100010],_a[100010],lg[100010],cnt=0,tot=0,klen,ksum;
    bitset<30010>f[10][210],tmp;
    void add(int u,int v)
    {
    	cnt++;  e[cnt]=(node){head[u],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)
    {
    	tot++;  dfn[x]=tot;  a[tot]=_a[x];
    	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);
    	}
    }
    void init(int n)
    {
    	klen=500;  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++)  f[0][i][a[j]]=1;
    	}
    	lg[1]=0;
    	for(int i=2;i<=ksum;i++)  lg[i]=lg[i>>1]+1;
    	for(int j=1;j<=lg[ksum];j++)
    	{
    		for(int i=1;i+(1<<j)-1<=ksum;i++)  f[j][i]=f[j-1][i+(1<<(j-1))]|f[j-1][i];
    	}
    }
    void query(int l,int r)
    {
    	if(pos[l]==pos[r])
    	{
    		for(int i=l;i<=r;i++)  tmp[a[i]]=1;
    	}
    	else
    	{
    		for(int i=l;i<=R[pos[l]];i++)  tmp[a[i]]=1;
    		if(pos[r]-1>=pos[l]+1)
    		{
    			int t=lg[pos[r]-1-pos[l]];
    			tmp|=f[t][l]|f[t][r-(1<<t)+1];
    		}
    		for(int i=L[pos[r]];i<=r;i++)  tmp[a[i]]=1;
    	}
    }
    void ask(int u,int v)
    {
    	while(top[u]!=top[v])
    	{
    		if(dep[top[u]]>dep[top[v]])
    		{
    			query(dfn[top[u]],dfn[u]);
    			u=fa[top[u]];
    		}
    		else
    		{
    			query(dfn[top[v]],dfn[v]);
    			v=fa[top[v]];
    		}
    	}
    	if(dep[u]<dep[v])  query(dfn[u],dfn[v]);
    	else  query(dfn[v],dfn[u]);
    }
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	int n,m,c,k,u,v,ans1=0,ans2=0,ans=0,i;
    	cin>>n>>m>>c;
    	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);
    	}
    	dfs1(1,0);  dfs2(1,1);  init(n);
    	for(i=1;i<=m;i++)
    	{
    		cin>>k;  tmp.reset();
    		for(;k>=1;k--)
    		{
    			cin>>u>>v;  u^=ans*c;  v^=ans*c;
    			ask(u,v);
    		}
    		ans1=tmp.count();  ans2=(~tmp)._Find_first();  ans=ans1+ans2;
    		cout<<ans1<<" "<<ans2<<endl;
    	}
    	return 0;
    }
    

UOJ 763. 树哈希

  • 有根树哈希。

    • 一种好写且卡不掉的树哈希
    • hx=Hash({hy|ySon(x)})
    • 一种常见的容易实现且不容易被卡的方法是 Hash(S)=(c+xSf(x))modm ,其中 c 为常数,一般取 1m232264 进行自然溢出; f(x) 可以采用异或哈希。此时哈希是和哈希与异或哈希相结合的方式。
    • 可以使用梅森旋转器 random_device{}() 或毫秒级种子 std::chrono::steady_clock::now().time_since_epoch().count() 生成随机种子。
    点击查看代码
    const ull seed=mt19937_64(random_device{}())();
    struct node
    {
    	int nxt,to;
    }e[2000010];
    int head[1000010],cnt=0;
    ull hsh[1000010];
    set<ull>s;
    void add(int u,int v)
    {
    	cnt++;  e[cnt]=(node){head[u],v};  head[u]=cnt;
    }
    ull sx_hash(ull x)
    {
    	x^=seed;
    	x^=x<<13;  x^=x<<7;  x^=x>>17;// 这里也可以改成和其他几个常数进行异或
    	x^=seed;
    	return x;
    }
    void dfs(int x,int fa)
    {
    	hsh[x]=1;
    	for(int i=head[x];i!=0;i=e[i].nxt)
    	{
    		if(e[i].to!=fa)
    		{
    			dfs(e[i].to,x);
    			hsh[x]+=sx_hash(hsh[e[i].to]);
    		}
    	}
    	s.insert(hsh[x]);
    }
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	int n,u,v,i;
    	scanf("%d",&n);
    	for(i=1;i<=n-1;i++)
    	{
    		scanf("%d%d",&u,&v);
    		add(u,v);  add(v,u);
    	}
    	dfs(1,0);
    	printf("%d\n",s.size());
    	return 0;
    }
    
    
    

luogu P5043 【模板】树同构([BJOI2015]树的同构)

  • 多倍经验: SP7826 TREEISO - Tree Isomorphism

  • 无根树哈希。

    • 一种处理方法是将取两个重心中哈希值较小(大)的一个。
    点击查看代码
    const ull seed=mt19937_64(random_device{}())();
    struct node
    {
    	int nxt,to;
    }e[110];
    int head[60],siz[60],weight[60],n,cnt=0;
    pair<int,int>center;
    ull hsh[60];
    map<ull,int>s;
    void add(int u,int v)
    {
    	cnt++;  e[cnt]=(node){head[u],v};  head[u]=cnt;
    }
    void get_center(int x,int fa)
    {
    	siz[x]=1;
    	weight[x]=0;
    	for(int i=head[x];i!=0;i=e[i].nxt)
    	{
    		if(e[i].to!=fa)
    		{
    			get_center(e[i].to,x);
    			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.second=center.first;
    		center.first=x;
    	}
    }
    ull sx_hash(ull x)
    {
    	x^=seed;
    	x^=x<<13;  x^=x<<7;  x^=x>>17;
    	x^=seed;
    	return x;
    }
    void dfs(int x,int fa)
    {
    	hsh[x]=1;
    	for(int i=head[x];i!=0;i=e[i].nxt)
    	{
    		if(e[i].to!=fa)
    		{
    			dfs(e[i].to,x);
    			hsh[x]+=sx_hash(hsh[e[i].to]);
    		}
    	}
    }
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	int t,x,i,j;
    	ull tmp;
    	cin>>t;
    	for(j=1;j<=t;j++)
    	{
    		cnt=0;
    		memset(e,0,sizeof(e));
    		memset(head,0,sizeof(head));
    		cin>>n;
    		for(i=1;i<=n;i++)
    		{
    			cin>>x;
    			if(x!=0)
    			{
    				add(x,i);  add(i,x);
    			}
    		}
    		center=make_pair(0,0);
    		get_center(1,0);
    		dfs(center.first,0);  tmp=hsh[center.first];
    		if(center.second!=0)
    		{
    			dfs(center.second,0);  tmp=min(tmp,hsh[center.second]);
    		}
    		if(s.find(tmp)==s.end())  s[tmp]=j;
    		cout<<s[tmp]<<endl;
    	}
    	return 0;
    }
    

luogu P4323 [JSOI2016] 独特的树叶

  • 和哈希与异或哈希相结合的方式使得支持换根 DP 预处理出以每个节点为根时的 Hash 值。

  • 转移方程为 gx=fx+Hash(gfaHash(fx))

  • 寻找合法叶子时只断掉这条边取 gxHash(1) 即可,其中 x 是与叶子相连的点。

    点击查看代码
    const ull seed=mt19937_64(random_device{}())();
    map<ull,int>s;
    struct Tree_Hash
    {
    	struct node
    	{
    		int nxt,to;
    	}e[200010];
    	int head[100010],du[100010],cnt=0;
    	ull f[100010],g[100010];
    	void add(int u,int v)
    	{
    		cnt++;  e[cnt]=(node){head[u],v};  head[u]=cnt;
    	}
    	ull sx_hash(ull x)
    	{
    		x^=seed;
    		x^=x<<13;  x^=x<<7;  x^=x>>17;
    		x^=seed;
    		return x;
    	}
    	void dfs(int x,int fa)
    	{
    		f[x]=1;
    		for(int i=head[x];i!=0;i=e[i].nxt)
    		{
    			if(e[i].to!=fa)
    			{
    				dfs(e[i].to,x);
    				f[x]+=sx_hash(f[e[i].to]);
    			}
    		}
    	}
    	void reroot(int x,int fa)
    	{
    		for(int i=head[x];i!=0;i=e[i].nxt)
    		{
    			if(e[i].to!=fa)
    			{
    				g[e[i].to]=f[e[i].to]+sx_hash(g[x]-sx_hash(f[e[i].to]));
    				reroot(e[i].to,x);
    			}
    		}
    	}
    	void init(int n)
    	{
    		int u,v;
    		for(int i=1;i<=n-1;i++)
    		{
    			cin>>u>>v;  du[u]++;  du[v]++;
    			add(u,v);  add(v,u);
    		}
    		dfs(1,0);  g[1]=f[1];  reroot(1,0);
    	}
    }A,B;
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	int n,i;
    	cin>>n;
    	A.init(n);  B.init(n+1);
    	for(i=1;i<=n;i++)  s[A.g[i]]=1;
    	for(i=1;i<=n+1;i++)
    	{
    		if(B.du[i]==1&&s.find(B.g[B.e[B.head[i]].to]-B.sx_hash(1))!=s.end())
    		{
    			cout<<i<<endl;
    			break;
    		}
    	}
    	return 0;
    }
    

CF1800G Symmetree

  • 两棵子树对称当且仅当其同构。

  • 在有奇数个儿子时每次选择没有被对称的那颗子树(如果找不到返回无解)递归判断即可。

    点击查看代码
    const ull seed=mt19937_64(random_device{}())();
    int f[200010];
    vector<int>e[200010];
    ull hsh[200010];
    void add(int u,int v)
    {
    	e[u].push_back(v);
    }
    ull sx_hash(ull x)
    {
    	x^=seed;
    	x^=x<<13;  x^=x<<7;  x^=x>>17;
    	x^=seed;
    	return x;
    }
    void dfs(int x,int fa)
    {
    	f[x]=0;  hsh[x]=1;
    	ull sum=0,son=0;
    	for(int i=0;i<e[x].size();i++)
    	{
    		if(e[x][i]!=fa)
    		{
    			dfs(e[x][i],x);
    			son++;  sum^=hsh[e[x][i]];
    			hsh[x]+=sx_hash(hsh[e[x][i]]);
    		}
    	}
    	if(son%2==0)  f[x]=(sum==0);
    	else  for(int i=0;i<e[x].size();i++)  if(e[x][i]!=fa&&hsh[e[x][i]]==sum)  f[x]=f[e[x][i]];
    }
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	int t,n,u,v,i;
    	cin>>t;
    	for(;t>=1;t--)
    	{
    		cin>>n;
    		for(i=1;i<=n;i++)  e[i].clear();
    		for(i=1;i<=n-1;i++)
    		{
    			cin>>u>>v;
    			add(u,v);  add(v,u);
    		}
    		dfs(1,0);
    		cout<<((f[1]==1)?"YES":"NO")<<endl;
    	}
    	return 0;
    }
    

luogu P6084 [JSOI2015] isomorphism

  • 观察到度数为 2 的点一定位于当某个叶子节点作为根节点到某个节点的链上,暴力缩树即可。

    点击查看代码
    const ull seed=mt19937_64(random_device{}())();
    struct node
    {
    	int nxt,to;
    }e[20010];
    int head[10010],du[10010],siz[10010],weight[10010],tot,cnt=0;
    pair<int,int>center;
    vector<int>d[10010];
    ull hsh[10010];
    set<pair<int,ull> >s;
    set<pair<int,ull> >::iterator it;
    void add(int u,int v)
    {
    	cnt++;  e[cnt]=(node){head[u],v}; head[u]=cnt;
    }
    void init(int x,int fa,int last)
    {
    	if(du[x]==2)
    	{
    		for(int i=0;i<d[x].size();i++)  if(d[x][i]!=fa)  init(d[x][i],x,last);
    	}
    	else
    	{
    		tot++;
    		if(last!=0)
    		{
    			add(last,x);  add(x,last);
    		}
    		for(int i=0;i<d[x].size();i++)  if(d[x][i]!=fa)  init(d[x][i],x,x);
    	}
    }
    void get_center(int x,int fa)
    {
    	siz[x]=1;  weight[x]=0;
    	for(int i=head[x];i!=0;i=e[i].nxt)
    	{
    		if(e[i].to!=fa)
    		{
    			get_center(e[i].to,x);
    			siz[x]+=siz[e[i].to];
    			weight[x]=max(weight[x],siz[e[i].to]);
    		}
    	}
    	weight[x]=max(weight[x],tot-siz[x]);
    	if(weight[x]<=tot/2)
    	{
    		center.second=center.first;
    		center.first=x;
    	}
    }
    ull sx_hash(ull x)
    {
    	x^=seed;
    	x^=x<<13;  x^=x<<7;  x^=x>>17;
    	x^=seed;
    	return x;
    }
    void dfs(int x,int fa)
    {
    	hsh[x]=1;
    	for(int i=head[x];i!=0;i=e[i].nxt)
    	{
    		if(e[i].to!=fa)
    		{
    			dfs(e[i].to,x);
    			hsh[x]+=sx_hash(hsh[e[i].to]);
    		}
    	}
    }
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	int t,n,u,v,i;
    	ull tmp;
    	cin>>t;
    	for(;t>=1;t--)
    	{
    		cin>>n;
    		cnt=tot=0;  center=make_pair(0,0);
    		memset(e,0,sizeof(e));
    		memset(head,0,sizeof(head));
    		memset(du,0,sizeof(du));
    		for(i=1;i<=n;i++)  d[i].clear();
    		for(i=1;i<=n-1;i++)
    		{
    			cin>>u>>v;  du[u]++;  du[v]++;
    			d[u].push_back(v);  d[v].push_back(u);
    		}
    		for(i=1;i<=n;i++)
    		{
    			if(du[i]==1)
    			{
    				init(i,0,0); 
    				get_center(i,0);
    				break;
    			}
    		}
    		dfs(center.first,0);  tmp=hsh[center.first];
    		if(center.second!=0)
    		{
    			dfs(center.second,0);  tmp=min(tmp,hsh[center.second]);
    		}
    		s.insert(make_pair(tot,tmp));
    	}
    	cout<<s.size()<<endl;
    	for(it=s.begin();it!=s.end();it++)  cout<<it->first<<" ";
    	return 0;
    }
    
    

[ABC308G] Minimum Xor Pair Query

  • 线性基无法维护。

  • 从高到低考虑,一定是相邻的数的异或值最小。

    点击查看代码
    multiset<int>q,ans;
    multiset<int>::iterator nxt,pre;
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif	
    	int m,pd,x,i;
    	cin>>m;
    	for(i=1;i<=m;i++)
    	{
    		cin>>pd;
    		if(pd==1)
    		{
    			cin>>x;
    			nxt=q.lower_bound(x);
    			if(nxt!=q.end())  ans.insert(x^*nxt);
    			if(nxt!=q.begin())
    			{
    				pre=prev(nxt);
    				ans.insert(x^*pre);
    				if(nxt!=q.end())  ans.erase(ans.find(*pre^*nxt));
    			}
    			q.insert(x);
    		}
    		if(pd==2)
    		{
    			cin>>x;
    			q.erase(q.find(x));
    			nxt=q.lower_bound(x);
    			if(nxt!=q.end())  ans.erase(ans.find(x^*nxt));
    			if(nxt!=q.begin())
    			{
    				pre=prev(nxt);
    				ans.erase(ans.find(x^*pre));
    				if(nxt!=q.end())  ans.insert(*pre^*nxt);
    			}
    		}
    		if(pd==3)  cout<<*ans.begin()<<endl;
    	}
    	return 0;
    }
    

luogu P6077 [BalticOI 2007] Escape

  • 转化为消灭最少的士兵使得上下边界不连通,需要先按照 y 升序排序。

  • 若两个士兵间距离 200 则连一条容量为 的边。

    点击查看代码
    const int inf=0x3f3f3f3f;
    pair<int,int>a[260];
    struct MinCut
    {
    	struct node
    	{
    		int nxt,to,cap,flow;
    	}e[140010];
    	int head[260],vis[260],dis[260],cur[260],cnt=1;
    	void add(int u,int v,int w)
    	{
    		cnt++;  e[cnt]=(node){head[u],v,w,0};  head[u]=cnt;
    		cnt++;  e[cnt]=(node){head[v],u,0,0};  head[v]=cnt;
    	}
    	bool bfs(int s,int t)
    	{
    		memset(vis,0,sizeof(vis));
    		queue<int>q;
    		dis[s]=1;  cur[s]=head[s];
    		q.push(s);  vis[s]=1;
    		while(q.empty()==0)
    		{
    			int x=q.front();  q.pop();
    			for(int i=head[x];i!=0;i=e[i].nxt)
    			{
    				if(vis[e[i].to]==0&&e[i].cap>e[i].flow)
    				{
    					dis[e[i].to]=dis[x]+1;  cur[e[i].to]=head[e[i].to];
    					q.push(e[i].to);  vis[e[i].to]=1;
    					if(e[i].to==t)  return true;
    				}
    			}
    		}
    		return false;
    	}
    	int dfs(int x,int t,int flow)
    	{
    		if(x==t)  return flow;
    		int sum=0,tmp;
    		for(int i=cur[x];i!=0&&sum<flow;i=e[i].nxt)
    		{
    			cur[x]=i;
    			if(dis[e[i].to]==dis[x]+1&&e[i].cap>e[i].flow)
    			{
    				tmp=dfs(e[i].to,t,min(e[i].cap-e[i].flow,flow-sum));
    				if(tmp==0)  dis[e[i].to]=0;
    				sum+=tmp;
    				e[i].flow+=tmp;  e[i^1].flow-=tmp;
    			}
    		}
    		return sum;
    	}
    	int Dinic(int s,int t)
    	{
    		int flow=0;
    		while(bfs(s,t)==true)  flow+=dfs(s,t,inf);
    		return flow;
    	}
    }C;
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	int h,w,n,s,t,i,j;
    	cin>>h>>w>>n;  s=n+1;  t=n+2;
    	for(i=1;i<=n;i++)  cin>>a[i].second>>a[i].first;
    	sort(a+1,a+1+n);
    	for(i=1;i<=n;i++)
    	{
    		if(a[i].first<=100)  C.add(s,i,1);
    		if(a[i].first>=w-100)  C.add(i,t,1);
    		for(j=i+1;j<=n;j++)
    			if(1ll*(a[i].second-a[j].second)*(a[i].second-a[j].second)+1ll*(a[i].first-a[j].first)*(a[i].first-a[j].first)<=40000)  C.add(i,j,inf);
    	}
    	cout<<C.Dinic(s,t)<<endl;
    	return 0;
    }	
    

luogu P3688 [ZJOI2017] 树状数组

  • 原维护前缀和的树状数组写成了维护后缀和,故答案仍然正确等价于 i=lrai=i=l1r1aial=ar1

  • 设当前 al=ar 概率为 p ,当前不对 l,r 相等关系进行修改的概率为 q ,此时更新为 ppq+(1p)(1q)

    • l[ql,qr],r[ql,qr] ,此时有 q=11qrql+1
    • l[ql,qr],r[ql,qr] ,此时有 q=11qrql+1
    • l[ql,qr],r[ql,qr] ,此时有 q=12qrql+1
  • 将左端点看做横坐标,右端点看做纵坐标后是一个矩阵修改单点查询的形式,考虑二维线段树,需要标记永久化。具体实现时观察到 f(f(p,q1),q2)=f(p,f(q1,q2))=f(p,f(q2,q1)) ,其中 f(p,q)=pq+(1p)(1q) 可以直接对着 q 打标记。

  • 特判 l=1 时限制条件为 i=1rai=i=rnai 。可以新建一个 0 号节点存储对于维护的右端点前缀和等于后缀和的概率,三种情况的 q 分别为 0,0,1qrql+1

    点击查看代码
    const ll p=998244353;
    int n;
    int qpow(int a,int b,int p)
    {
    	int ans=1;
    	while(b)
    	{
    		if(b&1)  ans=1ll*ans*a%p;
    		b>>=1;
    		a=1ll*a*a%p;
    	}
    	return ans;
    }
    int f(int a,int b)
    {
    	return ((1ll*a*b%p+1ll*(1-a)*(1-b)%p)%p+p)%p;
    }
    struct SMT_2D
    {
    	int rt_sum=0;
    	struct SegmentTree
    	{
    		int ls,rs,lazy;
    	}tree[100010*450];
    	#define lson(rt) (tree[rt].ls)
    	#define rson(rt) (tree[rt].rs)
    	int build_rt()
    	{
    		rt_sum++;
    		lson(rt_sum)=rson(rt_sum)=0;
    		tree[rt_sum].lazy=1;
    		return rt_sum;
    	}
    	void update(int &rt,int l,int r,int x,int y,int val)
    	{
    		rt=(rt==0)?build_rt():rt;
    		if(x<=l&&r<=y)
    		{
    			tree[rt].lazy=f(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 1;
    		if(l==r)  return tree[rt].lazy;
    		int mid=(l+r)/2;
    		if(pos<=mid)  return f(tree[rt].lazy,query(lson(rt),l,mid,pos));
    		else  return f(tree[rt].lazy,query(rson(rt),mid+1,r,pos));
    	}
    	#undef lson
    	#undef rson
    }S;
    struct SMT_1D
    {
    	struct SMT
    	{
    		int root;
    	}tree[400010];
    	#define lson(rt) (rt<<1)
    	#define rson(rt) (rt<<1|1)
    	void update(int rt,int l,int r,int x,int y,int _x,int _y,int val)
    	{
    		if(x<=l&&r<=y)
    		{
    			S.update(tree[rt].root,1,n,_x,_y,val);
    			return;
    		}
    		int mid=(l+r)/2;
    		if(x<=mid)  update(lson(rt),l,mid,x,y,_x,_y,val);
    		if(y>mid)  update(rson(rt),mid+1,r,x,y,_x,_y,val);
    	}
    	int query(int rt,int l,int r,int pos,int _pos)
    	{
    		if(l==r)  return S.query(tree[rt].root,1,n,_pos);
    		int mid=(l+r)/2;
    		if(pos<=mid)  return f(query(lson(rt),l,mid,pos,_pos),S.query(tree[rt].root,1,n,_pos));
    		else  return f(query(rson(rt),mid+1,r,pos,_pos),S.query(tree[rt].root,1,n,_pos));
    	}
    	#undef lson
    	#undef rson
    }T;
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	int m,pd,l,r,inv,i;
    	cin>>n>>m;
    	for(i=1;i<=m;i++)
    	{
    		cin>>pd>>l>>r;
    		if(pd==1)
    		{
    			inv=qpow(r-l+1,p-2,p);
    			if(l-1>=1)  
    			{
    				T.update(1,0,n,1,l-1,l,r,(1-inv+p)%p);  T.update(1,0,n,0,0,1,l-1,0);
    			}
    			if(r+1<=n)
    			{
    				T.update(1,0,n,l,r,r+1,n,(1-inv+p)%p);  T.update(1,0,n,0,0,r+1,n,0);
    			}
    			T.update(1,0,n,l,r,l,r,(1-2*inv%p+p)%p);    T.update(1,0,n,0,0,l,r,inv);
    		}
    		else  cout<<T.query(1,0,n,l-1,r)<<endl;
    	}
    	return 0;
    }
    

luogu P11804 [PA 2017] 换钱

  • 无脑往多了换一定不劣。

    点击查看代码
    int cnt[3000010];
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	int n,x,ans=0,i;
    	cin>>n;
    	for(i=1;i<=n;i++)
    	{
    		cin>>x;  cnt[x]++;
    	}
    	for(i=0;i<=300000;i++)
    	{
    		if(cnt[i]!=0)  ans=i;
    		cnt[i+1]+=cnt[i]/2;
    	}
    	cout<<ans<<endl;
    	return 0;
    }
    

[ARC191C] A^n - 1

  • 对着 n=δm(a),n|φ(m) 冲没冲出来。

  • 打表后得到 a=n+1,m=n2 为一组可行解。证明考虑 (n+1)n=i=0n(ni)ni=1+n2(1+i=2n(ni)ni2)1(modn2)

    点击查看代码
    ll t,n,i;
    cin>>t;
    for(i=1;i<=t;i++)
    {
    	cin>>n;
    	cout<<n+1<<" "<<n*n<<endl;
    }
    

2.26

闲话

  • 上午 7:4512:15 去新机房打 accoders NOI 的模拟赛。
  • 下午讲题前 huge 把考生答题信息确认单的几个错误填写典型放了出来,让我们注意一下。
  • 晚上 huge 说这两天最重要的是保证心态,考前最容易的就是浮躁,我们要想办法让自己稳定下来。现在再去学新知识点应该是来不及了,而且也没时间再去练习,所以建议我们看看之前自己写的博客和题解,梳理下思路。可能之前自己原创的一个写法就演变成了后面的某个算法或 Trick ,集训队论文大部分就是作者总结自己以前学过的东西再经过适当扩展得到的。适时整理之前自己学过的知识也挺有意思的,比如这次正好赶上给高一零基础的讲图的连通性,在涉及到求强连通分量时对于儿子节点被访问过,且已经在栈中的更新网上有两种写法,分别是用 dfn/low 去更新(前者不会影响划分强连通分量的结果,但当需要使用 low 时后者才符合定义),记得 CSPS2023 的时候有人(现在只能想起来那人是 hzoi2022 四个女生之一)专门问过 miaomiao 这个问题,但 miaomiao 当时没能回答上来,这次他们几个教练还专门讨论了下这个问题。又强调了下注意算法实现的细节。之前自己写的游记就别大范围看了,主要看看自己当时成功在哪里,又有哪些不足,这次打算怎么调整。又说现在有些同学还是习惯虚拟机或纯 Linux 环境,但省选考场所在的机房不变,设备质量就那样,必要情况下要考虑在 Windows 环境下编写代码,今明两天我们还有时间去熟悉 Windows 环境下编写代码、如何对拍之类。
  • 周五上午 10:00 出发,周六下午预定还是租会议室让我们去自习,建议我们带上两本参考书(蓝书、紫书、具体数学等),把整本书大体看一眼。这个时候再去比带电脑的比不带电脑的能多写几个题也没什么意义了。
  • 最后 huge 问了下我们明天模拟赛是选择打学校 OJ 的,还是打 luogu 的 2025 年湖北省省队选拔集训暨能力测试(同步赛) ,我们说要打 luogu 的。

做题纪要

luogu P4334 [COI 2007] Policija

  • 建出广义圆方树后第二问就是在问 ab 是否经过 c 。合法当且仅当 cLCA(a,b) 的子树内且 abc 的子树内。

  • 第一问转化为问 ab 是否经过 (c,d) 所在的点双的方点 e(c,d) 是否是割边。

    • (c,d) 所在的方点是 c,d 中深度较深的点的父亲。
    • (c,d) 是割边等价于 e 的度数为 2
    点击查看代码
    struct node
    {
    	int nxt,to;
    }e[1000010];
    int head[100010],dfn[200010],low[100010],fa[200010],siz[200010],son[200010],top[200010],dep[200010],out[200010],du[200010],cnt=0,v_dcc=0,tot=0;
    vector<int>g[200010];
    stack<int>s;
    void add(int u,int v)
    {
    	cnt++;  e[cnt]=(node){head[u],v}; head[u]=cnt;
    }
    void tarjan(int x)
    {
    	tot++;  dfn[x]=low[x]=tot;
    	s.push(x);
    	for(int i=head[x];i!=0;i=e[i].nxt)
    	{
    		if(dfn[e[i].to]==0)
    		{
    			tarjan(e[i].to);
    			low[x]=min(low[x],low[e[i].to]);
    			if(low[e[i].to]==dfn[x])
    			{
    				v_dcc++;
    				g[v_dcc].push_back(x);  g[x].push_back(v_dcc);
    				du[x]++;  du[v_dcc]++;
    				int tmp=0;
    				while(e[i].to!=tmp)
    				{
    					tmp=s.top();  s.pop();
    					g[v_dcc].push_back(tmp);  g[tmp].push_back(v_dcc);
    					du[tmp]++;  du[v_dcc]++;
    				}
    			}
    		}
    		else  low[x]=min(low[x],dfn[e[i].to]);
    	}
    }
    void dfs1(int x,int father)
    {
    	siz[x]=1;
    	fa[x]=father;
    	dep[x]=dep[father]+1;
    	for(int i=0;i<g[x].size();i++)
    	{
    		if(g[x][i]!=father)
    		{
    			dfs1(g[x][i],x);
    			siz[x]+=siz[g[x][i]];
    			son[x]=(siz[g[x][i]]>siz[son[x]])?g[x][i]:son[x];
    		}
    	}
    }
    void dfs2(int x,int id)
    {
    	tot++;  dfn[x]=tot;
    	top[x]=id;
    	if(son[x]!=0)
    	{
    		dfs2(son[x],id);
    		for(int i=0;i<g[x].size();i++)  if(g[x][i]!=fa[x]&&g[x][i]!=son[x])  dfs2(g[x][i],g[x][i]);
    	}
    	out[x]=tot;
    }
    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;
    }
    bool check(int x,int y)
    {
    	return dfn[x]<=dfn[y]&&dfn[y]<=out[x];
    }
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	int n,m,q,pd,u,v,x,y,rt,tmp,i;
    	cin>>n>>m;
    	for(i=1;i<=m;i++)
    	{
    		cin>>u>>v;
    		add(u,v);  add(v,u);
    	}
    	v_dcc=n;
    	for(i=1;i<=n;i++)  if(dfn[i]==0)  tarjan(i);
    	tot=0;  dfs1(1,0);  dfs2(1,1);
    	cin>>q;
    	for(i=1;i<=q;i++)
    	{
    		cin>>pd>>u>>v>>x;
    		if(pd==1)
    		{
    			cin>>y;  if(dep[x]>dep[y])  swap(x,y);
    			tmp=fa[y];  rt=lca(u,v);
    			cout<<(du[tmp]==2&&(check(rt,tmp)&&(check(tmp,u)||check(tmp,v)))?"no":"yes")<<endl;
    		}
    		else
    		{
    			rt=lca(u,v);
    			cout<<((check(rt,x)&&(check(x,u)||check(x,v)))?"no":"yes")<<endl;
    		}
    	}
    	return 0;
    }
    

QOJ 21566. 四维偏序

  • 四维偏序。

    • 考虑 CDQ 分治划分区间的本质是在给每个元素附上一个权值 1/2 ,只能从 12 进行转移。
    • 同理,四维偏序额外套一层 CDQ 分治即可。
    点击查看代码
    struct node
    {
    	int a,b,c,d,col;
    }q[100010],_q[100010],tmp[100010];
    int n;
    ll ans=0;
    bool cmp(node a,node b)
    {
    	return a.a<b.a;
    }
    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;
    	}
    }T;
    void cdq_2d(int l,int r)
    {
    	if(l==r)  return;	
    	int mid=(l+r)/2,x,y,pos=l;
    	cdq_2d(l,mid);  cdq_2d(mid+1,r);
    	for(x=l,y=mid+1;y<=r;y++)
    	{
    		for(;_q[x].c<_q[y].c&&x<=mid;x++)
    		{
    			if(_q[x].col==1)  T.add(n,_q[x].d,1);
    			tmp[pos]=_q[x];  pos++;
    		}
    		if(_q[y].col==2)  ans+=T.getsum(_q[y].d-1);
    		tmp[pos]=_q[y];  pos++;
    	}
    	for(int i=l;i<=x-1;i++)  if(_q[i].col==1)  T.add(n,_q[i].d,-1);
    	for(int i=x;i<=mid;i++)
    	{
    		tmp[pos]=_q[i];  pos++;
    	}
    	for(int i=l;i<=r;i++)  _q[i]=tmp[i];
    }
    void cdq_1d(int l,int r)
    {
    	if(l==r)  return;
    	int mid=(l+r)/2,x,y,pos=l;
    	cdq_1d(l,mid);  cdq_1d(mid+1,r);	
    	for(x=l,y=mid+1;y<=r;y++)
    	{
    		for(;q[x].b<q[y].b&&x<=mid;x++)
    		{
    			q[x].col=1;
    			_q[pos]=q[x];  pos++;
    		}
    		q[y].col=2;  
    		_q[pos]=q[y];  pos++;
    	}
    	for(int i=x;i<=mid;i++)
    	{
    		q[i].col=1;
    		_q[pos]=q[i];  pos++;
    	}
    	for(int i=l;i<=r;i++)  q[i]=_q[i];
    	cdq_2d(l,r);
    }
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	cin>>n;
    	for(int i=1;i<=n;i++)  cin>>q[i].a>>q[i].b>>q[i].c>>q[i].d;
    	sort(q+1,q+1+n,cmp);  cdq_1d(1,n);
    	cout<<ans<<endl;
    	return 0;
    }
    

luogu P3769 [CH弱省胡策R2] TATT

  • 多倍经验: luogu P5621 [DBOI2019] 德丽莎世界第一可爱

  • CDQ 分治优化四维偏序式的 DP 。

  • 为了简便,归并排序用 sort 代替。

    点击查看代码
    struct node
    {
    	int a,b,c,d,col,id;
    }q[50010];
    int f[50010],b[50010];
    bool cmpa(node a,node b)
    {
    	if(a.a!=b.a)  return a.a<b.a;
    	if(a.b!=b.b)  return a.b<b.b;
    	if(a.c!=b.c)  return a.c<b.c;
    	return a.d<b.d;
    }
    bool cmpb(node a,node b)
    {
    	if(a.b!=b.b)  return a.b<b.b;
    	if(a.c!=b.c)  return a.c<b.c;
    	return a.d<b.d;
    }
    bool cmpc(node a,node b)
    {
    	if(a.c!=b.c)  return a.c<b.c;
    	return a.d<b.d;
    }
    bool cmp(node a,node b)
    {
    	return a.id<b.id;
    }
    struct BIT
    {
    	int c[50010];
    	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_2d(int l,int r)
    {
    	if(l==r)  return;
    	int mid=(l+r)/2,x,y;
    	cdq_2d(l,mid);
    	sort(q+l,q+mid+1,cmpc);  sort(q+mid+1,q+r+1,cmpc);
    	for(x=l,y=mid+1;y<=r;y++)
    	{
    		for(;q[x].c<=q[y].c&&x<=mid;x++)  if(q[x].col==1)  T.add(b[0],q[x].d,f[q[x].id]);
    		if(q[y].col==2)  f[q[y].id]=max(f[q[y].id],T.getsum(q[y].d)+1);
    	}
    	for(int i=l;i<=x-1;i++)  if(q[i].col==1)  T.del(b[0],q[i].d);
    	sort(q+l,q+r+1,cmpb);  cdq_2d(mid+1,r);
    }
    void cdq_1d(int l,int r)
    {
    	if(l==r)  return;
    	int mid=(l+r)/2;
    	cdq_1d(l,mid);
    	sort(q+l,q+mid+1,cmpb);  sort(q+mid+1,q+r+1,cmpb);
    	for(int i=l;i<=mid;i++)  q[i].col=1;
    	for(int i=mid+1;i<=r;i++)  q[i].col=2;
    	sort(q+l,q+r+1,cmpb);  cdq_2d(l,r);
    	sort(q+l,q+r+1,cmp);  cdq_1d(mid+1,r);
    }
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	int n,i;
    	cin>>n;
    	for(i=1;i<=n;i++)
    	{
    		cin>>q[i].a>>q[i].b>>q[i].c>>q[i].d;
    		b[i]=q[i].d;
    	}
    	sort(b+1,b+1+n);  b[0]=unique(b+1,b+1+n)-(b+1);
    	sort(q+1,q+1+n,cmpa);
    	for(i=1;i<=n;i++)
    	{
    		q[i].d=lower_bound(b+1,b+1+b[0],q[i].d)-b;
    		q[i].id=i;
    		f[i]=1;
    	}
    	cdq_1d(1,n);
    	cout<<*max_element(f+1,f+1+n)<<endl;
    	return 0;
    }
    

luogu P4849 寻找宝藏

  • 树状数组额外保存方案数即可。

    点击查看代码
    const ll p=998244353;
    struct node
    {
    	ll a,b,c,d,val,col,id;
    }q[80010];
    ll f[80010],g[80010],b[80010];
    bool cmpa(node a,node b)
    {
    	if(a.a!=b.a)  return a.a<b.a;
    	if(a.b!=b.b)  return a.b<b.b;
    	if(a.c!=b.c)  return a.c<b.c;
    	return a.d<b.d;
    }
    bool cmpb(node a,node b)
    {
    	if(a.b!=b.b)  return a.b<b.b;
    	if(a.c!=b.c)  return a.c<b.c;
    	return a.d<b.d;
    }
    bool cmpc(node a,node b)
    {
    	if(a.c!=b.c)  return a.c<b.c;
    	return a.d<b.d;
    }
    bool cmp(node a,node b)
    {
    	return a.id<b.id;
    }
    struct BIT
    {
    	ll c[2][80010];
    	ll lowbit(ll x)
    	{
    		return (x&(-x));
    	}
    	void add(ll n,ll x,ll val,ll _val)
    	{
    		for(ll i=x;i<=n;i+=lowbit(i))  
    		{
    			if(c[0][i]<val)
    			{
    				c[0][i]=val;
    				c[1][i]=_val;
    			}
    			else  if(c[0][i]==val)  c[1][i]=(c[1][i]+_val)%p;
    		}
    	}
    	void del(ll n,ll x)
    	{
    		for(ll i=x;i<=n;i+=lowbit(i))  c[0][i]=c[1][i]=0;
    	}
    	pair<ll,ll> getsum(ll x)
    	{
    		ll ans=0,sum=0;
    		for(ll i=x;i>=1;i-=lowbit(i)) 
    		{
    			if(c[0][i]>ans)
    			{
    				ans=c[0][i];
    				sum=c[1][i];
    			}
    			else  if(c[0][i]==ans)  sum=(sum+c[1][i])%p;
    		}
    		return make_pair(ans,sum);
    	}
    }T;
    void cdq_2d(ll l,ll r)
    {
    	if(l==r)  return;
    	ll mid=(l+r)/2,x,y;
    	cdq_2d(l,mid);
    	sort(q+l,q+mid+1,cmpc);  sort(q+mid+1,q+r+1,cmpc);
    	for(x=l,y=mid+1;y<=r;y++)
    	{
    		for(;q[x].c<=q[y].c&&x<=mid;x++)  if(q[x].col==1)  T.add(b[0],q[x].d,f[q[x].id],g[q[x].id]);
    		if(q[y].col==2)  
    		{
    			pair<ll,ll>tmp=T.getsum(q[y].d);
    			tmp.first+=q[y].val;
    			if(f[q[y].id]<tmp.first)
    			{
    				f[q[y].id]=tmp.first;
    				g[q[y].id]=tmp.second;
    			}
    			else  if(f[q[y].id]==tmp.first)  g[q[y].id]=(g[q[y].id]+tmp.second)%p;
    		}
    	}
    	for(ll i=l;i<=x-1;i++)  if(q[i].col==1)  T.del(b[0],q[i].d);
    	sort(q+l,q+r+1,cmpb);  cdq_2d(mid+1,r);
    }
    void cdq_1d(ll l,ll r)
    {
    	if(l==r)  return;
    	ll mid=(l+r)/2;
    	cdq_1d(l,mid);
    	sort(q+l,q+mid+1,cmpb);  sort(q+mid+1,q+r+1,cmpb);
    	for(ll i=l;i<=mid;i++)  q[i].col=1;
    	for(ll i=mid+1;i<=r;i++)  q[i].col=2;
    	sort(q+l,q+r+1,cmpb);  cdq_2d(l,r);
    	sort(q+l,q+r+1,cmp);  cdq_1d(mid+1,r);
    }
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	ll n,m,ans=0,sum=0,i;
    	cin>>n>>m;
    	for(i=1;i<=n;i++)
    	{
    		cin>>q[i].a>>q[i].b>>q[i].c>>q[i].d>>q[i].val;
    		b[i]=q[i].d;
    	}
    	sort(b+1,b+1+n);  b[0]=unique(b+1,b+1+n)-(b+1);
    	sort(q+1,q+1+n,cmpa);
    	for(i=1;i<=n;i++)
    	{
    		q[i].d=lower_bound(b+1,b+1+b[0],q[i].d)-b;
    		q[i].id=i;
    		f[i]=q[i].val;  g[i]=1;
    	}
    	cdq_1d(1,n);
    	for(i=1;i<=n;i++)
    	{
    		if(f[i]>ans)
    		{
    			ans=f[i];
    			sum=g[i];
    		}
    		else  if(f[i]==ans)  sum=(sum+g[i])%p;
    	}
    	cout<<ans<<endl<<sum<<endl;
    	return 0;
    }
    

P1051. 单峰序列

P596. 【模板】平面最近点对

luogu P3939 数颜色

  • 主席树。

    点击查看代码
    int a[300010];
    struct PDS_SMT
    {
    	int root[300010],rt_sum=0;
    	struct SegmentTree
    	{
    		int ls,rs,sum;
    	}tree[300010<<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 update(int pre,int &rt,int l,int r,int pos,int val)
    	{
    		rt=build_rt();  tree[rt]=tree[pre];
    		tree[rt].sum+=val;
    		if(l==r)  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);
    	}
    	int query(int rt1,int rt2,int l,int r,int pos)
    	{
    		if(l==r)  return tree[rt2].sum-tree[rt1].sum;
    		int mid=(l+r)/2;
    		if(pos<=mid)  return query(lson(rt1),lson(rt2),l,mid,pos);
    		else  return query(rson(rt1),rson(rt2),mid+1,r,pos);
    	}
    }T;
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	int n,m,pd,l,r,x,i;
    	scanf("%d%d",&n,&m);
    	for(i=1;i<=n;i++)
    	{
    		scanf("%d",&a[i]);   T.update(T.root[i-1],T.root[i],1,300000,a[i],1);
    	}
    	for(i=1;i<=m;i++)
    	{
    		scanf("%d%d",&pd,&l);
    		if(pd==1)
    		{
    			scanf("%d%d",&r,&x);
    			printf("%d\n",T.query(T.root[l-1],T.root[r],1,300000,x));
    		}
    		else
    		{
    			T.update(T.root[l],T.root[l],1,300000,a[l],-1);
    			swap(a[l],a[l+1]);
    			T.update(T.root[l],T.root[l],1,300000,a[l],1);
    		}
    	}
    	return 0;
    }
    

2.27

闲话

做题纪要

luogu P1975 [国家集训队] 排队

  • 多倍经验: CF785E Anton and Permutation

  • 树状数组套主席树。

    点击查看代码
    int a[20010],b[20010];
    struct PDS_SMT
    {
    	int root[20010],rt_sum;
    	struct SegmentTree
    	{
    		int ls,rs,sum;
    	}tree[20010<<7];
    	#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 update(int &rt,int l,int r,int pos,int val)
    	{
    		rt=(rt==0)?build_rt():rt;
    		tree[rt].sum+=val;
    		if(l==r)  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);
    	}
    	int query(int rt,int l,int r,int x,int y)
    	{
    		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;
    struct BIT
    {
    	int lowbit(int x)
    	{
    		return (x&(-x));
    	}
    	void update(int n,int x,int pos,int val)
    	{
    		for(int i=x;i<=n;i+=lowbit(i))  T.update(T.root[i],1,b[0],pos,val);
    	}
    	int getsum(int x,int _x,int _y)
    	{
    		int ans=0;
    		for(int i=x;i>=1;i-=lowbit(i))  ans+=T.query(T.root[i],1,b[0],_x,_y);
    		return ans;
    	}
    	int query(int l,int r,int x,int y)
    	{
    		return (x>y||l>r)?0:getsum(r,x,y)-getsum(l-1,x,y);
    	}
    }B;
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	int n,m,l,r,ans=0,i;
    	cin>>n;
    	for(i=1;i<=n;i++)
    	{
    		cin>>a[i];  b[i]=a[i];
    	}
    	sort(b+1,b+1+n);  b[0]=unique(b+1,b+1+n)-(b+1);
    	for(i=n;i>=1;i--)
    	{
    		a[i]=lower_bound(b+1,b+1+b[0],a[i])-b;
    		ans+=B.query(i+1,n,1,a[i]-1);
    		B.update(n,i,a[i],1);
    	}
    	cout<<ans<<endl;
    	cin>>m;
    	for(i=1;i<=m;i++)
    	{
    		cin>>l>>r;
    		if(l>r)  swap(l,r);
    		ans-=B.query(l+1,r-1,1,a[l]-1);  ans+=B.query(l+1,r-1,a[l]+1,b[0]);
    		ans-=B.query(l+1,r-1,a[r]+1,b[0]);  ans+=B.query(l+1,r-1,1,a[r]-1);
    		if(a[l]<a[r])  ans++;
    		if(a[l]>a[r])  ans--;
    		B.update(n,l,a[l],-1);  B.update(n,r,a[r],-1);
    		swap(a[l],a[r]);
    		B.update(n,l,a[l],1);  B.update(n,r,a[r],1);
    		cout<<ans<<endl;
    	}
    	return 0;
    }
    

luogu P6572 [BalticOI 2017] Railway

  • 建出虚树后树上差分,注意不要算上新加入的 1 的贡献。

    点击查看代码
    struct node
    {
    	int nxt,to,id;
    }e[200010];
    int head[100010],pre[100010],fa[100010],siz[100010],dep[100010],son[100010],top[100010],dfn[100010],a[100010],d[100010],cnt=0,tot=0;
    vector<int>ans;
    void add(int u,int v,int id)
    {
    	cnt++;  e[cnt]=(node){head[u],v,id};  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)
    		{
    			pre[e[i].to]=e[i].id;
    			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)
    {
    	tot++;  dfn[x]=tot;
    	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;
    }
    void dfs(int x)
    {
    	for(int i=head[x];i!=0;i=e[i].nxt)
    	{
    		if(e[i].to!=fa[x])
    		{
    			dfs(e[i].to);
    			d[x]+=d[e[i].to];
    		}
    	}
    }
    bool cmp(int a,int b)
    {
    	return dfn[a]<dfn[b];
    }
    struct Virtual_Tree
    {
    	stack<int>s;
    	void build(int len)
    	{
    		sort(a+1,a+1+len,cmp);
    		while(s.empty()==0)  s.pop();
    		for(int i=1;i<=len;i++)
    		{
    			if(s.empty()==0)
    			{
    				int rt=lca(a[i],s.top());
    				while(s.empty()==0&&s.top()!=rt)
    				{
    					int tmp=s.top();  s.pop();
    					if(s.empty()==1||dfn[s.top()]<dfn[rt])
    					{
    						s.push(rt);
    					}
    					d[tmp]++;  d[s.top()]--;
    				}
    			}
    			s.push(a[i]);
    		}
    		while(s.size()>=2&&s.top()!=1)
    		{
    			int tmp=s.top();  s.pop();
    			d[tmp]++;  d[s.top()]--;
    		}
    	}
    }V;
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif	
    	int n,m,k,u,v,i,j;
    	cin>>n>>m>>k;
    	for(i=1;i<=n-1;i++)
    	{
    		cin>>u>>v;
    		add(u,v,i);  add(v,u,i);
    	}
    	dfs1(1,0);  dfs2(1,1);
    	for(j=1;j<=m;j++)
    	{
    		cin>>u;
    		for(i=1;i<=u;i++)  cin>>a[i];
    		V.build(u);
    	}
    	dfs(1);
    	for(i=1;i<=n;i++)  if(d[i]>=k)  ans.push_back(pre[i]);
    	sort(ans.begin(),ans.end());
    	cout<<ans.size()<<endl;
    	for(i=0;i<ans.size();i++)  cout<<ans[i]<<" ";
    	return 0;
    }
    

2.28

闲话

做题纪要

luogu P11145 「SFMOI Round I」Strange Homura Game

  • 先随便问一个大数 x ,设 y=xmodm ,则有 m|(xy) 。当询问 xy1 时可以得到 (xy1)modm=m1

    点击查看代码
    const ll inf=100000000000000013;
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	ll t,a,b,i;
    	cin>>t;
    	for(i=1;i<=t;i++)
    	{
    		cout<<"? "<<inf<<endl;
    		cin>>a;
    		cout<<"? "<<inf-a-1<<endl;
    		cin>>b;
    		cout<<"! "<<b+1<<endl;
    	}
    	return 0;
    }
    

luogu P11144 「SFMOI Round I」Strange Madoka Game

  • 中国剩余定理。

    点击查看代码
    const ll p[3]={0,400000000,399999999};
    ll a[3];
    ll smul(ll a,ll b,ll p)
    {
    	ll ans=0;
    	while(b)
    	{
    		if(b&1)  ans=(ans+a)%p;
    		b>>=1;
    		a=(a+a)%p;
    	}
    	return ans;
    }
    void exgcd(ll a,ll b,ll &x,ll &y)
    {
    	if(b==0)
    	{
    		x=1;  y=0;
    		return;
    	}
    	else
    	{
    		exgcd(b,a%b,y,x);
    		y-=a/b*x;
    	}
    }
    ll inv(ll a,ll p)
    {
    	ll x,y;  exgcd(a,p,x,y);
    	return (x%p+p)%p;
    }
    ll crt(ll n)
    {
    	ll mul=1,ans=0,r;
    	for(ll i=1;i<=n;i++)  mul*=p[i];
    	for(ll i=1;i<=n;i++)
    	{
    		r=mul/p[i];
    		ans=(ans+smul(a[i]*r%mul,inv(r,p[i]),mul))%mul;
    	}
    	return ans;
    }
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	ll t,i;
    	cin>>t;
    	for(i=1;i<=t;i++)
    	{
    		cout<<"? "<<p[1]<<endl;
    		cin>>a[1];
    		cout<<"? "<<p[2]<<endl;
    		cin>>a[2];
    		cout<<"! "<<crt(2)<<endl;
    	}
    	return 0;
    }
    

luogu P5353 树上后缀排序

luogu P6271 [湖北省队互测2014] 一个人的数论

luogu P4593 [TJOI2018] 教科书般的亵渎

luogu P3270 [JLOI2016] 成绩比较

LibreOJ 6024. XLkxc

luogu P4463 [集训队互测 2012] calc

BZOJ2194 快速傅立叶之二

luogu P3338 [ZJOI2014] 力

luogu P4199 万径人踪灭

luogu P4238 【模板】多项式乘法逆

luogu P5158 【模板】多项式快速插值

luogu P4721 【模板】分治 FFT

luogu P8264 [Ynoi Easy Round 2020] TEST_100

luogu P10151 [Ynoi1999] SMV CC-64“蝰蛇”

posted @   hzoi_Shadow  阅读(134)  评论(5编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
扩大
缩小
点击右上角即可分享
微信分享提示