多校A层冲刺NOIP2024模拟赛03

A. 五彩斑斓

没办法,不会统计四个点相同的,赛时没想到,写了一个神秘算法骗了80

考虑倒着计算,总子矩阵有 \(\frac{n(n+1)*m(m+1)}{4}\) 个,减去四个角相同的矩阵数量就是答案,枚举矩阵的上下边界两条线

再枚举每一列,会有两个交点,统计每种颜色的上下交点颜色一样的个数,就可以计算了

点击查看代码
#include<bits/stdc++.h>
const int maxn=410;
using namespace std;
int n,m,c[maxn][maxn],cnt[1000001];
long long ans; 

int main()
{
	freopen("colorful.in","r",stdin);
	freopen("colorful.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n>>m; 
	for(register int i=1;i<=n;i++)
	{
		for(register int j=1;j<=m;j++)
		{
			cin>>c[i][j];
		}
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=i;j<=n;j++)
		{
			for(int k=1;k<=m;k++)
			{
				if(c[i][k]==c[j][k])
				{
					cnt[c[i][k]]++;
					ans+=cnt[c[i][k]];
				}
			}
			for(int k=1;k<=m;k++) cnt[c[i][k]]=0; 
		}
	} 
	cout<<1ll*(n+1)*n/2*m*(m+1)/2-ans;

	return 0;
}
/*
3 4
1 2 3 1
1 3 1 2
1 2 1 1
 
*/

B. 错峰旅行

确实没想到怎么维护,直接找的每一天有多少城市可走,正解实际上就是再简化一下,因为区间很少,所以有很多天他们

的城市拥堵情况是一样的,这样就可以直接合并,这样就去除了很多重复的情况,因为不拥堵时城市数是 \(n\) ,所以直接

对每一个限制的 \(l,r\) 记一个在 \(l\) 处 -1,在 \(r+1\) 处 +1,把每一个有限制的时间点记录下来,按时间先后跑一边就行

点击查看代码
#include<bits/stdc++.h>
#define int long long 
const int mod=1e9+7;
const int maxn=2e6+10;
using namespace std;
int n,m,s,t,p[maxn],ans,cnt;
struct lsx
{
	int id,x,k;
	bool operator < (const lsx &a) const
	{
		return x==a.x?k<a.k:x<a.x; 
	}
}a[maxn<<1];

int qpow(int x,int y)
{
	int ans=1;
	while(y)
	{
		if(y&1) ans=ans*x%mod;
		x=x*x%mod;
		y>>=1;
	}
	return ans;
}

signed main()
{
	freopen("travel.in","r",stdin);
	freopen("travel.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n>>m>>s>>t;
	p[++p[0]]=s,p[++p[0]]=t+1;
	for(int i=1;i<=m;i++)
	{
		int x,l,r;
		cin>>x>>l>>r;
		p[++p[0]]=l,p[++p[0]]=r+1;
		a[++cnt]={x,l,-1};
		a[++cnt]={x,r+1,1};
	}
	sort(a+1,a+1+cnt);
	sort(p+1,p+1+p[0]);
	p[0]=unique(p+1,p+p[0]+1)-p-1;
	int num=n;
	ans=1; 
	for(int i=1,j=0;i<p[0];i++)
	{
		while(j<cnt&&a[j+1].x<=p[i])
		{
//			cout<<a[j].x<<" "<<a[j].k<<endl; 
			j++;
			int x=a[j].k;
			num+=x;	
		}
//		cout<<i<<" "<<p[0]<<endl; 
//		cout<<p[i]<<" "<<ans<<" "<<num<<endl; 
		ans=ans*qpow(num,p[i+1]-p[i])%mod;
	}
	cout<<ans;

	return 0;
}
/*

*/

C. 线段树

没想到是 \(dp\) 啊,由于一个区间至少需要一次,所以答案的下届是 \(q\) ,我们考虑对一个区间 \(l-r\) 查询次数增加需要达到

的条件,即为你划分出来的区间 \(x-y\) 满足与 \(l-r\) 有交集,但是不包含,这样当划分完之后,\(l-r\) 有一部分在区间外

一部分在区间内,它就需要进入子树内从而至少多花费1的代价

区间 \(dp\) ,设 \(f_{l,r}\) 表示将 \(l-r\) 划分区间对询问造成的最小代价,\(f_{l,r}=\min_{k=l}^{r-1}\limits(f_{l,k}+f_{k+1,r}+cost(l,r,k))\)

\(cost(l,r,k)\) 表示有多少询问区间与 \(l-r\) 有交且包含 \(k+0.5\)

点击查看代码
#include<bits/stdc++.h>
const int maxn=1e5+10;
using namespace std;
int n,q,x[maxn],y[maxn],w[501],a[501][501],ans,k,f[501][501],sum[501][501]; 

int main()
{
	freopen("segment.in","r",stdin);
	freopen("segment.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n>>q;
	for(int i=1;i<=q;i++)
	{
		cin>>x[i]>>y[i];
		a[x[i]][y[i]]++;
		for(int j=x[i];j<y[i];j++) w[j]++;
	}
	for(int l=1;l<=n;l++)
		for(int r=n;r>=l;r--) 
			sum[l][r]+=sum[l-1][r]+sum[l][r+1]-sum[l-1][r+1]+a[l][r];
	for(int len=2;len<=n;len++)
	{
		for(int i=1;i+len-1<=n;i++)
		{
			int j=i+len-1;
			f[i][j]=0x7f7f7f;
			for(int k=i;k<j;k++)
			{
				f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+w[k]-sum[i][j]);
//				cout<<w[k]-sum[i][j]<<endl; 
			}
//			cout<<i<<" "<<j<<" "<<f[i][j]<<endl; 
		}
	}
	cout<<f[1][n]+q;
	
	return 0;
}
/*

*/

你们怎么都往博客塞点私货啊。。。

我们终会在星辰里相遇

image

posted @ 2024-10-07 21:26  _君の名は  阅读(25)  评论(1编辑  收藏  举报