C - Many Balls
初始数字为 (0), 你可以选择对这个数进行两种操作:(A : +1) 、(B: *2),给出一组到达 (n) 的操作序列(长度最大 (120))。
转化成从 (n) 做 (-1) 和 (/2) 到 (0), 偶数做 (/2), 奇数做 (-1), (log(10^{18}) approx 60), 操作数最多不会超过 (120)
Sample Code (C++)
int main()
{
IOS; LL n; cin >> n;
vector<char> v;
while(n)
{
if(n & 1) v.push_back('A'), n -= 1;
else v.push_back('B'), n /= 2;
}
reverse(v.begin(), v.end());
for(auto c : v) cout << c;
cout << endl;
return 0;
}
D - Pair of Balls
(n) 种颜色的球,每种有两个,按一定顺序放在 (m) 个瓶子中,若某两个瓶子最上方的两个球颜色相同,则可以同时取掉,问是否可以把球取完。
每种颜色转化成一个点,然后做有向图拓扑判环即可
Sample Code (C++)
int n, m;
vector<int> G[N];
int d[N];
int main()
{
IOS; cin >> n >> m;
for(int i = 1; i <= m; ++ i)
{
int k, pre = 0; cin >> k;
for(int j = 1; j <= k; ++ j)
{
int x; cin >> x;
if(pre) G[pre].push_back(x), d[x] ++;
pre = x;
}
}
queue<int> q;
for(int i = 1; i <= n; ++ i)
if(!d[i]) q.push(i);
int res = 0;
while(!q.empty())
{
int u = q.front(); q.pop(); res ++;
for(auto v : G[u])
{
d[v] --;
if(!d[v]) q.push(v);
}
}
if(res == n) cout << "Yes" << endl;
else cout << "No" << endl;
return 0;
}
E - Amusement Park
(n) 个数,取 (k) 次,每个数取完之后 (-1) , 求可以取得的最大值。
贪心从最大的开始取,将 (a) 从大到小排序后,判断剩余次数是否够前 (i) 个 (a_i) 取至 (a_{i + 1}) , 分类讨论即可
Sample Code (C++)
LL n, m, a[N];
int main()
{
cin >> n >> m;
for(int i = 1; i <= n; ++ i) cin >> a[i];
LL res = 0;
sort(a + 1, a + n + 1); reverse(a + 1, a + n + 1);
for(int i = 1; i <= n; ++ i)
{
if((a[i] - a[i + 1]) * i <= m)
{
res += (a[i] + a[i + 1] + 1) * (a[i] - a[i + 1]) / 2 * i;
m -= (a[i] - a[i + 1]) * i;
}
else
{
int t = m / i;
res += t * (a[i] + a[i] - t + 1) / 2 * i;
res += (m % i) * (a[i] - t);
break;
}
}
cout << res << endl;
return 0;
}
F - Max Sum Counting
给长度为 (n) 的序列 (A = (A_1, A_2, ..., A_n)), (B = (B_1, B_2, ... , B_n)), 求集合 (S) 的方案数,使得:(maxlimits_{i in S}A_ige sumlimits_{i in S}B_i)
将 (A) 按升序排序,每次将当前 (A_i) 作为最大的 (A_i), 对 (B) 做背包统计方案数
Sample Code (C++)
int n;
PII a[N];
int dp[N];
int main()
{
IOS; cin >> n;
for(int i = 1; i <= n; ++ i) cin >> a[i].fi;
for(int i = 1; i <= n; ++ i) cin >> a[i].se;
sort(a + 1, a + n + 1);
int res = 0;
dp[0] = 1;
for(int i = 1; i <= n; ++ i)
{
for(int j = a[i].se; j <= a[i].fi; ++ j)
{
res += dp[j - a[i].se];
if(res > P) res -= P;
}
for(int j = 5000; j >= a[i].se; -- j)
{
dp[j] += dp[j - a[i].se];
if(dp[j] > P) dp[j] -= P;
}
}
cout << res << endl;
return 0;
}
G - 01Sequence
构造长度为 (n) 的 (01) 串, 满足 (m) 个 ((l, r, x)) 的限制,即区间 ([l, r]) 至少有 (x) 个 (1), 构造一种 (1) 最少的方案。
贪心按照右端点排序,并从右端点开始填 (1) , 使得填上的 (1) 尽可能同时满足多个限制,结合树状数组统计区间 (1) 的个数,并查集找最靠右端点的下一个可填 (1) 的位置,复杂度 (O(nlogn))
Sample Code (C++)
int n, m, a[N];
struct Node
{
int l, r, x;
bool operator < (const Node& t) const
{
return r < t.r;
}
}q[N];
int fa[N], tr[N];
int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); }
int lowbit(int x) { return x & -x; }
void add(int x) { for(int i = x; i <= n; i += lowbit(i)) tr[i] ++; }
int sum(int x)
{
int res = 0;
for(int i = x; i; i -= lowbit(i)) res += tr[i];
return res;
}
int main()
{
IOS; cin >> n >> m;
for(int i = 1; i <= n; ++ i) fa[i] = i;
for(int i = 1; i <= m; ++ i) cin >> q[i].l >> q[i].r >> q[i].x;
sort(q + 1, q + m + 1);
for(int i = 1; i <= m; ++ i)
{
int one = q[i].x - (sum(q[i].r) - sum(q[i].l - 1));
for(int j = 1; j <= one; ++ j)
{
int now = find(q[i].r);
add(now);
a[now] = 1;
fa[now] = find(now - 1);
}
}
for(int i = 1; i <= n; ++ i) cout << a[i] << " ";
cout << endl;
return 0;
}