Codeforces Round #711 (Div. 2)(A-D)
Codeforces Round #711 (Div. 2)
A
题意
给一个\(n\) 求 最小的\(x ( x \geq n)\) 使 \(gcd(x的各位和,x) > 1\)
sol
暴力即可,显然不会T
code
int f(int x) {
int sum = 0;
while(x) {
sum += x % 10;
x /= 10;
}
return sum;
}
void solve() {
cin >> n;
for(int i = n; ; i++) {
if(__gcd(f(i),i) != 1) {
cout << i << "\n";
return;
}
}
}
B
题意
给\(n\) 个长度为 \(a_i\) 高度为\(1\) 的矩形,和一个长度为\(m\)的大矩形,不能竖着放,求放完\(n\)个矩形后最小的大矩形的高度为多少
sol
\(multiset\) 二分 或者 维护一个优先队列都可
先从大到小排序,用一个multiset容器储存留下的空隙的长度,
枚举贪心,找到所有空隙中大于等于当前矩形长的空隙,减去即可
如果容器为空或者当前矩形长度比容器中最大值都大,需要重新开一层
code
int n,m;
int a[maxn];
void solve() {
cin >> n >> m;
for(int i = 1; i <= n; i++) {
cin >> a[i];
}
multiset<int> s;
s.clear();
int ans = 1;
s.insert(m);
sort(a+1,a+1+n,greater<int> () );
for(int i = 1; i <= n; i++) {
while(s.empty() || (a[i] > *--s.end())) {
ans++;
s.insert(m);
}
auto p = s.lower_bound(a[i]);
int tmp = *p - a[i];
s.erase(p);
s.insert(tmp);
}
cout << ans << "\n";
}
C
题意
略
sol
\(dp[i][j]\) 表示某个粒子衰变年龄为\(j\) 前方还有\(i\) 个平面,
粒子会分成两个,继续向右走且衰变年龄不变 \(dp[i][j] += dp[i-1][j]\)
另一个向左走且衰变年龄减\(1\) \(dp[i][j] += dp[n-i][j-1]\) 一共有\(n\) 个平面. 向左走就是走之前走过的,也就是\(n-i\)个
转移方程即为
初始化的时候平面为\(0\)的情况和粒子为一的情况都初始化为\(1\)即可
注意需要先枚举粒子,
code
int n,m;
int dp[maxn][maxn];
const int mod = 1e9 + 7;
void solve() {
cin >> n >> m;
CLR(dp,0);
for(int i = 1; i <= n; i++) {
dp[i][1] = 1;
}
for(int i = 1; i <= m; i++) {
dp[0][i] = 1;
}
for(int j = 1; j <= m; j++) {
for(int i = 1; i <= n; i++) {
dp[i][j] = (dp[n-i][j-1] + dp[i-1][j]) % mod;
}
}
cout << dp[n][m] << "\n";
}
D
题意
给\(n\)组数和一个\(m\),每组包含三个数\(t,x,y\),初始化\(k\)为\(0\) 当\(t = 1\) 时,\(k += x, 否则,k *= x\) ,且可以执行\([0,y]\)次,求\([1,m]\)中\(k\)能到达每个数的最短时间
sol
暴力再加一点点优化.
首先想到\(O(nm^2)\)做法,直接暴力枚举判断即可,考虑优化到\(O(nm)\),在枚举每\([1,f]\)的时候,对于求得的数,如果已经出现过,则直接\(break;\)
假设当前枚举到\(m_i\) 求得的数为\(m_j\),那么\(m_j = m_i + n * x (n\in[1,f])\) 且\(m_j\)在之前已经出现过,
也就说明枚举\([0,m]\)的时候必会枚举到\(m_j\), 而\(m_j\)也会进行\(m_j + n * x (n\in[1,f])\) 可以很容易发现这两次次枚举中重复枚举了\([m_j,m]\) 之间的可能的数,所以当枚举到\(m_j\)且\(m_j\)出现过的时候,直接跳出,后面的数由\(m_j\)继续枚举即可
可以发现这样复杂度就会降低到\(O(nm)\) , 因为\([0,m]\)区间中的数最多不会枚举超过两次
注意枚举\([1,n]\)的时候,每一次的起点是由上一次决定的,所以需要新开一个临时的数组存储这一次中数的状态.
code
int n,m;
#define int long long
int a[maxn],b[maxn],c[maxn],ans[maxn];
std::vector<bool> v(maxn+1,0);
void solve() {
cin >> n >> m;
for(int i = 1; i <= n; i++) {
cin >> a[i] >> b[i] >> c[i];
}
v[0] = 1;
for(int i = 1; i <= n; i++) {
auto d = v;
for(int j = 0; j <= m; j++) {
if(!v[j]) continue;
if(j == 0 && a[i] == 2) continue;
int p = j;
for(int k = 1; k <= c[i]; k++) {
if(a[i] == 1) {
p = p + (b[i] + 100000 - 1)/100000;
}
else {
p = (p * b[i] + 100000 - 1)/100000;
}
if(p > m || v[p]) break;
ans[p] = i;
d[p] = 1;
}
}
v = d;
}
for(int i = 1; i <= m; i++) {
cout << (ans[i] ? ans[i] : -1) << " \n"[i == m];
}
}