8-23模拟赛解题报告

新来Ag大佬,下午写了三小时成绩是我三倍(捂脸


T1.淘淘种地

题目大意:给出一个矩阵,每次选出一个子矩阵与一个权值,如果矩阵内权值与给出权值不同,则变成(-1),求最后有多少个数变成了(-1)

本题有一个部分分,就是矩阵内权值只有(1)(2),也就是说只有两种情况,我们其实也可以看成(1)(0),这样就变成了一个比较直观的问题。

我们可以发现,题目中要求的只是最后有多少个数变成了(-1),这就提醒我们可以最后一起统计答案,所以二维差分就变成了一个不错的选择。

(0)(1)分成两部分计算,然后答案取并就好了。开两个数组,分别保存(0)(1)的差分值,最后前缀和统计答案,如果这个点(a_{ij}~xor~1)后的值的覆盖了这个位置的话,答案(+1)

现在我们来看正解,通过运用上面的方法,我们想到把所有数二进制拆分,然后每一位进行一次上面的操作,最后答案取并就好了。时间复杂度为(O((nm+T)log{nm}))

小结:

解题关键:想到二进制拆分每一个数统计答案

芝士点:差分

不知道是我打炸了还是咋滴,总是TLE或者RE,没办法,只能贴一个(60)分代码了 (~

#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define re register
using namespace std;
const int N=1000005;
inline int read()
{
	re int x=0,f=1;
	re char ch=getchar();
	for(;ch>'9'||ch<'0';ch=getchar())if(ch=='-')f*=-1;
	for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<1)+(x<<3)+(ch^48);
	return x*f;
}
int n,m,T,aa,vis[N][20][2],ans,a[N][20],mid;
bool finvis[N];
inline int to(int x,int y)
{
	if(x<1||y<1)return 0;
	return (x-1)*m+y;
}
int main()
{
	scanf("%d%d%d",&n,&m,&T);
	for(re int i=1;i<=n;i++)
		for(re int j=1;j<=m;j++)
		{
			aa=read();
			for(re int k=0;k<20;k++)
			a[to(i,j)][k]=(aa&(1<<k));
		}
	for(re int i=1,type,x1,x2,y1,y2;i<=T;i++)
	{
		x1=read(),y1=read(),x2=read(),y2=read(),type=read();
		for(re int j=0;j<20;j++)
		{
			mid=(type&(1<<j));
			vis[to(x1,y1)][j][mid]++;
			if(x2+1<=n&&y2+1<=m)vis[to(x2+1,y2+1)][j][mid]++;
			if(x2+1<=n) vis[to(x2+1,y1)][j][mid]--;
			if(y2+1<=m) vis[to(x1,y2+1)][j][mid]--;
		}
	}
	for(re int i=1;i<=n;i++)
		for(re int j=1;j<=m;j++)
		{
			mid=to(i,j);
			for(re int k=0;k<20;k++)
			{
				vis[mid][k][0]+=vis[to(i-1,j)][k][0]+vis[to(i,j-1)][k][0]-vis[to(i-1,j-1)][k][0];
				vis[mid][k][1]+=vis[to(i-1,j)][k][1]+vis[to(i,j-1)][k][1]-vis[to(i-1,j-1)][k][1];
				if(vis[mid][k][a[mid][k]^1]>0){ans++;break;}
			}
		}
	printf("%d",ans);
	return 0;
}

T2.蓝蓝的数列

题目大意:定义如下数列:

[a_n=left{egin{aligned}0~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ n=1\a_{lfloorfrac{n}{2} floor}+(-1)^{frac{n(n+1)}{2}}~~~~nge 2end{aligned} ight. ]

(sum_{i=1}^{n}left|a_i ight|)的值。


考场上想了好久呀!(题目漏看了一个绝对值

现在还不会,以后再来补吧,咕咕咕~~


T3.树

题目大意:给出一棵树,求所有包含节点(x)的联通块的权值和,一个连通块的权值定义为这个联通块的所有点的权值之积。支持操作为修改权值和改变一个节点的父亲。

这题考场上毫无头绪,也是看题解才略懂一些。

这题也是一道DP题,(f[u][i])为以(u)为根的子树中,包含根节点的联通块大小为(i)的权值和。(

考虑到联通块大小最大为(10),所以我们可以更新每个点与其距离为(10)以内的祖先节点,每次递归计算答案。

先是操作一,修改权值,其实我们可以将这个点对其祖先节点的贡献归零,再重新计算新的贡献。操作二的方式差不多,删去这个点对其原祖先节点的贡献,再重新计算对现在祖先节点的贡献,由于联通块大小最多为(c),更新一个节点的(f)需要(O(c^2)),所以每次删除和计算的时间复杂度为(O(c^3))

又由于联通块的权值定义为点的乘积,所以我们可以重新定义运算符,使其变成卷积的形式就可以大大减小代码量 (可蒟蒻的我只会看题解写

小结:

解题关键:想到如何快速计算贡献

芝士点:树形DP

手起,码落:

#include<bits/stdc++.h>
#define re register
#define mod 1000000007
using namespace std;
inline int read()
{
	re int x=0,f=1;
	re char ch=getchar();
	for(;ch>'9'||ch<'0';ch=getchar())if(ch=='-')f*=-1;
	for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<1)+(x<<3)+(ch^48);
	return x*f;
}
inline int Pow(long long x,int y)
{
	re long long sum=1;
	for(;y;x=(x*x)%mod,y>>=1)
		if(y&1)sum=(sum*x)%mod;
	return sum;
}
const int N=200005;
struct node{long long val[11];}e[N];
node operator +(node a,node b)
{
	for(re int i=9;i>0;i--)
		for(re int j=1;j+i<=10;j++)
			a.val[i+j]=(a.val[i+j]+a.val[i]*b.val[j])%mod;
	return a;
}
node operator -(node a,node b)
{
	for(re int i=1;i<=9;i++)
		for(re int j=1;j+i<=10;j++)
			a.val[i+j]=(a.val[i+j]+(mod-a.val[i])*b.val[j]%mod)%mod;
	return a;
}
int n,m,fa[N];
long long a[N];
inline void dele(int x,int dep)
{
	if(x==1||dep==0)return ;
	dele(fa[x],dep-1);
	e[fa[x]]=e[fa[x]]-e[x];
}
inline void add(int x,int dep)
{
	if(x==1||dep==0)return ;
	e[fa[x]]=e[fa[x]]+e[x];
	add(fa[x],dep-1);
}
inline node query(int x,int y)
{
	if(x==1||y==0)return e[x];
	return e[x]+(query(fa[x],y-1)-e[x]);
}
int main()
{
	n=read(),m=read();
	for(re int i=1;i<=n;i++) a[i]=read(),e[i].val[1]=a[i];
	for(re int i=2;i<=n;i++) fa[i]=read();
	for(re int i=n;i>1;i--) e[fa[i]]=e[fa[i]]+e[i];
	for(re int i=1,type,x,y;i<=m;i++)
	{
		type=read(),x=read(),y=read();
		if(type==0) 
		{
			dele(x,10);
			for(re int j=1;j<=10;j++)
				e[x].val[j]=e[x].val[j]*Pow(a[x],mod-2)%mod*y%mod;
			a[x]=y,add(x,10);
		}
		else if(type==1) 
		{
			dele(x,10),add(fa[x],9),fa[x]=y;
			dele(fa[x],9),add(x,10);
		}
		else printf("%lld
",query(x,y).val[y]);
	}
	return 0;
}
原文地址:https://www.cnblogs.com/jkzcr01-QAQ/p/13574137.html