题解

套路题,直接切掉(果然,我们学校的题温和多了,其他学校的题完全没有思路。。。)

直接手算一下生成函数,再推出递推式就可以矩阵快速幂了

具体方法:之前做过一道更难的题:https://blog.csdn.net/C20180602_csq/article/details/103823671

代码:

#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>

#define maxn 105
#define MOD 998244353

using namespace std;

inline long long getint()
{
	long long num=0,flag=1;char c;
	while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
	while(c>='0'&&c<='9')num=num*10+c-48,c=getchar();
	return num*flag;
}

long long n;
int k;

struct node{
	long long a[3][3];
	friend node operator*(node x,node y)
	{
		node z;memset(z.a,0,sizeof z.a);
		for(int i=0;i<3;i++)for(int j=0;j<3;j++)for(int k=0;k<3;k++)(z.a[i][j]+=x.a[i][k]*y.a[k][j])%=MOD;
		return z;
	}
}A,I;

inline long long ksm(long long num,long long k)
{
	long long ret=1;
	for(;k;k>>=1,num=num*num%MOD)if(k&1)ret=ret*num%MOD;
	return ret;
}

inline node ksm(node num,long long k)
{
	node ret=I;
	for(;k;k>>=1,num=num*num)if(k&1)ret=ret*num;
	return ret;
}

inline void solve1()
{
	I.a[0][0]=I.a[0][1]=1;
	A.a[0][0]=A.a[0][1]=A.a[1][0]=1,A.a[1][1]=2;
	if(!n)printf("1\n");
	else printf("%lld\n",(ksm(A,n).a[0][0]-ksm(A,n-1).a[0][0]+MOD)%MOD);
}

inline void solve2()
{
	I.a[0][0]=I.a[0][2]=1;
	A.a[0][0]=A.a[0][1]=A.a[0][2]=1;
	A.a[1][0]=A.a[1][2]=MOD-1;
	A.a[2][0]=2,A.a[2][2]=3;
	if(n<2)printf("1\n");
	else printf("%lld\n",((ksm(A,n).a[0][0]-2*ksm(A,n-1).a[0][0]+ksm(A,n-2).a[0][0])%MOD+MOD)%MOD);
}

int main()
{
	freopen("generate.in","r",stdin);
	freopen("generate.out","w",stdout);
	
	n=getint(),k=getint();
	I.a[0][0]=I.a[1][1]=I.a[2][2]=1;
	if(k==0)printf("%lld\n",ksm(2,max(n-1,0ll)));
	if(k==1)solve1();
	if(k==2)solve2();
}

 

 

 

 

 

题解

很明显的离线线段树

(考试的时候明明写的正解,却以为自己只写了60,结果后来测出来只有10分,当场惊讶。。。)

(然后调了1h发现自己pushdown的时候把len写成了sum,竟然还有10分!!!)

(改了之后就过了。。。)

所以考试的时候不要脑抽啊啊啊

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
inline int gi()
{
	char c;int num=0,flg=1;
	while((c=getchar())<'0'||c>'9')if(c=='-')flg=-1;
	while(c>='0'&&c<='9'){num=num*10+c-48;c=getchar();}
	return num*flg;
}
#define N 100005
#define LL long long
#define lc i<<1
#define rc i<<1|1
struct node{
	int l,r,len;
	LL sum,la;
}a[32][N<<2];
struct qnode{
	int l,r,k,id;
	bool operator < (const qnode t)const{return r<t.r;}
}q[2*N];
LL ans[2*N];
int val[N],pos[N][32],tmp[32];
void pushdown(int flg,int i)
{
	if(a[flg][i].la&&a[flg][i].l<a[flg][i].r){
		a[flg][lc].sum+=1ll*a[flg][i].la*a[flg][lc].len;
		a[flg][rc].sum+=1ll*a[flg][i].la*a[flg][rc].len;
		a[flg][lc].la+=a[flg][i].la;
		a[flg][rc].la+=a[flg][i].la;
		a[flg][i].la=0;
	}
}
void build(int flg,int i,int l,int r)
{
	a[flg][i].l=l;a[flg][i].r=r;
	a[flg][i].len=r-l+1;
	if(l==r)return;
	int mid=(l+r)>>1;
	build(flg,lc,l,mid);
	build(flg,rc,mid+1,r);
}
void insert(int flg,int i,int l,int r)
{
	if(a[flg][i].l>r||a[flg][i].r<l)return;
	pushdown(flg,i);
	if(l<=a[flg][i].l&&a[flg][i].r<=r){
		a[flg][i].la++;
		a[flg][i].sum+=a[flg][i].len;
		return;
	}
	insert(flg,lc,l,r);insert(flg,rc,l,r);
	a[flg][i].sum=a[flg][lc].sum+a[flg][rc].sum;
}
LL query(int flg,int i,int l,int r)
{
	if(a[flg][i].l>r||a[flg][i].r<l)return 0ll;
	pushdown(flg,i);
	if(l<=a[flg][i].l&&a[flg][i].r<=r)return a[flg][i].sum;
	return query(flg,lc,l,r)+query(flg,rc,l,r);
}
int main()
{
	freopen("sequence.in","r",stdin);
	freopen("sequence.out","w",stdout);
	int n,m,i,j,k;
	n=gi();m=gi();
	for(i=1;i<=n;i++)val[i]=gi();
	for(i=1;i<=m;i++){q[i].l=gi();q[i].r=gi();q[i].k=gi();q[i].id=i;}
	sort(q+1,q+m+1);
	for(k=0;k<=30;k++)build(k,1,1,n);
	for(i=1,j=1;i<=n;i++){
		for(k=0;k<=30;k++){
			if((val[i]>>k)&1) pos[i][k]=pos[i-1][k];
			else pos[i][k]=i;
		}
		memcpy(tmp,pos[i],sizeof(pos[i]));
		tmp[31]=0;sort(tmp,tmp+32);
		for(k=0;k<=30;k++)insert(k,1,tmp[k]+1,tmp[k+1]);
		while(j<=m&&q[j].r==i){
			ans[q[j].id]+=query(q[j].k,1,q[j].l,q[j].r);
			j++;
		}
	}
	for(i=1;i<=m;i++)
		printf("%lld\n",ans[i]);
}

 

 

 

 

 

 

题解

先分析一下问题:一个路径怎样才会被完全包含

我们可以把路径(u,v)分为两类:

1、u、v不是祖先关系

那么对包含它的路径(x,y)的要求就是(x在u子树,且y在v子树)或(x在v子树,且y在u子树)

2、u、v是祖先关系

假设u是v的祖先

我们可以用倍增找到u通向v的儿子z

然后路径(x,y)的要求就是(x不在z子树且y在v子树)或(y不在z子树且x在v子树)

发现这种类型的限制条件可以转换到dfs序上来表达

然后就可以用KD树来解决(如果离线的话直接CDQ就可以了)

调了两个小时,发现自己把mx[0][0]和mx[0][1]设成了INF,还一直以为是对的。。。

应该把mi[0][0]和mi[0][1]设成INF

然后就过了

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
inline int gi(){
	char c;int num=0,flg=1;
	while((c=getchar())<'0'||c>'9')if(c=='-')flg=-1;
	while(c>='0'&&c<='9'){num=num*10+c-48;c=getchar();}
	return num*flg;
}
#define N 200005
#define LOG 16
#define lc ch[i][0]
#define rc ch[i][1]
int n,m;
int fir[N],to[2*N],nxt[2*N],cnt;
int f[N][LOG+2],siz[N],dep[N];
int in[N],out[N],dfn;
int treearray[N],val[N];
int D,tmp[N],TCT,rt,tot;
int a[N][2],ch[N][2],mi[N][2],mx[N][2],sz[N],sum[N],vl[N];
int zb[2],qmx[2],ans;
char s[4];

void adde(int a,int b)
{
	to[++cnt]=b;nxt[cnt]=fir[a];fir[a]=cnt;
	to[++cnt]=a;nxt[cnt]=fir[b];fir[b]=cnt;
}
void dfs(int u)
{
	in[u]=++dfn;
	siz[u]=1;dep[u]=dep[f[u][0]]+1;
	for(int v,p=fir[u];p;p=nxt[p]){
		v=to[p];
		if(v!=f[u][0]){
			f[v][0]=u;
			dfs(v);
			siz[u]+=siz[v];
		}
	}
	out[u]=dfn;
}
inline int getk(int x,int k)
{
	for(int i=LOG;i>=0;i--)
		if(k&(1<<i)) x=f[x][i];
	return x;
}
inline int getd(int x,int d)
{
	return getk(x,dep[x]-d);
}
inline int LCA(int x,int y)
{
	if(dep[x]<dep[y])swap(x,y);
	x=getd(x,dep[y]);
	if(x==y)return x;
	for(int i=LOG;i>=0;i--)
		if(f[x][i]!=f[y][i])
			x=f[x][i],y=f[y][i];
	return f[x][0];
}
inline void update(int x,int k)
{
	while(x<=n){
		treearray[x]+=k;
		x+=(x&-x);
	}
}
inline int getsum(int x)
{
	int sum=0;
	while(x){
		sum+=treearray[x];
		x-=(x&-x);
	}
	return sum;
}
//long long quecon;
inline void pushup(int i)
{
	mx[i][0]=max(max(mx[lc][0],mx[rc][0]),a[i][0]);
	mx[i][1]=max(max(mx[lc][1],mx[rc][1]),a[i][1]);
	mi[i][0]=min(min(mi[lc][0],mi[rc][0]),a[i][0]);
	mi[i][1]=min(min(mi[lc][1],mi[rc][1]),a[i][1]);
	sum[i]=sum[lc]+sum[rc]+vl[i];sz[i]=sz[lc]+sz[rc]+1;
}
inline bool cmp(const int &x,const int &y){return a[x][D]<a[y][D];}
void build(int &i,int l,int r,int d)
{
	int mid=(l+r)>>1;D=d;
	nth_element(tmp+l,tmp+mid,tmp+r+1,cmp);
	i=tmp[mid];lc=rc=0;
	if(l<mid)build(lc,l,mid-1,d^1);
	if(r>mid)build(rc,mid+1,r,d^1);
	pushup(i);
}
void prebuild(int i)
{
	if(!i)return;
	prebuild(lc);
	tmp[++TCT]=i;
	prebuild(rc);
}
void insert(int &i,int d,int k)
{
	if(!i){
		i=++tot;sum[i]=vl[i]=k;
		a[i][0]=mx[i][0]=mi[i][0]=zb[0];
		a[i][1]=mx[i][1]=mi[i][1]=zb[1];
		return;
	}
	bool flg=(zb[d]>=a[i][d]);
	insert(ch[i][flg],d^1,k);
	pushup(i);
	if(1.0*max(sz[lc],sz[rc])>0.75*sz[i]){
		TCT=0;prebuild(i);
		build(i,1,TCT,d);
	}
}
void query(int i)
{
	//quecon++;
	if(!i||qmx[0]<mi[i][0]||qmx[1]<mi[i][1])return;
	if(mx[i][0]<=qmx[0]&&mx[i][1]<=qmx[1]){ans+=sum[i];return;}
	if(a[i][0]<=qmx[0]&&a[i][1]<=qmx[1])ans+=vl[i];
	query(lc);query(rc);
}
void addmat(int l1,int r1,int l2,int r2)
{
	zb[0]=l1,zb[1]=l2,insert(rt,0,1);
	if(r1<n)zb[0]=r1+1,zb[1]=l2,insert(rt,0,-1);
	if(r2<n)zb[0]=l1,zb[1]=r2+1,insert(rt,0,-1);
	if(r1<n&&r2<n)zb[0]=r1+1,zb[1]=r2+1,insert(rt,0,1);
}
int main()
{
	freopen("water.in","r",stdin);
	freopen("water.out","w",stdout);
	mi[0][0]=mi[0][1]=1000000000;
	int i,j,u,v,z;n=gi();m=gi();
	for(i=1;i<n;i++){
		u=gi();v=gi();
		adde(u,v);
	}
	dfs(1);
	for(j=1;j<=LOG;j++)
		for(i=1;i<=n;i++)
			f[i][j]=f[f[i][j-1]][j-1];
	
	for(i=1;i<=m;i++){
		scanf("%s",s);
		u=gi()^ans;v=gi()^ans;
		if(in[u]>in[v])swap(u,v);
		if(s[0]=='A'){
			if(u==v){
				val[u]++;
				update(in[u],1);
				update(out[u]+1,-1);
				continue;
			}
			if(in[u]<=in[v]&&out[v]<=out[u]){
				z=getd(v,dep[u]+1);
				addmat(1,in[z]-1,in[v],out[v]);
				if(out[z]<n)addmat(in[v],out[v],out[z]+1,n);
			}
			else addmat(in[u],out[u],in[v],out[v]);
		}
		else{
			int lca=LCA(u,v);
			ans=getsum(in[u])+getsum(in[v])-2*getsum(in[lca])+val[lca];
			qmx[0]=in[u];qmx[1]=in[v];query(rt);
			printf("%d\n",ans);
		}
	}
	//freopen("CON","w",stdout);
	//printf("%lld %d",quecon,tot);
}