abc 从0开始的刷题记录:记录每场abc

目前情况:42-49

-AtCoder Beginner Contest 042 - AtCoder

 分数查看 网站 atcoder proble

1.C:

  对于这道题 起始我刚开始还是想这么贪最合适 但想来想去没有找到合理的做法 于是我就决定直接暴力找 因为我们对每一个数的判断均不超过len次

  

#include
#include
#include
using namespace std;
int n, k, dat[10];
int main()
{
	memset(dat, false, sizeof dat);
	cin >> n >> k;
	for (int i = 0, x; i < k; i++)
		cin>>x, dat[x] = true;
	for (int i = n; i < 100000; i++)
	{
		bool ok = true;
		int num = i;
		while (num && ok)
		{
			if (dat[num % 10])
				ok = false;
			num /= 10;
		}
		if (ok)
		{
			cout << i;
			return 0;
		}
	}
}

D:这道题我想了比较久 :因为这很明显是一个组合数求解 :n,m比较大 像这样的走法可以看为组合数 对于像这样的切除一部分后来进行操作 我们可以将切除后的[n-a+1,b+1]-[n,m]

重新跑一次组合数(起始我最初是像看看有什么差分的关系在相邻列中 然后利用线段树来维护)当并没有 开始我们打标后发现:每一个均是一个新的组合数

abc 从0开始的刷题记录:记录每场abc_第1张图片

对于b c  d ...的来说均相同 

#include
#include
#include
#include
using namespace std;
#define int long long
const int maxn = 2e5 + 10;
int fact[maxn], invfact[maxn];
const int mod = 1e9 + 7;
int qpow(int a, int b)
{
	int res = 1;
	while (b)
	{
		if (b & 1)
		{
			res = res * a%mod;
		}
		b >>= 1;
		a = a * a%mod;
	}
	return res;
}
void init() {
	fact[0] = invfact[0] = 1;
	for (int i = 1; i < maxn; i++)
		fact[i] = fact[i - 1] * i%mod;
	invfact[maxn - 1] = qpow(fact[maxn - 1], mod - 2);
	for (int i = maxn - 2; i; i--)
		invfact[i] = invfact[i + 1] * (i + 1) % mod;
}
int q_mul(int a, int b) {
	a %= mod; b %= mod;
	int ans = 0;
	while (b) {
		if (b & 1)
			ans = (ans + a) % mod;
		a = (a + a) % mod;
		b >>= 1;
	}
	return ans % mod;
}
int C(int a, int b) {
	if (a < b)return 0;
	return q_mul(fact[a], q_mul(invfact[b], invfact[a - b])) % mod;
}
int now[maxn];
int ans = 0;
signed main()
{
	init();
	int n, m, a, b;
	cin >> n >> m >> a >> b;
	for (int i = 1; i <= m - b; i++)
	{
		now[i] = C(n - a + b + i - 2, n -a-1);
	}
	for (int i = 1; i <= m - b;i++)
	{
		ans += now[i] * C(a + m - b - i - 1, a - 1)%mod;
		ans %= mod;
	}
	cout << ans;
}

AtCoder Beginner Contest 043 - AtCoder

同样从C开始:

对于C题 起始可以利用假设变化完后的是x来进行化简式子然后进行查找最小值 不过我第一眼想到的是直接dp

dp[i][j]代表前i个数变为j的最小代价 由于j可以为负数 我们要利用偏移 就是对所有数+100 然后转移 

我们转移的方程很简单

#include
#include
using namespace std;
#define int long long
const int maxn = 3e5 + 10;
int dp[110][210];
int a[110];
signed main()
{
	int n;
	cin >> n;
	for (int i = 1; i <= n; i++)
		cin >> a[i];
	for (int i = 1; i <= n; i++)
	{
		for (int j = 0; j <= 200; j++)
		{
			dp[i][j] = dp[i - 1][j] + (abs((j - 100-a[i]))*abs((j - 100 - a[i])));
		}
	}
	int ans = 1e9;
	for (int i = 0; i <= 200; i++)
		ans = min(ans, dp[n][i]);
	cout << ans;
}

D:其实这道题并不难 主要是抓住关键点 对于许多问题我们均可以将其转化为许多很小的问题来处理 就像这道题一样 我们可以发现 对于一个长度>=2 且有一个数出现至少一个的话 我们可以将其转化为长度为2 3 的问题 2:两个相同的数 3:隔一位相同的数 这样的化我们久可以非常简单找到答案了 

#include
#include
#include
using namespace std;
#define int long long
const int maxn = 3e5 + 10;
string s;
signed main()
{
	cin >> s;
	int len = s.length();
	for (int i = 0; i < len; i++)
	{
		if (i + 1 < len&&s[i] == s[i + 1])
		{
			cout << i + 1 << ' ' << i + 2;
			return 0;
		}
		else if (i + 2 < len&&s[i] == s[i + 2])
		{
			cout << i + 1 << ' ' << i + 3;
			return 0;
		}

	}
	cout << -1 << ' ' << -1;
}

044:

1.C:

 这道题还是有很明显的dp性

我们此时dp[i][j][k]代表前i项 选择j项 sum=k的方案数 接下来就是转移和统计了 转移和统计比较简单久不多说了

(其实我之前是相利用 k表示%a 的余数 )但很明显这样做不行 因为假如 假设长度为 len sum=(len-1)*a 的话 就会计错答案

#include
#include
using namespace std;
#define int long long
const int maxn = 55;
int dp[maxn][maxn][maxn*maxn];//dp[i][j][k]代表前i位选取j为 sumZ%A=k的方案数
int n, a;
int x[maxn];
signed main()
{
	cin >> n >> a;
	dp[0][0][0] = 1;
	for (int i = 0; i < n; i++)cin >> x[i];
	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j <= n; j++)
		{
			for (int k = 0; k <= 2500; k++)
			{
				if (j != n && k + x[i] <= 2500)
					dp[i + 1][j + 1][k + x[i]] += dp[i][j][k];
				dp[i + 1][j][k] += dp[i][j][k];
			}
		}
	}
	int ans = 0;
	for (int i = 1; i <= n; i++)
		ans += dp[n][i][i*a];
	cout << ans;

}

D: 这道题说实话我写不出来的

首先这道题要具备一下几点知识:

类似10^11 这样大的数据范围的话 我们只能想是否和sqrt有关系 而这样的关系一般是有 因数要求在里面

我们对这道题进行分析:
假设 此时b满足条件:
  a0*b^x+a1*b^(x-1)+.....=n
  a0+a1+.....=s;
  此时我们将两者相减可以发现: a0*(b^x-1)+a1*(b^(x-1)-1).....=n-s
  此时左边式子为 b-1的倍数 所以b-1|n-s
  所以我们可以此时利用n-s 的因数来判断此时b是否满足
  这道题的关键切入点在于此时的数据范围:10^11 这个范围一般和根号有关系 而于sqrt 有关的又有因数


#include
#include
#include
#include
using namespace std;
#define int long long
int n, s;
vectorv;
bool check(int x)
{
	int now = n;
	int ans = 0;
	while (now)
	{
		ans += now % x;
		now /= x;
	}
	if (ans == s)
		return 1;
	else
		return 0;
}
signed main()
{
	cin >> n >> s;
	if (n - s < 0)
	{
		cout << -1;
		return 0;
	}
	else if (n == s)
	{
		cout << n + 1;
		return 0;
	}
	int now = sqrt(n - s);
	for (int i = 1; i <= now; i++)
	{
		if ((n - s) % i == 0)
		{
			if (i*i == n - s)
			{
				v.push_back(i);
			}
			else
			{
				v.push_back(i);
				v.push_back((n - s) / i);
			}
		}
	}
	sort(v.begin(), v.end());
	for (auto x : v)
	{
		if (check(x + 1))
		{
			cout << x + 1;
			return 0;
		}
	}
	cout << -1;
}

AtCoder Beginner Contest 045 - AtCoder

C: 这道题其实很明显可以保留

#include
#include
#include
#include
using namespace std;
#define int long long
string s;
int a[110];
int ans,n;
void dfs(int id,int sum,int num)
{
	if (id == n + 1)
	{
		ans += (num + sum);
		return;
	}
	num = num * 10 + a[id];
	dfs(id+1, sum, num);
	dfs(id+1, sum + num, 0);
}
signed main()
{
	cin >> s;
	 n = s.length();
	for (int i = 0; i < n; i++)
	{
		a[i + 1] = s[i] - '0';
	}
	dfs(1, 0, 0);
	cout << ans/2;
}

D:

 这道题的h w明显不可能遍历 所以我就将目光放到了 k身上 我们尝试维护一下k的贡献 对于一个位置 他可以产生贡献的地方在于包含他的3*3矩形 当这样的矩形有较多且不容易询问 于是我就利用每个矩形的右下角代表这个矩形 对于一个点 我们对包含他的所有矩形的右下角代价+1

所以此时就比较好搞定了

#include
#include
#include
#include
#include
using namespace std;
#define int long long
string s;
int a[110];
int n, m;
int ans[10];
map, int>mp;
vector>pii;
signed main()
{
	cin >> n >> m;
	int k;
	cin >> k;
	for (int i = 1; i <= k; i++)
	{
		int idx, idy;
		cin >> idx >> idy;
		if (idx >= 3 && idy >= 3)
		{
			if (mp.count({ idx,idy }) == 0)
				pii.push_back({ idx,idy });
			mp[{idx, idy}]++;
		}
		if (idx >= 3 && idy + 1 >= 3&&idy+1<=m)
		{
			if (mp.count({idx,idy+1})== 0)
				pii.push_back({ idx,idy+1});
			mp[{idx, idy+1}]++;
		}
		if (idx >= 3 && idy +2>= 3 && idy+2<=m)
		{
			if (mp.count({ idx,idy + 2 }) == 0)
				pii.push_back({ idx,idy+2 });
			mp[{idx, idy+2}]++;
		}
		if (idx+1>= 3&& idy >= 3&&idx+1<=n)
		{
			if (mp.count({ idx+1,idy }) == 0)
				pii.push_back({ idx+1,idy });
			mp[{idx+1, idy}]++;
		}
		if (idx+1>= 3 && idy + 1>= 3 && idy + 1 <= m&&idx+1<=n)
		{
			if (mp.count({ idx+1,idy + 1 }) == 0)
				pii.push_back({ idx+1,idy + 1 });
			mp[{idx+1, idy + 1}]++;
		}
		if (idx+1>= 3 && idy + 2 >= 3 && idy + 2 <= m&&idx+1<=n)
		{
			if (mp.count({ idx+1,idy + 2 }) == 0)
				pii.push_back({ idx+1,idy + 2 });
			mp[{idx+1, idy + 2}]++;
		}
		if (idx+2>= 3 && idy >= 3&&idx+2<=n)
		{
			if (mp.count({ idx+2,idy }) == 0)
				pii.push_back({ idx+2,idy });
			mp[{idx+2, idy}]++;
		}
		if (idx+2>= 3 && idy + 1>= 3 && idy + 1 <= m&&idx+2<=n)
		{
			if (mp.count({ idx+2,idy + 1 }) == 0)
				pii.push_back({ idx+2,idy + 1 });
			mp[{idx+2,idy + 1}]++;
		}
		if (idx+2>= 3 && idy + 2 >= 3 && idy + 2 <= m&&idx+2<=n)
		{
			if (mp.count({ idx+2,idy + 2 }) == 0)
				pii.push_back({ idx+2,idy + 2 });
			mp[{idx+2, idy + 2}]++;
		}
	}
	int sum = (n - 2)*(m - 2);
	for (auto x : pii)
	{
		a[mp[{x.first, x.second}]]++;
	}
	for (int i = 1; i <= 9; i++)
		sum -= a[i];
	cout << sum <

AtCoder Beginner Contest 046 - AtCoder

C:

这道题本来我是想二分 但结果这么二分都不对也不知道为啥不对 

正解就是一个模拟而已:我们考虑将 1号维护到大于此时T的最小值 2号维护到大于a的最小值:

  1. u = e[i].t*((u + e[i].t - 1) / e[i].t);
  2. v = e[i].a*((v + e[i].a - 1) / e[i].a);

此时假如两者的比值于规定不同的话:

  1. if (u / e[i].t < v / e[i].a)u = v / e[i].a * e[i].t;
  2. else
  3. v = u / e[i].t*e[i].a;

假如u小于比值规定的话 就将u以v的基础扩大

假如v小的话同理

#include
#include
#include
using namespace std;
#define int long long
const int maxn = 1e5 + 10;
int n;
struct node
{
	int t, a;
}e[maxn];
signed main()
{
	cin >> n;
	for (int i = 1; i <= n; i++)
	{
		cin >> e[i].t >> e[i].a;
	}
	int u = e[1].t, v = e[1].a;
	for (int i = 2; i <= n; i++)
	{
		u = e[i].t*((u + e[i].t - 1) / e[i].t);
		v = e[i].a*((v + e[i].a - 1) / e[i].a);
		if (u / e[i].t < v / e[i].a)u = v / e[i].a * e[i].t;
		else
			v = u / e[i].t*e[i].a;
	}
	cout << u + v;
}

AtCoder Beginner Contest 047 - AtCoder

1.c:

这道题我们通过题目可以得知 我们将所有连续相同的划分为一段 可以观察到:假设要将该段变为和前一段相同的话 我们必须在最后面放一个和前一段相同的颜色这样会使得我们此时最后一段扩展 变为更长的一段 按这样模拟的话 答案就是任意两个相邻不同的颜块都有1的贡献

#include
#include
#include
using namespace std;
#define int long long
string s;
signed main()
{
	cin >> s;
	int ans = 0;
	for (int i = 1; i < s.length(); i++)
	{
		if (s[i] != s[i - 1])
			ans++;
	}
	cout << ans;
}

D:

就单纯模拟就好 至于正确性的话 对于这道题的话 拳头一定比布劣 我们假设刚开始均为拳头的话 此时我们将拳头变为布的话:1 假如此时对手为拳头 我们可以ans+1 2 假如我们此时对手为 布的话 我们此时ans+1 所以此时我们只要模拟一下可以用布就用就好了 比较不管怎么样都ans+1

#include
#include
#include
#include
using namespace std;
#define int long long
const int maxn = 1e5 + 10;
string s;
int cnt1, cnt2;
signed main()
{
	cin >> s;
	int ans = 0;
	for (int i = 0; i < s.length(); i++)
	{
		if (s[i] == 'g')
		{
			if (cnt1 > cnt2)
			{
				ans++;
				cnt2++;
			}
			else
			{
				cnt1++;
			}
		}
		else
		{
			if (cnt1 > cnt2)
			{
				cnt2++;
			}
			else
			{
				cnt1++;
				ans--;
			}
		}
		
	}
	cout << ans;
}

48:

C:很明显的贪心 我们考虑如何处理才能达到最优效果:我们从1号开始 :从左往右,尽可能将右边减小:因为这样可以影响到下一位

#include 
#include 
using namespace std;
#define int long long
int n, x, ans, a, b, c;
signed main() {
	cin >> n >> x;
	for (int i = 1; i <= n; i++) {
		cin >> a;
		c = a + b - x;
		if (c > 0) a -= c, ans += c;
		b = a;
	}
	cout << ans;
}

 D:由于左右端点无法消除 这是我们做这道题的关键点:我们考虑必败点:

abababab这类情况

由于最后首尾一定保留 所以我们考虑首尾的相同性:

1、假如==的话 可以确定 最后结果长度一定为奇数 那么当此时长度为奇数的话先手删掉任意一个点 长度变为偶数 保证一定不是必败点 那么此时必败点这可能在后手操作后产生 所以此时后手必胜

当长度为偶数的时候情况相反

2.不等分析与上述类似

#include 
#include 
#include
using namespace std;
#define int long long
string s;
signed main() {
	cin >> s;
	int n = s.length();
	if (s[0] == s[n - 1])
	{
		if (n % 2)
		{
			cout << "Second";
		}
		else
		{
			cout << "First";
		}
	}
	else
	{
		if (n % 2==0)
		{
			cout << "Second";
		}
		else
		{
			cout << "First";
		}
	}
}

49:

C:简单的模拟题:我们考虑2种写法:1.由于此时有前缀相同的字符串 我们需要考虑如何才能分别将其匹配:

1、dp[i]代表前i个字符能否匹配:转移的话很好想

2.我们考虑既然此时前缀是相同的但假如此时我们将其翻转后前缀就变为独一无二的了 此时我们就可以一一匹配了

#include 
#include 
#include 
using namespace std;
string divide[4] = { "dream", "dreamer", "erase", "eraser" };
int main() {
	string S;
	cin >> S;
	reverse(S.begin(), S.end());

	for (int i = 0; i < 4; i++) reverse(divide[i].begin(), divide[i].end());
	bool can = 1;

	for (int i = 0; i < S.length();) {
		bool can2 = 0;
		for (int j = 0; j < 4; j++) {
			string d = divide[j];
			if (S.substr(i, d.size()) == d) {
				can2 = 1;
				i += d.size();
				break;
			}
		}
		if (!can2) {
			can = false;
			break;
		}
	}
	if (can) cout << "YES" << endl;
	else cout << "NO" << endl;
}

D:

对于这道题我们可以发现:当两个点分别通过两种方式形成的最小生成树分别相同的话 这两个点可以互相到达 所以此时我们维护一下两个点的最小生成树的祖先和就好了  

你可能感兴趣的:(atc,c++,算法)