Educational Codeforces Round 115 (Div. 2)

本文通过五个实例展示了不同类型的算法问题解决方案,包括基于移动条件的路径判断、会议安排、统计计算、二维平面上点的排列以及矩阵中楼梯计数。涉及到了暴力枚举、模拟统计、动态规划等思维和计数技巧,揭示了在解决信息技术问题时的多种策略。

A.思维

可以搜索

但是根据题目所给的移动条件,我们可以推断

只要从第一列到最后一列之间不存在同一列两行都为陷阱的情况

我们就一定可以到达

#include<bits/stdc++.h>
using namespace std;
const int maxn = 110;
char g[110][3];
int main()
{
	ios::sync_with_stdio(0);
	int T;cin>>T;
	while (T--)
	{
		int n;cin>>n;
		for (int i=1;i<=n;++i)cin>>g[i][1];
		for (int i=1;i<=n;++i)cin>>g[i][2];
		string s = "YES\n";
		for (int i=1;i<=n;++i)if (g[i][1]==g[i][2]&&g[i][1]=='1')
		{
			s = "NO\n";
			break;
		}
		cout<<s;
	}
}

B.暴力

我们可以暴力枚举在第一、二两组在哪两天开会

day1,day2day_1,day_2day1,day2

然后,判断nnn个人能否均分为两组

  • 如果有人day1,day2day_1,day_2day1,day2都没空,显然不可以
  • 只在day1day_1day1有空的人和只在day2day_2day2有空的人不能超过n2\frac n22n
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1100;
int can[maxn][5];
int n;
void solve()
{
	for (int day1 = 0;day1<5;++day1)
		for (int day2 = day1+1;day2<5;++day2)
		{
			int cnt1 = 0,cnt2 = 0,cnt3 = 0;
			for (int i=1;i<=n;++i)
			{
				if (can[i][day1]&&can[i][day2])++cnt3;
				else if (can[i][day1])++cnt1;
				else if (can[i][day2])++cnt2;
			}
			if (cnt1+cnt2+cnt3!=n)continue;
			if (cnt1*2<=n&&cnt2*2<=n)
			{
				cout<<"YES\n";
				return;
			}
		}
	cout<<"NO\n";
}
int main()
{
	ios::sync_with_stdio(0);
	int T;cin>>T;
	while (T--)
	{
		cin>>n;
		for (int i=1;i<=n;++i)
			for (int j=0;j<5;++j)cin>>can[i][j];
		solve();
	}
}

C.模拟

统计就好了

如果我们已经知道了平均数aveaveave

那么我们可以枚举所有的数aia_iai,根据平均数计算出aja_jaj值,加上aja_jaj出现的个数即可

#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5+100;
typedef long long ll;
int a[maxn];
int n;
map<int,int> mp;
int main()
{
	ios::sync_with_stdio(0);
	int T;cin>>T;
	while (T--)
	{
		cin>>n;ll sum = 0;mp.clear();
		for (int i=1;i<=n;++i)cin>>a[i];
		for (int i=1;i<=n;++i)sum += a[i];
		if (sum*2LL%n!=0)
		{
			cout<<"0\n";
			continue;
		}sum = sum*2LL/n;
		ll ans = 0;
		for (int i=1;i<=n;++i)
		{
			if (mp.count(sum-a[i]))ans+=mp[sum-a[i]];
			mp[a[i]]++;
		}cout<<ans<<"\n";
	}
}

D.思维+计数

数据量只支持O(n)O(n)O(n)或者O(nlogn)O(nlogn)O(nlogn)的解法

我们观察题目所给的特殊的限制条件!

  1. 不存在一个点对(i,j)(i,j)(i,j)使得ai==aja_i==a_jai==ajbi==bjb_i==b_jbi==bj
  2. 满足条件的要求是ai≠aj≠ak∣∣bi≠bj≠bka_i\not=a_j\not=a_k||b_i\not=b_j\not=b_kai=aj=akbi=bj=bk

首先我们根据第一个条件,可以把所有点投射到一个2e5∗2e52e5*2e52e52e5的矩阵上

行坐标为aia_iai,纵坐标为bib_ibi

因为条件一,所以每一个坐标点上最多只有一个点

再根据条件二,思考一会发现直接求解是困难的

考虑容斥

什么情况下,不满足条件二呢?

选取的三个点中,至少有两个点在同一行,且至少有两个在同一列

投射到矩阵上就是下面四种情况:




我们计算每一行,每一列有多少个点,然后枚举每一个点作为中间节点,计算有多少种情况

#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5+100;
typedef long long ll;
int row[maxn],col[maxn];
int a[maxn],b[maxn];
int n;
int main()
{
	ios::sync_with_stdio(0);
	int T;cin>>T;
	while (T--)
	{
		cin>>n;
		for (int i=1;i<=n;++i)
		{
			cin>>a[i]>>b[i];
			row[a[i]]++;
			col[b[i]]++;
		}
		ll ans = 1LL*n*(n-1)*(n-2)/6LL;
		for (int i=1;i<=n;++i)
			ans -= 1LL*(row[a[i]]-1)*(col[b[i]]-1);
		cout<<ans<<"\n";
		for (int i=1;i<=n;++i)
		{
			--row[a[i]];
			--col[b[i]];
		}
	}
}

E.dpdpdp+思维

如果没有翻转的操作,单纯询问n×mn\times mn×m的矩阵中有多少个楼梯

我们利用dpdpdp计算

dp[0][i][j],dp[1][i][j]:dp[0][i][j],dp[1][i][j]:dp[0][i][j],dp[1][i][j]:分别是以(i,j)(i,j)(i,j)为结尾的两种不同的楼梯个数

并且我们把只取自身(i,j)(i,j)(i,j)既看做成第一种也看做成第二种

dp[0][i][j]=dp[1][i−1][j]+1dp[0][i][j]=dp[1][i-1][j]+1dp[0][i][j]=dp[1][i1][j]+1

dp[1][i][j]=dp[0][i][j−1]+1dp[1][i][j]=dp[0][i][j-1]+1dp[1][i][j]=dp[0][i][j1]+1

最终统计以每一点为结尾的楼梯的个数

−n×m+∑(i,j)dp[0][i][j]+dp[1][i][j]-n\times m+\sum_{(i,j)}dp[0][i][j]+dp[1][i][j]n×m+(i,j)dp[0][i][j]+dp[1][i][j]

利用cntcntcnt记录矩阵中freefreefree状态的点,初始free=n×mfree=n\times mfree=n×m

当我们改动点(x,y)(x,y)(x,y)时,dp[1][x][y],dp[0][x][y],cntdp[1][x][y],dp[0][x][y],cntdp[1][x][y],dp[0][x][y],cnt均发生改变

dp[0][x][y]dp[0][x][y]dp[0][x][y]改变时,dp[1][i][j+1]dp[1][i][j+1]dp[1][i][j+1]也改变

dp[1][x][y]dp[1][x][y]dp[1][x][y]改变时,dp[0][i+1][j]dp[0][i+1][j]dp[0][i+1][j]也改变

如此,我们一路更新dpdpdp的状态,会发现,至多更新O(n)O(n)O(n)

因此,暴力更新dpdpdp状态,维护dpdpdp数组的和与cntcntcnt

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1100;
typedef long long ll;
ll dp[2][maxn][maxn];
int g[maxn][maxn];
int n,m,q;
ll ans = 0;
void dfs(int x,int y,int sta)
{
	if (x>n||y>m)return;
	ans-=dp[sta][x][y];
	if (g[x][y]==1)dp[sta][x][y]=0;
	else if (sta==0)dp[sta][x][y] = dp[sta^1][x-1][y]+1;
	else dp[sta][x][y] = dp[sta^1][x][y-1]+1;
	ans+=dp[sta][x][y];
	if (sta==0)dfs(x,y+1,sta^1);
	else dfs(x+1,y,sta^1);
}
int main()
{
	ios::sync_with_stdio(0);
	cin>>n>>m>>q;
	for (int i=1;i<=n;++i)
	{
		for (int j=1;j<=m;++j)
		{
			dp[0][i][j]=dp[1][i-1][j]+1;
			dp[1][i][j]=dp[0][i][j-1]+1;
			ans+=dp[0][i][j]+dp[1][i][j];
		}
	}
	int cnt = n*m;
	while (q--)
	{
		int x,y;cin>>x>>y;
		if (g[x][y])++cnt;
		else --cnt;
		g[x][y]^=1;
		dfs(x,y,0);
		dfs(x,y,1);
		cout<<ans-cnt<<"\n";
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值