多校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;
}
/*
*/
你们怎么都往博客塞点私货啊。。。