Educational Codeforces Round 80 (Rated for Div. 2)
A. Deadline
如果n>=d 直接yes
否则对于公式
\(x+ceil(d/(x+1))<=n\)
两边同时乘以x+1
会得到一个关于x的一元二次方程,
通过求根公式解出较小的那一个根x1,
然后在\(x1+-3\) 的范围内找到\(x+ceil(d/(x+1))\) 的最小值与n比较即可。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <vector>
#include <iomanip>
#define ALL(x) (x).begin(), (x).end()
#define sz(a) int(a.size())
#define rep(i,x,n) for(int i=x;i<n;i++)
#define repd(i,x,n) for(int i=x;i<=n;i++)
#define pii pair<int,int>
#define pll pair<long long ,long long>
#define gbtb ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define MS0(X) memset((X), 0, sizeof((X)))
#define MSC0(X) memset((X), '\0', sizeof((X)))
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define eps 1e-6
#define gg(x) getInt(&x)
#define chu(x) cout<<"["<<#x<<" "<<(x)<<"]"<<endl
#define du3(a,b,c) scanf("%d %d %d",&(a),&(b),&(c))
#define du2(a,b) scanf("%d %d",&(a),&(b))
#define du1(a) scanf("%d",&(a));
using namespace std;
typedef long long ll;
ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
ll lcm(ll a, ll b) {return a / gcd(a, b) * b;}
ll powmod(ll a, ll b, ll MOD) { if (a == 0ll) {return 0ll;} a %= MOD; ll ans = 1; while (b) {if (b & 1) {ans = ans * a % MOD;} a = a * a % MOD; b >>= 1;} return ans;}
void Pv(const vector<int> &V) {int Len = sz(V); for (int i = 0; i < Len; ++i) {printf("%d", V[i] ); if (i != Len - 1) {printf(" ");} else {printf("\n");}}}
void Pvl(const vector<ll> &V) {int Len = sz(V); for (int i = 0; i < Len; ++i) {printf("%lld", V[i] ); if (i != Len - 1) {printf(" ");} else {printf("\n");}}}
inline void getInt(int* p);
const int maxn = 1000010;
const int inf = 0x3f3f3f3f;
/*** TEMPLATE CODE * * STARTS HERE ***/
int t;
ll n, d;
ll check(ll x)
{
return x + ceil(1.0 * d / (x + 1));
}
typedef long double ld;
int main()
{
//freopen("D:\\code\\text\\input.txt","r",stdin);
//freopen("D:\\code\\text\\output.txt","w",stdout);
// gbtb;
cin >> t;
while (t--)
{
cin >> n >> d;
if (d <= n)
{
cout << "YES" << endl;
} else
{
int isok = 0;
ld b = 1 - n;
ld c = d - n;
ld xw = (-b - sqrtl(b * b - 4.0 * c)) * 0.5;
ll xx = ll(xw);
ll w = inf;
repd(i, max(0ll, xx - 3), min(n * 1ll, xx + 3))
{
w = min(w, check(i));
}
if (w <= n)
{
isok = 1;
}
if (isok)
{
cout << "YES" << endl;
} else
{
cout << "NO" << endl;
}
}
}
return 0;
}
inline void getInt(int* p) {
char ch;
do {
ch = getchar();
} while (ch == ' ' || ch == '\n');
if (ch == '-') {
*p = -(getchar() - '0');
while ((ch = getchar()) >= '0' && ch <= '9') {
*p = *p * 10 - ch + '0';
}
}
else {
*p = ch - '0';
while ((ch = getchar()) >= '0' && ch <= '9') {
*p = *p * 10 + ch - '0';
}
}
}
B. Yet Another Meme Problem
通过打表可以发现
满足 \(a \cdot b + a + b = conc(a, b)\) 的b 为9 ,99,999,9999...等等
所以答案即为 A*F(B)
F(B) 为 <=B 的 9 ,99,999,9999...等等的个数
F(B) 的求法非常简单。
int t;
ll a, b;
ll F(ll x)
{
ll res = 0ll;
ll base = 9ll;
while (base <= x)
{
base *= 10;
base += 9;
res++;
}
return res;
}
int main()
{
//freopen("D:\\code\\text\\input.txt","r",stdin);
//freopen("D:\\code\\text\\output.txt","w",stdout);
gbtb;
cin >> t;
while (t--)
{
cin >> a >> b;
a *= F(b);
cout << a << endl;
}
return 0;
}
C. Two Arrays
设\(dp1[i][j]\) 为长度为i,以j为结尾的单调不下降序列方案数
设\(dp2[i][j]\) 为长度为i,以j为结尾的单调不上升序列方案数
当j1>=j2 时 以j1为结尾的单调不下降序列和以j2为结尾的单调不上升序列
自动满足:
- ai≤bi for any index ii from 11 to mm;
- array a is sorted in non-descending order;
- array b is sorted in non-ascending order.
然后扫一遍计算答案即可,可以用前缀和来优化,但是这个数据范围没啥必要。
ll dp1[11][1111];
ll dp2[11][1111];
ll n;
ll m;
const ll mod = 1e9 + 7;
int main()
{
//freopen("D:\\code\\text\\input.txt","r",stdin);
//freopen("D:\\code\\text\\output.txt","w",stdout);
cin >> n >> m;
repd(i, 1, n)
{
dp1[1][i] = 1;
dp2[1][i] = 1;
}
repd(i, 2, m)
{
repd(j, 1, n)
{
repd(k, 1, j)
{
dp1[i][j] += dp1[i - 1][k];
dp1[i][j] %= mod;
}
repd(k, j, n)
{
dp2[i][j] += dp2[i - 1][k];
dp2[i][j] %= mod;
}
}
}
ll ans = 0ll;
repd(i, 1, n)
{
repd(j, 1, i)
{
ans += dp2[m][i] * dp1[m][j] % mod;
ans %= mod;
}
}
cout << ans << endl;
return 0;
}
D. Minimax Problem
极小极大问题 普遍容易通过转化为二分极小值,然后验证可行性的问题。
本题也不例外,我们二分\(\min \limits_{k = 1}^{m} b_k\) 的数值mid。
然后去check 是否存在两个数组满足此条件。
check的方法如下:
我们遍历这n个数组,对于第i个数组的第j个数a(i,j) 是否大于等于mid用一个int整数进行二进制的状态压缩表示,然后将int数值加入一个unordered_set 中。
然后对unset中的元素进行两两测试是否按位与起来满足 m个数均为1.
为什么要用unset呢?
紧扣题目的数据范围,m<=8, 那么对于每一次check一共也就只有 2^8 个情况,
两两测试的时候最大也就是2^16次,依次来降低时间复杂度。
二分的时间复杂度时O(1e9)
check的时间复杂度为O(n*m+2^16)
总时间复杂度为:\(O(log_2(1e9)*(n*m+2^{16}))\)
#include <bits/stdc++.h>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <vector>
#include <iomanip>
#define ALL(x) (x).begin(), (x).end()
#define sz(a) int(a.size())
#define rep(i,x,n) for(int i=x;i<n;i++)
#define repd(i,x,n) for(int i=x;i<=n;i++)
#define pii pair<int,int>
#define pll pair<long long ,long long>
#define gbtb ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define MS0(X) memset((X), 0, sizeof((X)))
#define MSC0(X) memset((X), '\0', sizeof((X)))
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define eps 1e-6
#define gg(x) getInt(&x)
#define chu(x) cout<<"["<<#x<<" "<<(x)<<"]"<<endl
#define du3(a,b,c) scanf("%d %d %d",&(a),&(b),&(c))
#define du2(a,b) scanf("%d %d",&(a),&(b))
#define du1(a) scanf("%d",&(a));
using namespace std;
typedef long long ll;
ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
ll lcm(ll a, ll b) {return a / gcd(a, b) * b;}
ll powmod(ll a, ll b, ll MOD) { if (a == 0ll) {return 0ll;} a %= MOD; ll ans = 1; while (b) {if (b & 1) {ans = ans * a % MOD;} a = a * a % MOD; b >>= 1;} return ans;}
void Pv(const vector<int> &V) {int Len = sz(V); for (int i = 0; i < Len; ++i) {printf("%d", V[i] ); if (i != Len - 1) {printf(" ");} else {printf("\n");}}}
void Pvl(const vector<ll> &V) {int Len = sz(V); for (int i = 0; i < Len; ++i) {printf("%lld", V[i] ); if (i != Len - 1) {printf(" ");} else {printf("\n");}}}
inline void getInt(int* p);
const int maxn = 1000010;
const int inf = 0x3f3f3f3f;
/*** TEMPLATE CODE * * STARTS HERE ***/
int n, m;
int a[300010][9];
unordered_set<int> st;
int t1, t2;
bool check(int x)
{
st.clear();
int t;
repd(i, 1, n)
{
t = 0;
repd(j, 1, m)
{
if (a[i][j] >= x)
{
t |= (1 << (j - 1));
}
}
st.insert(t);
}
for (auto y : st)
{
for (auto z : st)
{
if ((y | z) == (1 << m) - 1)
{
t1 = y;
t2 = z;
return 1;
}
}
}
}
int main()
{
//freopen("D:\\code\\text\\input.txt","r",stdin);
//freopen("D:\\code\\text\\output.txt","w",stdout);
du2(n, m);
repd(i, 1, n)
{
repd(j, 1, m)
{
du1(a[i][j]);
}
}
int l = 0;
int r = 1e9;
int mid;
int ans;
while (l <= r)
{
mid = (l + r) >> 1;
if (check(mid))
{
l = mid + 1;
ans = mid;
} else
{
r = mid - 1;
}
}
int ans1, ans2;
repd(i, 1, n)
{
int t = 0;
repd(j, 1, m)
{
if (a[i][j] >= ans)
{
t |= (1 << (j - 1));
}
}
if (t == t1)
{
ans1 = i;
}
if (t == t2)
{
ans2 = i;
}
}
printf("%d %d\n", ans1, ans2 );
return 0;
}
inline void getInt(int* p) {
char ch;
do {
ch = getchar();
} while (ch == ' ' || ch == '\n');
if (ch == '-') {
*p = -(getchar() - '0');
while ((ch = getchar()) >= '0' && ch <= '9') {
*p = *p * 10 - ch + '0';
}
}
else {
*p = ch - '0';
while ((ch = getchar()) >= '0' && ch <= '9') {
*p = *p * 10 + ch - '0';
}
}
}
E. Messenger Simulator
对于n个数的数组,我们先初始为 n,n-1,n-2,,,,3 ,2,1 的情况 (即n~1的降序排列)
然后把给定的m个数依次放在数组的尾部。
例如样例1 为:
5 4 3 2 1 3 5 1 4
定义数组vis[i] 代表数字i上一次出现在哪个位置,初始为0。
我们维护两个数组 be[i] 代表数字i出现的最前的位置,ed[i] 代表数字i 出现 的最后的位置。
如果有把数字i提前到第一位,be[i]=1,否则be[i]=i
ed[i] 为上述数组中紧挨着的两个i之间的数字种类数,或者最后一个数字i到结束有多少种类的数字。
因为如果出现第2次以及以上次数i时,证明将数字i提到数组的最前端了,那么i之前的位置一定是一个偏后面的位置,我们维护ed[i]的最大值即可。
至于“ed[i]的另一个情况,最后一个数字i到结束有多少种类的数字”,可以通过样例自行理解一下。
因为询问的区间均为有序的区间,所以可以直接用树状数组进行维护即可。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <vector>
#include <iomanip>
#define ALL(x) (x).begin(), (x).end()
#define sz(a) int(a.size())
#define rep(i,x,n) for(int i=x;i<n;i++)
#define repd(i,x,n) for(int i=x;i<=n;i++)
#define pii pair<int,int>
#define pll pair<long long ,long long>
#define gbtb ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define MS0(X) memset((X), 0, sizeof((X)))
#define MSC0(X) memset((X), '\0', sizeof((X)))
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define eps 1e-6
#define gg(x) getInt(&x)
#define chu(x) cout<<"["<<#x<<" "<<(x)<<"]"<<endl
#define du3(a,b,c) scanf("%d %d %d",&(a),&(b),&(c))
#define du2(a,b) scanf("%d %d",&(a),&(b))
#define du1(a) scanf("%d",&(a));
using namespace std;
typedef long long ll;
ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
ll lcm(ll a, ll b) {return a / gcd(a, b) * b;}
ll powmod(ll a, ll b, ll MOD) { if (a == 0ll) {return 0ll;} a %= MOD; ll ans = 1; while (b) {if (b & 1) {ans = ans * a % MOD;} a = a * a % MOD; b >>= 1;} return ans;}
void Pv(const vector<int> &V) {int Len = sz(V); for (int i = 0; i < Len; ++i) {printf("%d", V[i] ); if (i != Len - 1) {printf(" ");} else {printf("\n");}}}
void Pvl(const vector<ll> &V) {int Len = sz(V); for (int i = 0; i < Len; ++i) {printf("%lld", V[i] ); if (i != Len - 1) {printf(" ");} else {printf("\n");}}}
inline void getInt(int* p);
const int maxn = 1000010;
const int inf = 0x3f3f3f3f;
/*** TEMPLATE CODE * * STARTS HERE ***/
int n, m;
int a[maxn];
int tree[maxn];
int lowbit(int x)
{
return x & (-x);
}
void add(int pos, int val)
{
while (pos < maxn)
{
tree[pos] += val;
pos += lowbit(pos);
}
}
int ask(int pos)
{
int res = 0;
while (pos>0)
{
res += tree[pos];
pos -= lowbit(pos);
}
return res;
}
int be[maxn];
int ed[maxn];
int vis[maxn];
int main()
{
//freopen("D:\\code\\text\\input.txt","r",stdin);
//freopen("D:\\code\\text\\output.txt","w",stdout);
du2(n, m);
repd(i, 1, n)
{
a[i] = n - i + 1;
}
repd(i, n + 1, n + m)
{
du1(a[i]);
be[a[i]] = 1;
}
repd(i, 1, n)
{
if (!be[i])
{
be[i] = i;
}
}
repd(i, 1, n + m)
{
if (!vis[a[i]])
{
add(i, 1);
} else
{
add(vis[a[i]], -1);
add(i, 1);
}
// chu(i);
if (vis[a[i]])ed[a[i]] = max(ed[a[i]], ask(i) - ask(vis[a[i]] - 1));
vis[a[i]] = i;
}
repd(i, 1, n)
{
ed[i] = max(ed[i], ask(n + m) - ask(vis[i] - 1));
}
repd(i, 1, n)
{
printf("%d %d\n", be[i], ed[i] );
}
return 0;
}
inline void getInt(int* p) {
char ch;
do {
ch = getchar();
} while (ch == ' ' || ch == '\n');
if (ch == '-') {
*p = -(getchar() - '0');
while ((ch = getchar()) >= '0' && ch <= '9') {
*p = *p * 10 - ch + '0';
}
}
else {
*p = ch - '0';
while ((ch = getchar()) >= '0' && ch <= '9') {
*p = *p * 10 + ch - '0';
}
}
}