好题记录

我是大鸽子。慢慢更新吧。

CF666E

跑一边后缀数组,转化为区间l,r值域为pl,pr的众数。

由于并不强制在线,考虑莫队。

莫队做完值域分块,支持单点修改区间查询max。

加法是简单的,减法不好做,于是就回滚一下就好了。

#include<bits/stdc++.h>
using namespace std;
 
typedef long long ll;
const ll N=1e6+3,H=998244353;
ll n,m,px[N],py[N],pz[N],tim,dfn[N],low[N],f[N],sz[N];
bool v[N];
struct Nod{ll y,id;}; 
vector<Nod>ve[N];
ll Ksm(ll x,ll y)
{
	ll s=1;
	for(ll i=1;i<=y;i<<=1,x=x*x%H)if(i&y)s=s*x%H;
	return s;
}
void Dfs(int x,int z)
{
	dfn[x]=low[x]=++tim;
	for(Nod t:ve[x])if(t.id!=z)
	{
		if(dfn[t.y]){low[x]=min(low[x],dfn[t.y]);continue;}
		Dfs(t.y,t.id);low[x]=min(low[x],low[t.y]);
		if(low[t.y]>dfn[x])v[t.id]=1;
	}
}
ll F(ll x){return f[x]==x?x:f[x]=F(f[x]);}
ll Ans(ll x){return x==1?3:(Ksm(2,x)+(x%2==1?-1:1)*2+H)%H;}
int main()
{
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0); 
	cin>>n>>m; 
	for(int i=1;i<=m;i++)cin>>px[i]>>py[i]>>pz[i],ve[px[i]].push_back({py[i],i}),ve[py[i]].push_back({px[i],i});
	if(n%2!=m%2){cout<<0;return 0;}
	Dfs(1,0);ll sa=0,sb=1,sc=1;
	for(int i=1;i<=n;i++)f[i]=i;
	for(int i=1;i<=m;i++)
	{
		if(v[i])sa+=pz[i]+1,sb=sb*Ksm(3,pz[i])%H; 
		else if(F(px[i])!=F(py[i]))f[F(px[i])]=F(py[i]);
	}
	for(int i=1;i<=n;i++)sz[F(i)]++;
	for(int i=1;i<=m;i++)if(!v[i])sz[F(px[i])]+=pz[i];
	for(int i=1;i<=n;i++)if(sz[i]>1)sb=sb*2%H; 
	for(int i=1;i<=n;i++)if(sz[i])sc=sc*Ans(sz[i])%H; 
	sa=Ksm(2,sa)*Ksm(Ksm(3,H-2),sa)%H;
	cout<<sa*sb%H*sc%H;
}
#include<bits/stdc++.h>
using namespace std;
 
typedef long long ll;
const int N=6e5+3;
int n,B,Q,BA,BB,pr[N],col[N],cnt[N],sx[N],sy[N],al[N],ar[N],ap[N];
char ch[N];
bool vis[N];
struct Suffix_Array
{
	int m=200,X[N],Y[N],c[N],sa[N],height[N],rk[N],num[N],lg[N],st[N][23];
	void Sa()
	{
		for(int i=1;i<=n;i++)X[i]=num[i],c[X[i]]++;
		for(int i=2;i<=m;i++)c[i]+=c[i-1];
		for(int i=n;i>=1;i--)sa[c[X[i]]--]=i;
		for(int k=1,p=0;k<=n;k=k<<1,p=0)
		{
			for(int i=n-k+1;i<=n;i++)Y[++p]=i;
			for(int i=1;i<=n;i++)if(sa[i]>k)Y[++p]=sa[i]-k;
			for(int i=1;i<=m;i++)c[i]=0;
			for(int i=1;i<=n;i++)c[X[i]]++;
			for(int i=2;i<=m;i++)c[i]+=c[i-1];
			for(int i=n;i>=1;i--)sa[c[X[Y[i]]]--]=Y[i],Y[i]=0;
			swap(X,Y);p=1;X[sa[1]]=1;
			for(int i=2;i<=n;i++)
			{
				if(Y[sa[i]]==Y[sa[i-1]]&&Y[sa[i]+k]==Y[sa[i-1]+k])X[sa[i]]=p;
				else X[sa[i]]=++p;
			}
			if(n==p)return;m=p;
		}
	}
	void Height()
	{
		int k=0;
		for(int i=1;i<=n;i++)rk[sa[i]]=i;
		for(int i=1;i<=n;i++)
		{
			if(rk[i]==1)continue;
			if(k)k--;
			int j=sa[rk[i]-1];
			while(i+k<=n&&j+k<=n&&num[i+k]==num[j+k])k++;
			height[rk[i]]=k;
		}
	}
	void St()
	{
		for(int i=2;i<=n;i++)lg[i]=lg[i>>1]+1;
		for(int i=1;i<=n;i++)st[i][0]=height[i];
		for(int j=1;j<=20;j++)for(int i=1;i+(1<<j)-1<=n;i++)
		    st[i][j]=min(st[i][j-1],st[i+(1<<j-1)][j-1]);
	}
	int Lcp(int l,int r)
	{
		int d=lg[r-l+1];
		return min(st[l][d],st[r-(1<<d)+1][d]);
	}
}SA;
struct Nod{int l,r,ql,qr,id;}a[N];
bool CA(Nod a,Nod b)
{
	return vis[a.id]!=vis[b.id]?vis[a.id]>vis[b.id]:ap[a.l]!=ap[b.l]?a.l<b.l:a.r<b.r;
}
#define mi ((l+r)/2+1)
void Baoli(int p)
{
	for(int i=a[p].l;i<=a[p].r;i++)if(a[p].ql<=col[i]&&col[i]<=a[p].qr)cnt[col[i]]++;
	for(int i=a[p].l;i<=a[p].r;i++)
	    if(a[p].ql<=col[i]&&col[i]<=a[p].qr&&(cnt[col[i]]>sx[p]||(cnt[col[i]]==sx[p]&&col[i]<sy[p])))
	        sx[p]=cnt[col[i]],sy[p]=col[i];
	for(int i=a[p].l;i<=a[p].r;i++)cnt[col[i]]=0;
}
void Pre()
{
	BA=sqrt(n);int t=n/BA;if(n%BA)t++;
	for(int i=1;i<=t;i++)al[i]=ar[i-1]+1,ar[i]=i*BA;
	ar[t]=n;
	for(int i=1;i<=t;i++)for(int j=al[i];j<=ar[i];j++)ap[j]=i; 
}
struct Block
{
	int val[N],mx[N],my[N],vval[N],mmx[N],mmy[N],bl[N],br[N],bp[N];
	void Clear(){for(int i=0;i<=B;i++)val[i]=mx[i]=my[i]=vval[i]=mmx[i]=mmy[i]=0;}
	void Init()
	{
		BB=sqrt(B);int t=B/BB;if(B%BB)t++;
		for(int i=1;i<=t;i++)bl[i]=br[i-1]+1,br[i]=i*BB;
		br[t]=B;
		for(int i=1;i<=t;i++)for(int j=bl[i];j<=br[i];j++)bp[j]=i; 
	}
	void Add(int x,int op)
	{
		if(!x)return;
	    val[x]++;int y=bp[x];
	    if(val[x]>mx[y]||(val[x]==mx[y])&&x<my[y])mx[y]=val[x],my[y]=x;
	    if(!op)mmx[y]=mx[y],mmy[y]=my[y],vval[x]=val[x];
	}
	void Del(int x)
	{
		if(!x)return;
		val[x]=vval[x];int y=bp[x];mx[y]=mmx[y],my[y]=mmy[y];
	}
	void Chk(int &x,int &y,int px,int py){if(px>x||(px==x&&py<y&&py))x=px,y=py;}
	void Ask(int x,int l,int r)
	{
		int y=a[x].id;
		if(bp[l]==bp[r])
		{
			for(int i=l;i<=r;i++)Chk(sx[y],sy[y],val[i],i);
			return;
		}
		for(int i=l;i<=br[bp[l]];i++)Chk(sx[y],sy[y],val[i],i);
		for(int i=bl[bp[r]];i<=r;i++)Chk(sx[y],sy[y],val[i],i);
		for(int i=bp[l]+1;i<bp[r];i++)Chk(sx[y],sy[y],mx[i],my[i]);
	}
}BL;
int main()
{
	cin>>(ch+1);n=strlen(ch+1);cin>>B;
	for(int i=1;i<=n;i++)SA.num[i]=ch[i]; 
	for(int t=1;t<=B;t++)
	{
		cin>>(ch+1);int z=strlen(ch+1);SA.num[++n]=++SA.m;
		for(int i=1;i<=z;i++)SA.num[++n]=ch[i],pr[n]=t;
	}
	cin>>Q;SA.Sa();SA.Height();SA.St();Pre();
	for(int i=1;i<=n;i++)col[i]=pr[SA.sa[i]]; 
	for(int i=1;i<=Q;i++)
	{
		cin>>a[i].ql>>a[i].qr>>a[i].l>>a[i].r;a[i].id=i;sy[i]=a[i].ql;
		int x=SA.rk[a[i].l],len=a[i].r-a[i].l+1,l=1,r=n-x;
		while(l<r)(SA.Lcp(x+1,x+mi)>=len)?l=mi:r=mi-1;
		a[i].r=(x==n||SA.height[x+l]<len)?x:x+l;l=1,r=x-1;
		while(l<r)(SA.Lcp(x-mi+1,x)>=len)?l=mi:r=mi-1;
		a[i].l=(x==1||SA.height[x-l+1]<len)?x:x-l;
		if(ap[a[i].l]==ap[a[i].r])Baoli(i),vis[i]=1;
	}
	sort(a+1,a+Q+1,CA);BL.Init();
	for(int i=1;i<=Q;i++)if(!vis[a[i].id])
	{
		int x=a[i-1].r+1,y=ap[a[i].l];
		if(i==1||vis[a[i-1].id]||ap[a[i].l]!=ap[a[i-1].l])BL.Clear(),x=al[y+1];
		for(int j=x;j<=a[i].r;j++)BL.Add(col[j],0);
		for(int j=ar[y];j>=a[i].l;j--)BL.Add(col[j],1);
		BL.Ask(i,a[i].ql,a[i].qr);
		for(int j=ar[y];j>=a[i].l;j--)BL.Del(col[j]);
	}
	for(int i=1;i<=Q;i++)cout<<sy[i]<<" "<<sx[i]<<endl;
}

纪念一下第一次补完 div1 的所有题

这个 1E 相较于其他的 1E 并不算太难。本题解部分参考官方题解。

先观察到一条边是好的当且仅当它的值和一个端点的值相同。

原因很简单,要求两端点值不同,若边权跟点权也不同,那么三个值分别只能为 \(1,2,3\),又因为 \(1 \oplus 2 \oplus 3=0\),不符合原条件。

那么我们假设两端点分别为 \(u,v\),如果边权与 \(u\) 的值相同就让 \(v\) 连向 \(u\),反之 \(u\) 连向 \(v\),那我们也就相当于要让边定向。

再观察到一个点是好的当且仅当它的出边度数为奇数。

还是反证法,如果一个点的出边度数为偶数,不妨设当前点的点权为 \(2\)(为其他值的时候同理),那么所有出边的异或和只能是 \(2\) 或者 \(0\),又因为所有入边的值为当前点的点权 \(2\),所以最终的异或和只能为 \(0\) 或者 \(2\),不符合条件。

那我们就成功的将题目转换为两部分:给所有点赋点权以及给所有边定向。分开计算最后相乘即可。

第一部分:

注意到原图是一个仙人掌,我们把原图的所有割边去掉就会成为若干个环。

注意到一条割边的两边贡献独立,只会在此处进行合并,具体地,如果两边的贡献分别为 \(x,y\),那么最后的答案为 \(\frac{2}{3}xy\) ,原因是一条边的两个端点值不能相同。

现在就将仙人掌拆成了若干个环,也就转换为长为 \(n\) 的环可以染 \(k\) 种颜色,相邻两点颜色不同的方案数。

这是一个 经典问题,直接使用结论,设长为 \(n\) 的环的答案为 \(s_n\) ,那么有 \(s_n=(k-1)^n+(-1)^n(k-1)\) ,在本题中,\(k=3\),当然你也打表/归纳法来找到通项公式。

注意:一个点也应该被视为一个环,但是通项公式适用于\(n>2\) ,需特殊处理一下。

那么这部分的答案为 \(\frac{2}{3}^p \prod s_{z}\)\(p\) 为割边的总数量,\(z\) 为每个环的环长。

第二部分:

还是注意到原图是仙人掌,由若干个环(环长大于 \(2\) )和若干个单点构成。

环的贡献显然为 \(2\),每个点的出边可以都连向顺时针/逆时针的下一个点,那我们就可以把一个环看成一个点,将原图缩成一棵树。

注意到叶子节点,如果当前点有出边数量是奇数(如果是一个环缩成一个点的话当前点会有出边),那么叶子只能接受入边,否则只能接受出边。

那我们把叶子删掉,直到把整个树删完即可,树的定向方案是唯一的。

注意:有可能有无解情况,比如说删到最后删到只剩两个点,每个点都只能接受出边/入边此时就会无解。

但是直接模拟删叶子判无解会比较麻烦,巧妙地做法是从奇偶分析,\(n\) 个点都只有奇数条出边,每条边都会被计算一次,那个总条数也就是 \(m\),那么有解当前仅当 \(n,m\) 奇偶性相同。

这部分的答案为 \(2^{cnt}\)\(cnt\) 为环长大于 \(2\) 的环长总个数。

实现精细能也许能做到线性,但是笔者比较懒,什么都是暴力做的,复杂度带个 \(log\)

#include<bits/stdc++.h>
using namespace std;
 
typedef long long ll;
const ll N=1e6+3,H=998244353;
ll n,m,px[N],py[N],pz[N],tim,dfn[N],low[N],f[N],sz[N];
bool v[N];
struct Nod{ll y,id;}; 
vector<Nod>ve[N];
ll Ksm(ll x,ll y)
{
	ll s=1;
	for(ll i=1;i<=y;i<<=1,x=x*x%H)if(i&y)s=s*x%H;
	return s;
}
void Dfs(int x,int z)
{
	dfn[x]=low[x]=++tim;
	for(Nod t:ve[x])if(t.id!=z)
	{
		if(dfn[t.y]){low[x]=min(low[x],dfn[t.y]);continue;}
		Dfs(t.y,t.id);low[x]=min(low[x],low[t.y]);
		if(low[t.y]>dfn[x])v[t.id]=1;
	}
}
ll F(ll x){return f[x]==x?x:f[x]=F(f[x]);}
ll Ans(ll x){return x==1?3:(Ksm(2,x)+(x%2==1?-1:1)*2+H)%H;}
int main()
{
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0); 
	cin>>n>>m; 
	for(int i=1;i<=m;i++)cin>>px[i]>>py[i]>>pz[i],ve[px[i]].push_back({py[i],i}),ve[py[i]].push_back({px[i],i});
	if(n%2!=m%2){cout<<0;return 0;}
	Dfs(1,0);ll sa=0,sb=1,sc=1;
	for(int i=1;i<=n;i++)f[i]=i;
	for(int i=1;i<=m;i++)
	{
		if(v[i])sa+=pz[i]+1,sb=sb*Ksm(3,pz[i])%H; 
		else if(F(px[i])!=F(py[i]))f[F(px[i])]=F(py[i]);
	}
	for(int i=1;i<=n;i++)sz[F(i)]++;
	for(int i=1;i<=m;i++)if(!v[i])sz[F(px[i])]+=pz[i];
	for(int i=1;i<=n;i++)if(sz[i]>1)sb=sb*2%H; 
	for(int i=1;i<=n;i++)if(sz[i])sc=sc*Ans(sz[i])%H; 
	sa=Ksm(2,sa)*Ksm(Ksm(3,H-2),sa)%H;
	cout<<sa*sb%H*sc%H;
}
posted @ 2023-07-29 15:08  Hanghang007  阅读(43)  评论(0)    收藏  举报