T1 树上的数

考场上比较脑瘫没有想到直接dfs就行了这样是O(n+m)的,傻不拉几地多添了个log,

不过因为accoder的评测机太弱了,绝大多数人的正解都是60分,所以没有什么差别;

直接dfs,d到不能d的点就return就好了

#include<bits/stdc++.h>
#define lid id<<1
#define rid id<<1|1
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
int n,m,a,b;
const int mod=19760817, maxn=5e6+5;
int fa[maxn],q[maxn],sum[2];
int head[maxn],num;
struct edge{int to,nxt;}e[maxn<<1];
inline  void add(int x,int y)
{e[++num]=(edge){y,head[x]};head[x]=num;}
bool vis[maxn];
inline void dfs(int x)
{
	if(vis[x]) return;
	vis[x]=1;++sum[1];
	for(int i=head[x];i;i=e[i].nxt)
	dfs(e[i].to);
}
signed main()
{
	freopen("tree.in","r",stdin);
	freopen("tree.out","w",stdout);
	n=read();m=read();a=read();b=read();
	q[1]=read(); int x=read(),y=read();
	fa[2]=1; add(1,2); for(int i=3;i<=n;i++)
	fa[i]=((1ll*fa[i-1]*a+b)^mod)%(i-1)+1,add(fa[i],i);
	sum[1]=0;int ans=0; if(!vis[q[1]])dfs(q[1]);ans^=(n-sum[1]);
	for(int i=2;i<=m;i++)
	{
		q[i]=(((1ll*q[i-1]*x+y)^mod)^(i<<1))%(n-1)+2;
		dfs(q[i]);ans^=(n-sum[1]);
	}
	printf("%d\n",ans);
}
T2 时代的眼泪

看到这个题目的时候有点慌,以为是那个dio题,结果后来发现只是题目一样,一个比较显然的换根

将以x为根变为他的儿子y为根,他的变化量是在除了y的子树之外的点比y小的点的个数减去y的子树里比x小的数的个数

可以先dfs出来根为1的最终答案,然后用树状数组处理出对应变化量,最后再dfs一次换根就行了

复杂度O(n*log(n))。不过考场上我没有想到如何用树状数组解决这个问题,在dfs序上开了一棵主席树

复杂度也是O(nlog(n))的,不过常数比较大,再加上那个OJ比较慢,最后只有90分

主席树写法:

#include<bits/stdc++.h>
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const int maxn=1e6+5;
struct edge{int to,nxt;}e[maxn<<1];
int head[maxn],num,w[maxn],lsh[maxn],ext,n;
inline  void add(int x,int y)
{
	e[++num]=(edge){y,head[x]};head[x]=num;
	e[++num]=(edge){x,head[y]};head[y]=num;
}
struct szsz{
	#define lowbit(x) (x&(-x))
	int c[maxn];
	inline int query(int x){int res=0;for(;x;x-=lowbit(x))res+=c[x];return res;}
	inline void update(int x,int val){for(;x<=ext;x+=lowbit(x))c[x]+=val;}
}z;
int dfn[maxn],pp[maxn],cnt,siz[maxn];
long long ans[maxn];
inline void dfs1(int x,int f)
{
	ans[1]=ans[1]+z.query(ext)-z.query(w[x]);
	z.update(w[x],1);siz[x]=1;
	dfn[x]=++cnt;pp[cnt]=x;
	for(int i=head[x];i;i=e[i].nxt)
	{
		int y=e[i].to;
		if(y==f)continue;
		dfs1(y,x); siz[x]+=siz[y];
	} z.update(w[x],-1);
}
struct hjts{
	int sum[maxn*23],root[maxn],cnt,ls[maxn*23],rs[maxn*23];
	inline void update(int &rt1,int rt2,int l,int r,int pos)
	{
		rt1=++cnt; sum[rt1]=sum[rt2]; 
		++sum[rt1]; if(l==r)return ;
		ls[rt1]=ls[rt2];rs[rt1]=rs[rt2];
		int mid=(l+r)>>1;
		if(pos<=mid) update(ls[rt1],ls[rt2],l,mid,pos);
		else update(rs[rt1],rs[rt2],mid+1,r,pos);
		sum[rt1]=sum[ls[rt1]]+sum[rs[rt1]];
	}
	inline int query(int rt,int rt2,int l,int r,int ll,int rr)
	{
		if(ll>rr||rt==rt2)return 0;
		if(l>=ll&&r<=rr) return sum[rt]-sum[rt2];
		int mid=(l+r)>>1,res=0;
		if(ll<=mid) res+=query(ls[rt],ls[rt2],l,mid,ll,rr);
		if(rr>mid) res+=query(rs[rt],rs[rt2],mid+1,r,ll,rr);
		return res;
	}
	inline int getval(int l,int r,int ll,int rr)
	{
		if(l>r||ll>rr)return 0;
		return query(root[r],root[l-1],1,ext,ll,rr);
	}
}tr;
inline void dfs2(int x,int f)
{
	for(int i=head[x];i;i=e[i].nxt)
	{
		int y=e[i].to; if(y==f)continue;
		int tmp1=tr.getval(1,dfn[y]-1,1,w[y]-1)+tr.getval(dfn[y]+siz[y],n,1,w[y]-1);
		int tmp2=tr.getval(dfn[y],dfn[y]+siz[y]-1,1,w[x]-1);
		ans[y]=ans[x]+tmp1-tmp2; dfs2(y,x);
	}
}
signed main()
{
	freopen("tears.in","r",stdin);
	freopen("tears.out","w",stdout);
	n=read();int q=read();
	for(int i=1;i<=n;i++)
	{
		w[i]=read();
		lsh[++ext]=w[i];
	}
	for(int i=1;i<n;i++)add(read(),read());
	sort(lsh+1,lsh+1+ext);
	ext=unique(lsh+1,lsh+1+ext)-lsh-1;
	for(int i=1;i<=n;i++)w[i]=lower_bound(lsh+1,lsh+1+ext,w[i])-lsh;
	dfs1(1,0);for(int i=1;i<=n;i++)
	tr.update(tr.root[i],tr.root[i-1],1,ext,w[pp[i]]);
	dfs2(1,0);int x; for(int i=1;i<=q;i++)
	x=read(),printf("%lld\n",ans[x]);
}

树状数组写法:

#include<bits/stdc++.h>
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const int maxn=1e6+5;
struct edge{int to,nxt;}e[maxn<<1];
int head[maxn],num,w[maxn],lsh[maxn],ext,n;
inline  void add(int x,int y)
{
	e[++num]=(edge){y,head[x]};head[x]=num;
	e[++num]=(edge){x,head[y]};head[y]=num;
}
struct szsz{
	#define lowbit(x) (x&(-x))
	int c[maxn];
	inline int query(int x){int res=0;for(;x;x-=lowbit(x))res+=c[x];return res;}
	inline void update(int x,int val){for(;x<=ext;x+=lowbit(x))c[x]+=val;}
}z;
int dfn[maxn],pp[maxn],sum1[maxn],sum2[maxn],cnt,siz[maxn];
long long ans[maxn];
inline void dfs1(int x,int f)
{
	z.update(w[x],1);siz[x]=1;
	for(int i=head[x];i;i=e[i].nxt)
	{
		int y=e[i].to;
		if(y==f)continue;
		int jb=z.query(w[x]-1); 
		int bj=z.query(w[y]-1);
		dfs1(y,x); 
		sum2[y]=z.query(w[y]-1)-bj;
		sum1[y]=z.query(w[x]-1)-jb;
		ans[1]+=sum2[y];
	}
	if(x==1)ans[1]+=z.query(w[1]-1);
}
inline void dfs2(int x,int f)
{
	for(int i=head[x];i;i=e[i].nxt)
	{
		int y=e[i].to; if(y==f)continue;
		ans[y]=ans[x]-sum1[y]+z.query(w[y]-1)-sum2[y]; 
		dfs2(y,x);
	}
}
signed main()
{
	freopen("tears.in","r",stdin);
	freopen("tears.out","w",stdout);
	n=read();int q=read();
	for(int i=1;i<=n;i++)
	{
		w[i]=read();
		lsh[++ext]=w[i];
	}
	for(int i=1;i<n;i++)add(read(),read());
	sort(lsh+1,lsh+1+ext);
	ext=unique(lsh+1,lsh+1+ext)-lsh-1;
	for(int i=1;i<=n;i++)w[i]=lower_bound(lsh+1,lsh+1+ext,w[i])-lsh;
	dfs1(1,0); dfs2(1,0);
	int x; for(int i=1;i<=q;i++)
	x=read(),printf("%lld\n",ans[x]);
}
T3 传统异能

这个题挺不错的,由于我自己之前只会O(n)求不同子序列个数的写法,所以没有想到可以用矩阵;

先说一下n^2的做法,我们可以设dp[i][1/0] 为第i个字符选或者不选的能够构成的子序列的个数

lst[c]为c这个字符上一次出现的位置,那么转移的话就是:

dp[i][1]=(dp[i-1][1]+dp[i-1][0]+1)%mod;
dp[i][0]=(dp[i-1][1]+dp[i-1][0])%mod;
if(lst[a[i]]) dp[i][0]=(dp[i][0]+mod-dp[lst[a[i]]][1])%mod;

当然,如果你写了这个式子之后就会很难想到正解,因为这个式子有减去的地方,会很难处理

所以我们就要用一种复杂度稍微高一些的做法来想到正解

我们设dp[i][j]为前i个字符里面,以j结尾的子序列有多少个,

那么转移的话就是

if(j==s[i]) dp[i][j]=dp[i][1]+dp[i][2]+dp[i][3]+1;
else dp[i][j]=dp[i-1][j];

我觉得还是比较好理解的,我们假如让所有的以c为结尾的子序列再加上一个c

那么现在以c结尾的子序列的最小长度都为2,那么再加上让以a和b结尾的子序列加一个c

就又补回来了,最后再加1表示前面一个都不被选择,只选它自己

我们发现这是一个很明显的矩形式子,因为字符集大小只有3,所以我们可以构建三个不同的矩阵

第1个矩阵大概长这个亚子

1 0 0 0
1 1 1 1
0 0 1 0 
0 0 0 1

对角线显然都是1,然后第i行是1,嗯就这样

然后我们可以用线段树维护一下每个点对应的矩形,以及区间乘积是多少 相信大家都知道矩形支持交换律

查询完之后再让答案矩形乘一个这个矩阵

1 0 0 0 
0 0 0 0
0 0 0 0
0 0 0 0

将第一列的加起来就是答案了

#include<bits/stdc++.h>
#define int long long
#define lid id<<1
#define rid id<<1|1
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const int mod=998244353;
const int maxn=1e5+5;
struct matrix{
	int mat[5][5];
	inline void init(){
		memset(mat,0,sizeof(mat));
		for(int i=0;i<=3;i++)mat[i][i]=1;
	}
	inline void clear(){memset(mat,0,sizeof(mat));}
	inline void print()
	{
		for(int i=0;i<=3;i++)
		{
			for(int j=0;j<=3;j++)
			cout<<mat[i][j]<<" ";
			cout<<endl;
		}
	}
	inline matrix operator*(const matrix &a)const{
		matrix c;c.clear();
		for(int i=0;i<=3;i++) for(int j=0;j<=3;j++) for(int k=0;k<=3;k++)
		(c.mat[i][j]=a.mat[i][k]*mat[k][j]%mod+c.mat[i][j])%=mod;
		return c;
	}
}tmp[4];
int n,m;
inline int id(char c){return c-'A'+1;}
char s[maxn];int a[maxn];
matrix sum[maxn<<2];
inline void init()
{
	tmp[1].init();tmp[2].init();tmp[3].init();
	for(int i=1;i<=3;i++) for(int j=0;j<=3;j++)
	tmp[i].mat[i][j]=1;tmp[0].clear();
	tmp[0].mat[0][0]=1;
}
inline void build(int id,int l,int r)
{
	if(l==r) return sum[id]=tmp[a[l]],void();
	int mid=(l+r)>>1;
	build(lid,l,mid);build(rid,mid+1,r);
	sum[id]=sum[lid]*sum[rid];
}
inline void update(int id,int l,int r,int pos)
{
	if(l==r) return sum[id]=tmp[a[pos]],void();
	int mid=(l+r)>>1;
	if(pos<=mid) update(lid,l,mid,pos);
	else update(rid,mid+1,r,pos);
	sum[id]=sum[lid]*sum[rid];
}
inline matrix query(int id,int l,int r,int ll,int rr)
{
	if(l>=ll&&r<=rr)return sum[id];
	int mid=(l+r)>>1;matrix a;a.init();
	if(ll<=mid) a=query(lid,l,mid,ll,rr);
	if(rr>mid) a=a*query(rid,mid+1,r,ll,rr);
	return a;
}
signed main()
{
	freopen("string.in","r",stdin);
	freopen("string.out","w",stdout);
	n=read();m=read(); scanf("%s",s+1);
	for(int i=1;i<=n;i++) a[i]=id(s[i]);
	init();build(1,1,n);
	while(m--)
	{
		int type=read();
		if(type==2)
		{
			int ll=read(),rr=read();
			matrix ans=query(1,1,n,ll,rr);
			ans=tmp[0]*ans;int sum=0;
			sum=(ans.mat[1][0]+ans.mat[2][0]+ans.mat[3][0])%mod;
			printf("%lld\n",sum);
		}
		else
		{
			int pos=read();char c;cin>>c;
			a[pos]=id(c);update(1,1,n,pos);
		}
	}
}

T4 铺设道路

我们假设第0块和第n+1块的高度为0,那么显然此时的答案不会改变

那么,我们首先可以先将其差分一下,我们将每个点的高度干到0,等价于将差分数组全部干到0

对于i>=1&&i<=n来说 我们设b[i]为d[i]-d[i-1],即差分数组

那么显然最短时间就是sigma(max(b[i],0));

对于b[l]和b[r],假设我们对l到r-1(r>l)之间施一次工,那么b[l]会-1,b[r]会+1

那么我们考虑一个贪心思想,如果我们想要总体力是最大的,那么我们应该让每个小于0的b[r]匹配的b[l]越远越好

最小的则反之,我们从1枚举每个b[i],如果b[i]>0就将其压入队列,否则就取队尾或者队首进行操作

复杂度O(n);

#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const int mod=1e9+7;
const int maxn=3e5+5;
int n,d[maxn],b[maxn];
deque<int >q;
signed main()
{
	freopen("road.in","r",stdin);
	freopen("road.out","w",stdout);
	n=read();int ans=0;
	for(int i=1;i<=n;i++)
	{
		d[i]=read();b[i]=d[i]-d[i-1];
		ans+=max(0ll,d[i]-d[i-1]);
	}
	printf("%lld\n",ans);
	int maxx=0,minn=0;n++;b[n]=-d[n-1];
	for(int i=1;i<=n;i++)
	{
		if(b[i]>0) q.push_back(i);
		else while(b[i])
		{
			int x=q.back();
			if(abs(b[i])>=abs(b[x]))
			{
				q.pop_back();
				(maxx+=(i-x)*(i-x)%mod*b[x]%mod)%=mod;
				b[i]+=b[x];b[x]=0;
			}
			else
			{
				(maxx+=(i-x)*(i-x)%mod*(-b[i])%mod)%=mod;
				b[x]+=b[i];b[i]=0;
			}
		}
	}
	for(int i=1;i<=n;i++) b[i]=d[i]-d[i-1];
	printf("%lld\n",maxx);
	for(int i=1;i<=n;i++)
	{
		if(b[i]>0) q.push_back(i);
		else while(b[i])
		{
			int x=q.front();
			if(abs(b[i])>=abs(b[x]))
			{
				q.pop_front();
				(minn+=(i-x)*(i-x)%mod*b[x]%mod)%=mod;
				b[i]+=b[x];b[x]=0;
			}
			else
			{
				(minn+=(i-x)*(i-x)%mod*(-b[i])%mod)%=mod;
				b[x]+=b[i];b[i]=0;
			}
		}
	}
	printf("%lld\n",minn);
}
总结::

距离noip2021不远了,csp的失利一直缠绕再我心头,不过我是不会放弃的,加油吧JYF!!!!!!!