Codeforces Round #736 (Div. 2)A~E
A. Gregor and Cryptography
题意:给出P (5~1e9) ,找到2\(\leq\)a\(<\)b\(\leq\)p,使得p mod a = p mod b
分析:显然固定a为2, p为奇数,b为p-1,p为偶数,b为p,而p \(\geq\) 5,b-1$>$2,故满足题意
代码:
#include <cstring>
#include <iostream>
#include <algorithm>
#include <map>
#define x first
#define y second
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
const int N = 2e5 + 10, mod = 1e9 + 7;
int n, m, t, k;
int s[N], g[N], f[N];
void solve()
{
cin >> n;
if (n % 2)
{
printf("2 %d\n", n - 1);
}
else
{
printf("2 %d\n", n);
}
}
int main()
{
t = 1;
cin >> t;
while (t--)
solve();
return 0;
}
B. Gregor and the Pawn Game
题意:国际象棋规则,小兵只可以直走或者吃斜对角的敌方小兵,给出对方棋子排布和自己棋子排布,敌方棋子不动,问自己方棋子有多少可以到达最后一层
分析:分析可得,每一列小兵只与当前列,左一列,右一列有关,而右一列的状态不与前一列相关,可以用两个变量进行实时维护前一列和当前列的状态,模拟即可
代码:
#include <cstring>
#include <iostream>
#include <algorithm>
#include <map>
#define x first
#define y second
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
const int N = 2e5 + 10, mod = 1e9 + 7;
int n, m, t, k;
int s[N], g[N], f[N];
string a, b;
void solve()
{
cin >> n;
cin >> a >> b;
int c = 0, d = 0;
int cnt = 0;
if (b[0] == '1')
{
if (a[0] == '0' && a[1] == '1')
{
cnt++;
c = 0, d = 0;
}
else if (a[0] == '0')
{
cnt++;
c = 1, d = 0;
}
else if (a[1] == '1')
{
cnt++;
c = 0, d = 1;
}
}
for (int i = 1; i < n; i++)
{
if (b[i] == '1')
{
if (a[i-1] == '1' && c == 0)
{
c = d, d = 0;
cnt++;
continue;
}
else if (a[i] == '0' && d == 0)
{
c = 1, d = 0;
cnt++;
continue;
}
else if (i + 1 < n && a[i+1] == '1')
{
cnt++;
c = d, d = 1;
continue;
}
}
else c = d, d = 0;
}
cout << cnt << endl;
}
int main()
{
t = 1;
cin >> t;
while (t--)
solve();
return 0;
}
C. Web of Lies
题意:n个数,m条边,三种操作,添加边与边(保证之前不存在),删除边与边(保证之前存在),查询有多少个节点存在,查询规则是对于有边相连的连通块,删除当前连通块权值最小的点及其所有的连边,点i的权值为i,多次操作知道没有边(查询结束状态回归查询前)
分析:对于每条边的存在,权值小的点会被全职大的点杀死一次,每次统计点的死亡次数,最初无边相连ans = n,初始化生命值均为1,当生命从1到0意为死去ans--,当生命从0到1意为复活,ans++。
代码:
#include <cstring>
#include <iostream>
#include <algorithm>
#include <map>
#define x first
#define y second
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
const int N = 2e5 + 10, mod = 1e9 + 7;
int n, m, t, k;
int s[N], g[N], f[N];
void solve()
{
int a, b, c;
cin >> n >> m;
int ans = n;
for (int i = 1; i <= n; i++) s[i] = 1;
for (int i = 1; i <= m; i++)
{
cin >> a >> b;
if (a > b) swap(a, b);
s[a]--;
if (s[a] == 0) ans--;
}
cin >> k;
while (k--)
{
cin >> a;
if (a == 3) printf("%d\n", ans);
else
{
cin >> b >> c;
if (b > c) swap(b, c);
if (a == 1)
{
s[b]--;
if (s[b] == 0) ans--;
}
else
{
s[b]++;
if (s[b] == 1) ans++;
}
}
}
}
int main()
{
t = 1;
// cin >> t;
while (t--)
solve();
return 0;
}
D. Integers Have Friends
题意:找到连续的多个元素,使他们的gcd不为1,问最大长度为多少,gcd可以通过差值转移,原理类似gcd的实现,而转换差分之后gcd有了连续性
分析:两种方法:一种通过刷st表,知道第i个元素往后2^j个位置的连续gcd,从而对每个位置枚举,二分查找答案;
第二种是 每一个位置的连续gcd都可以看做前面连续gcd的答案gcd该位置,而通过繁琐的证明(整不出来qwq),可以得知每个元素前的不重复的gcd很少,(int范围内不超过1600,ll范围不知道)反正很少就对了,所以可以 对相同元素的重复的gcd去重,其实map标记一下就好了,然后暴力维护就能过。
代码:
第一种:
#include <cstring>
#include <iostream>
#include <algorithm>
#include <map>
#include <cmath>
#define x first
#define y second
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
const int N = 2e5 + 10, mod = 1e9 + 7;
ll n, m, t, k;
ll s[N], dp[N][22], g[N], f[N];
ll gcd(ll a, ll b)
{
return b ? gcd(b, a % b) : a;
}
void st()
{
for (int i = 1; i <= n; i++)
dp[i][0] = s[i];
for (int j = 1; (1 << j) <= n; j++)
for (int i = 1; i + (1 << j) - 1 <= n; i++)
dp[i][j] = gcd(dp[i][j-1], dp[i + (1 << (j-1))][j - 1]);
}
bool judge(int l, int r)
{
int k = log(r - l + 1) / log(2);
return gcd(dp[l][k], dp[r - (1 << k) + 1][k]) > 1;
}
int col(int u)
{
int v = u;
int l = u, r = n;
while (l < r)
{
int mid = l + r + 1 >> 1;
if (judge(u, mid)) l = mid;
else r = mid - 1;
}
return l - u + 1;
}
void solve()
{
int maxn = 0;
cin >> n;
for (int i = 0; i < n; i++)
cin >> s[i];
for (int i = n - 1; i > 0; i--)
s[i] = abs(s[i] - s[i-1]);
n--;
st();
for (int i = 1; i <= n; i++)
if (s[i] != 1)
maxn = max(maxn, col(i));
cout << maxn + 1 << endl;
for (int i = 1; i <= n; i++)
for (int j = 0; j < 20; j++)
dp[i][j] = 0;
}
int main()
{
t = 1;
cin >> t;
while (t--)
solve();
return 0;
}
第二种:
#include <cstring>
#include <iostream>
#include <algorithm>
#include <map>
#include <queue>
#include <unordered_map>
#define x first
#define y second
using namespace std;
typedef long long ll;
typedef pair<ll, ll> PII;
const int N = 2e5 + 10, mod = 1e9 + 7;
ll n, m, t, k;
ll s[N], g[N], f[N];
queue<PII> q;
unordered_map<ll, ll> ma;
ll gcd(ll a, ll b)
{
return b ? gcd(b, a % b) : a;
}
void solve()
{
ll ans = 1;
cin >> n;
for (int i = 1; i <= n; i++)
cin >> s[i];
for (int i = n; i >= 2; i--)
s[i] = abs(s[i] - s[i-1]);
for (int i = 2; i <= n; i++)
{
ma.clear();
m = q.size();
for (int j = 0; j < m; j++)
{
PII p = q.front();
q.pop();
ll u = gcd(p.x, s[i]);
ll v = p.y;
if (u != 1 && ma[u] == 0)
v++, q.push({u, v}), ma[u] = 1;
ans = max(ans, v);
}
if (s[i] != 1) q.push({s[i], 2});
}
while(!q.empty()) ans = max(ans, q.front().y) ,q.pop();
cout << ans << endl;
}
int main()
{
t = 1;
cin >> t;
while (t--)
solve();
return 0;
}
E. The Three Little Pigs
题意:给出n(1e6)天,第一天总数量为3,之后每天总数量+3,q次询问,每次查询\(\sum_{i = 1}^{n}C_{3i}^{m}\);
分析:明显暴力的话O(n m) 达到2e11不可取,而对每次单查2e5次查询,时间复杂度需要O(log)或者O(1)的才行,所以明显就是预处理O(nlogn)或者O(n)得出答案O(1)查找,每天多3只。
有递推式:
\(C_{n+3}^{m}\) = \(C_{3}^{0}\) * \(C_{n}^{m}\) + \(C_{3}^{1}\) * \(C_{n}^{m-1}\) + \(C_{3}^{2}\) * \(C_{n}^{m-2}\) + \(C_{3}^{3}\) * \(C_{n}^{m-3}\) = \(C_{n}^{m}\) + 3 * \(C_{n}^{m-1}\) + 3 * \(C_{n}^{m-2}\) + \(C_{n}^{m-3}\)
即:\(C_{n+3}^{m}\) = \(C_{n}^{m}\) + 3 * \(C_{n}^{m-1}\) + 3 * \(C_{n}^{m-2}\) + \(C_{n}^{m-3}\)
拿n=4举例(m比n打的不合规的取0)
\(C_{12}^{12}\) = \(C_{9}^{12}\) + 3 * \(C_{9}^{11}\) + 3 * \(C_{9}^{10}\) + \(C_{9}^{9}\)
\(C_{12}^{11}\) = \(C_{9}^{11}\) + 3 * \(C_{9}^{10}\) + 3 * \(C_{9}^{9}\) + \(C_{9}^{8}\)
\(C_{12}^{10}\) = \(C_{9}^{10}\) + 3 * \(C_{9}^{9}\) + 3 * \(C_{9}^{8}\) + \(C_{9}^{7}\)
\(C_{12}^{9}\) = \(C_{9}^{9}\) + 3 * \(C_{9}^{8}\) + 3 * \(C_{9}^{7}\) + \(C_{9}^{6}\)
\(C_{12}^{8}\) = \(C_{9}^{8}\) + 3 * \(C_{9}^{7}\) + 3 * \(C_{9}^{6}\) + \(C_{9}^{5}\)
列出来可以看出,我们可以先预处理出组合数\(\sum_{i = 1}^{3n+3}C_{3n+3}^{m}\),然后通过迭代对下两项减去3倍的自己,可得到每一次的答案,第一次的\(C_{9}^{9}\) = \(C_{12}^{12}\),\(C_{9}^{8}\) = \(C_{12}^{11}\) - 3 * \(C_{9}^{9}\),\(C_{9}^{7}\) = \(C_{12}^{10}\) - 3 * \(C_{9}^{9}\) - 3 * \(C_{9}^{9}\),这里,我们可以发现,每次单个的答案只与其他四项有关,其中第一项预处理出来了,每次只与前三项有关,但我们的结果不是单个的,而是满足条件的所有的和,这里就不去管第一项,结果就是答案了,这是因为在上述例子中,前三项都是\(C_{9}^{9}\),\(C_{9}^{8}\),\(C_{9}^{7}\),第四项开始,还是减前两项的三倍,这样就变成了\(C_{9}^{6}\)+\(C_{9}^{9}\)但这个\(C_{9}^{9}\)我们不拿\(C_{9}^{9}\)理解,我们用同样的道理替换成\(C_{6}^{6}\),这样就巧妙的使得迭代求出来的是答案的组合数。
代码:
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int mod = 1e9+7, Mod = 1e9+7;
const int N = 3e6+10;
int n, q, u;
int inv[N], fac[N], ifac[N], ans[N], f[N];
void sub(int&a, int b)
{
a = ((1ll * a - b) % mod + mod) % mod;
}
void init(int n)
{
inv[1] = fac[0] = fac[1] = ifac[0] = ifac[1] = 1;
for (int i = 2; i <= n; i++)
{
inv[i] = 1ll * (mod - mod / i) * inv[mod % i] % mod;
fac[i] = 1ll * fac[i - 1] * i % mod;
ifac[i] = 1ll * ifac[i - 1] * inv[i] % mod;
}
}
int C(int m,int n)
{
return 1ll * fac[m] * ifac[n] % mod * ifac[m - n] % mod;
}
int main()
{
cin >> n >> q;
init(3 * n + 3);
for (int i = 0; i < 3 * n + 3; i++)
ans[i] = C(3 * n + 3, i + 1);
for (int i = 3 * n + 2; i > 2; i--)
{
f[i-2] = ans[i];
sub(ans[i-1], 1ll * f[i-2] * 3 % mod);
sub(ans[i-2], 1ll * f[i-2] * 3 % mod);
}
while(q--)
{
scanf("%d", &u);
printf("%d\n", f[u]);
}
return 0;
}
浙公网安备 33010602011771号