8.13考试总结(NOIP模拟38)[a·b·c]

重要的不是你做了多少事,而是你放了多少心思进去。

T1 a

解题思路

总结一下,是双指针运用不够熟练(zxb笑了笑)。

其实这个题是可以用树状数组卡过的(众所周知我是一个正直的人),但是一定是要打正解的。

树状数组比较好像,就和 入阵曲 一样只不过这个维护的是个范围。

因此需要树状数组维护前缀和,时间复杂度就多了一个 log

首先,这个数据范围明显是让我们 \(n^2m\) 跑过。

因此先枚举矩形的上边界,接着枚举列。

然后双指针扫比当前左边界为大矩形左边界,右边界为当前扫到列的矩形大的部分刚好在 \([l,r]\) 的列

注意卡一下边界,对于 \(l=0,r=n\times m\) 的需要特殊判断一下。

code

57pts 树状数组

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Pass"<<endl
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){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 N=40,M=5e4+10,K=N*M;
int n,m,ans,li,ri,s[N][M],pre[N][M],tre[K];
char ch[M];
int lowbit(int x)
{
	return x&(-x);
}
void insert(int x,int num)
{
	for(int i=x+1;i<=pre[n][m]+1;i+=lowbit(i))
		tre[i]+=num;
}
int query(int x)
{
	if(x<0)	return 0;
	int sum=0;
	for(int i=x+1;i;i-=lowbit(i))
		sum+=tre[i];
	return sum;
}
signed main()
{
	scanf("%lld%lld",&n,&m);
	for(int i=1;i<=n;i++)
	{
		scanf("%s",ch+1);
		for(int j=1;j<=m;j++)
			s[i][j]=ch[j]-'0';
	}
	scanf("%lld%lld",&li,&ri);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			pre[i][j]=pre[i-1][j]+pre[i][j-1]-pre[i-1][j-1]+s[i][j];
	ri=min(ri,pre[n][m]);
	insert(0,1);
	for(int i=0;i<n;i++)
		for(int j=i+1;j<=n;j++)
		{
			for(int k=1;k<=m;k++)
			{
				int tmp=pre[j][k]-pre[i][k];
				//cout<<i<<' '<<j<<' '<<k<<"		"<<tmp<<' '<<query(tmp-li)<<' '<<query(tmp-ri-1)<<endl;
				ans+=query(tmp-li)-query(tmp-ri-1);
				//if(tmp)	
				insert(pre[j][k]-pre[i][k],1);
			}
			//memset(tre,0,sizeof(tre));
			for(int k=1;k<=m;k++)
				if(pre[j][k]-pre[i][k]>=0)
					insert(pre[j][k]-pre[i][k],-1);
		}
	printf("%lld",ans);
	return 0;
}

正解

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Pass"<<endl
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){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 N=40,M=5e4+10,K=N*M;
int n,m,ans,li,ri,s[N][M],pre[N][M],l[M],r[M];
char ch[M];
signed main()
{
	scanf("%lld%lld",&n,&m);
	for(int i=1;i<=n;i++)
	{
		scanf("%s",ch+1);
		for(int j=1;j<=m;j++)
			s[i][j]=ch[j]-'0';
	}
	scanf("%lld%lld",&li,&ri);
	if(!li&&ri==m*n)
	{
		for(int i=1;i<=n;i++)
			for(int j=1;j<=m;j++)
				ans+=(n-i+1)*(m-j+1);
		printf("%lld",ans);
		return 0;
	}
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			pre[i][j]=pre[i-1][j]+pre[i][j-1]-pre[i-1][j-1]+s[i][j];
	ri=min(ri,pre[n][m]);
	for(int i=0;i<n;i++)
	{
		memset(l,0,sizeof(l));
		memset(r,0,sizeof(r));
		for(int j=0;j<m;j++)
		{
			for(int k=i+1;k<=n;k++)
			{
				int num=pre[k][j]-pre[i][j];
				while(l[k]<m&&pre[k][l[k]]-pre[i][l[k]]-num<li)	l[k]++;
				while(r[k]<m&&pre[k][r[k]+1]-pre[i][r[k]+1]-num<=ri)	r[k]++;
				if(l[k]==m&&pre[k][l[k]]-pre[i][l[k]]-num<li)	continue;
				ans+=r[k]-l[k]+1;
			}
		}
	}
	printf("%lld",ans);
	return 0;
}

T2 b

解题思路

显然是枚举 gcd 。

接下来对于重复的部分直接暴力容斥算回去。

问题转换为: 对于\(i \in [1,10^5]\)求多少种选择方案使得选的所有数均为 i 的倍数。

预处理出每一行中每一个数字的个数 记为 \(cnt_{i,j}\)

最后的答案就是 \(\prod\limits_{i=1}^{n}(cnt_{i,j}+1) -1\) 种方案。

毕竟要算上 0 的情况,去掉都是 0 的情况。

code

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Pass"<<endl
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){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 N=30,M=1e5+10,mod=1e9+7;
int n,m,ans,mx,s[N][M],cnt[N][M],all[M];
signed main()
{
	n=read();	m=read();
	for(int i=1,x;i<=n;i++)
		for(int j=1;j<=m;j++)
		{
			x=read();
			s[i][x]++;
			mx=max(mx,x);
		}
	for(int i=1;i<=n;i++)
		for(int j=1;j<=mx;j++)
			for(int k=1;k*j<=mx;k++)
				cnt[i][j]+=s[i][j*k];
	for(int i=mx;i>=1;i--)
	{
		bool flag=false;
		for(int j=1;j<=n;j++)
			flag|=(cnt[j][i]!=0);
		if(!flag)	goto V;
		all[i]=1;
		for(int j=1;j<=n;j++)
			all[i]=all[i]*(cnt[j][i]+1)%mod;
		all[i]=(all[i]-1+mod)%mod;
		for(int j=2;i*j<=mx;j++)
			all[i]=(all[i]-all[i*j]%mod+mod)%mod;
		V:;
	}
	for(int i=1;i<=mx;i++)
		ans=(ans+all[i]*i%mod)%mod;
	printf("%lld",ans);
	return 0;
}

T3 c

解题思路

点分治

首先可以发现其实这就是一棵树,每一条边可能会有多种颜色。

然后每一条边最多留下三种颜色就不会影响我们的答案(贪心,抽屉原理)。

在对于颜色去重之后离线点分治。

每层跑一遍,\(dp_{i,j,k}\):分治中心 focus 到 i 的路径中,第一条边的颜色为j,最后一条边颜色为 k 的最优解。

首先是点权下放,然后暴力枚举 focus,x,y 的四条边的眼色就好了。

code

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Pass"<<endl
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){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 N=1e5+10,M=3e5+10,Q=1e5+10;
int n,m,qus,focus,root,tot,ans[Q],siz[N],rt[N],w[N][5],cnt[N],f[N][5][5];
bool vis[N];
vector<pair<int,int> > v[N],q[N];
vector<pair<int,vector<int> > > e[N];
void pre_dfs(int x,int fat)
{
	sort(v[x].begin(),v[x].end());
	v[x].erase(unique(v[x].begin(),v[x].end()),v[x].end());
	for(int i=0,j;i<v[x].size();i=j)
	{
		int to=v[x][i].first;
		for(j=i+1;j<v[x].size();j++)
			if(v[x][j].first!=to)
				break;
		if(to==fat)	continue;
		e[x].push_back(make_pair(to,vector<int>()));
		e[to].push_back(make_pair(x,vector<int>()));
		for(int k=i;k<j&&k<i+3;k++)
		{
			e[x].back().second.push_back(v[x][k].second);
			e[to].back().second.push_back(v[x][k].second);
		}
		pre_dfs(to,x);
	}
}
void get_focus(int x,int all,int fat)
{
	siz[x]=1;
	int maxn=0;
	for(int i=0;i<e[x].size();i++)
	{
		int to=e[x][i].first;
		if(to==fat||vis[to])	continue;
		get_focus(to,all,x);
		maxn=max(maxn,siz[to]);
		siz[x]+=siz[to];
	}
	maxn=max(all-siz[x],maxn);
	if(maxn<tot)
		tot=maxn,focus=x;
}
void work(int x,int fat)
{
	siz[x]=1;
	for(int i=0;i<e[x].size();i++)
	{
		int to=e[x][i].first;
		if(to==fat||vis[to])	continue;
		for(int j=0;j<e[x][i].second.size();j++)
			w[to][j+1]=e[x][i].second[j];
		cnt[to]=e[x][i].second.size();
		for(int l=1;l<=cnt[root];l++)
			for(int j=1;j<=cnt[to];j++)
				for(int k=1;k<=cnt[x];k++)
				if(w[x][k]==w[to][j])	f[to][l][j]=max(f[to][l][j],f[x][l][k]);
				else	f[to][l][j]=max(f[to][l][j],f[x][l][k]+1);
		work(to,x);
		siz[x]+=siz[to];
	}
}
void query(int x,int fat)
{
	for(int i=0;i<q[x].size();i++)
	{
		int num=q[x][i].second,id=q[x][i].first;
		if(!rt[num]&&num!=focus)	continue;
		if(num==focus)
		{
			for(int j=1;j<=cnt[root];j++)
				for(int k=1;k<=cnt[x];k++)
					ans[id]=max(ans[id],f[x][j][k]);
			continue;
		}
		for(int j=1;j<=cnt[root];j++)
			for(int k=1;k<=cnt[rt[num]];k++)
				for(int a=1;a<=cnt[x];a++)
					for(int b=1;b<=cnt[num];b++)
						if(w[rt[num]][k]==w[root][j])	ans[id]=max(ans[id],f[x][j][a]+f[num][k][b]-1);
						else	ans[id]=max(ans[id],f[x][j][a]+f[num][k][b]);
	}
	for(int i=0;i<e[x].size();i++)
	{
		int to=e[x][i].first;
		if(to==fat||vis[to])	continue;
		query(to,x);
	}
}
void clear(int x,int fat)
{
	rt[x]=0;
	memset(f[x],0,sizeof(f[x]));
	for(int i=0;i<e[x].size();i++)
	{
		int to=e[x][i].first;
		if(to==fat||vis[to])	continue;
		clear(to,x);
	}
}
void mark(int x,int fat)
{
	rt[x]=root;
	for(int i=0;i<e[x].size();i++)
	{
		int to=e[x][i].first;
		if(vis[to]||to==fat)	continue;
		mark(to,x);
	}
}
void solve(int x,int all)
{
	tot=all;
	get_focus(x,all,0);
	clear(focus,0);
	vis[focus]=true;
	int tmp=focus;
	for(int i=0;i<e[tmp].size();i++)
	{
		int to=e[tmp][i].first;
		if(vis[to])	continue;
		cnt[to]=e[tmp][i].second.size();
		for(int j=0;j<e[tmp][i].second.size();j++)
			w[to][j+1]=e[tmp][i].second[j],f[to][j+1][j+1]=1;
		root=to;
		work(to,tmp);
		query(to,tmp);
		mark(to,tmp);
	}
	clear(focus,0);
	for(int i=0;i<e[tmp].size();i++)
	{
		int to=e[tmp][i].first;
		if(vis[to])	continue;
		solve(to,siz[to]);
	}
}
signed main()
{
	n=read();	m=read();
	for(int i=1,x,y,val;i<=m;i++)
	{
		x=read();	y=read();	val=read();
		v[x].push_back(make_pair(y,val));
		v[y].push_back(make_pair(x,val));
	}
	pre_dfs(1,0);
	qus=read();
	for(int i=1,x,y;i<=qus;i++)
	{
		x=read();	y=read();
		q[x].push_back(make_pair(i,y));
		q[y].push_back(make_pair(i,x));
	}
	solve(1,n);
	for(int i=1;i<=qus;i++)
		printf("%lld\n",ans[i]);
	return 0;
}
posted @ 2021-08-17 21:30  Varuxn  阅读(54)  评论(0编辑  收藏  举报