Codeforces Round #778 (Div. 1 + Div. 2, based on Technocup 2022 Final Round)
Codeforces Round #778 (Div. 1 + Div. 2, based on Technocup 2022 Final Round)
A
题意
进行一次翻转,求相邻两数最大和
思路
总可以将最大的两个凑一起
#include<bits/stdc++.h>
#define yes puts("yes");
#define inf 0x3f3f3f3f
#define ll long long
#define linf 0x3f3f3f3f3f3f3f3f
#define debug(x) cout<<'>' << ' ' << x<<endl;
#define ull unsigned long long
#define endl '\n'
#define lowbit(x) x&-x
#define int long long
using namespace std;
typedef pair<int,int> PII;
const int MAXN =10 + 2e5 ,mod=1e9 + 7;
void solve()
{
int N;
cin >> N;
vector<int> a(N);
for(int i = 0;i < N;i ++) cin >> a[i];
sort(a.begin(),a.end());
cout << a[N - 1] + a[N - 2] << endl;
}
signed main()
{
ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
int T;cin>>T;
while(T--)
solve();
return 0;
}
B
题意
每次删除一个字符串的极长重复前缀,
重复前缀指可以在后面找到一个与它相同且不相交的子串
求删到最后剩什么
思路
如果该字母只有一个,显然不可删
考虑删除一个极长前缀,可以变成多次删一个字符的操作
所以只要这个字符串中的同类字符大于一,这个字符就是可删的
#include<bits/stdc++.h>
#define yes puts("yes");
#define inf 0x3f3f3f3f
#define ll long long
#define linf 0x3f3f3f3f3f3f3f3f
#define debug(x) cout<<'>' << ' ' << x<<endl;
#define ull unsigned long long
#define endl '\n'
#define lowbit(x) x&-x
#define int long long
using namespace std;
typedef pair<int,int> PII;
const int MAXN =10 + 2e5 ,mod=1e9 + 7;
void solve()
{
string s;
cin >> s;
vector<int> cnt(26);
for(int i = 0;i < s.size();i ++) {
cnt[s[i] - 'a'] ++;
}
int st = 0;
for(int i = 0;i < s.size();i ++) {
if(cnt[s[i] - 'a'] > 1) {
cnt[s[i] - 'a'] --;
st ++;
}else break;
}
for(int i = st;i < s.size();i ++) {
cout << s[i] ;
}
cout << endl;
}
signed main()
{
ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
int T;cin>>T;
while(T--)
solve();
return 0;
}
C
题意
把一个数等分 $N - 1 $ 次,如果这个数是奇数,按上下取整等分
现在给出操作结果,问是否合法
思路
第一思路是开一个小根堆,每次取出最小的两个,合并它们看是否满足要求
但可以发现拆分策略不是优先合并最小值
不考虑过程,可以发现这个操作的终止状态和起始状态都十分明确
向上合并的策略不好发现,不妨向下拆分
显然最大的数字一定最先被拆出来,设当前未被拆出的最大值为 \(x\)
所以就维护大根堆,拿出队首 $ mx $ ,和 $ x $ 比较
如果 $ mx $ 较小,显然无论如何都凑不出
如果 $mx $ 和 $ x $ 相同,固定这个 \(mx\) ,不再对它拆分,更新当前 $mx $ 和 \(x\)
如果大,那无论如何都要将它继续拆分
如果拆分 \(n - 1\) 次后合法,则输出 YES。
如果发现,大的数总是先被拆出以及如果当前数大就一定要继续拆分,当前数小,就说明不再有拆出这个数的可能,就非常显然了。
#include<bits/stdc++.h>
#define yes puts("yes");
#define inf 0x3f3f3f3f
#define ll long long
#define linf 0x3f3f3f3f3f3f3f3f
#define debug(x) cout<<'>' << ' ' << x<<endl;
#define ull unsigned long long
#define endl '\n'
#define lowbit(x) x&-x
#define int long long
using namespace std;
typedef pair<int,int> PII;
const int MAXN =10 + 2e5 ,mod=1e9 + 7;
void solve()
{
int N;
cin >> N;
vector<int> a(N + 1);
int sum = 0;
for(int i = 1;i <= N;i ++) {
cin >> a[i];
sum += a[i];
}
if(N == 1) {
cout << "YES\n";
return;
}
sort(a.begin() + 1,a.end());
int cnt = 0;
priority_queue<int> q;
q.push(sum);
while(cnt != N) {
int t = q.top();
q.pop();
if(t == a.back()) {
a.pop_back();
cnt ++;
}else if(t > a.back()) {
q.push(t / 2);
q.push((t + 1) / 2);
// cnt += 2;
}else {
cout << "NO\n";
return;
}
}
cout << "YES\n";
}
signed main()
{
ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
int T;cin>>T;
while(T--)
solve();
return 0;
}
D
题意
$ N $ 种配料,给出它们的比例,保证每种配料都受到约束,共给出 \(N - 1\) 个比例关系。
问为使得配料在满足给出比例情况下,各个配料的最小整数解的和是多少
思路
将 \(N - 1\) 个比例关系当成在两种配料之间的一条边,就可以构成一棵树。
显然如果我们可以得到任意配料的一个解,其他配料都可以通过比例推出
那么我们随便取这棵关系树的某一个点当根,想办法找到根节点的解,然后再推出其他节点的解把它们求和就是答案了
如何求出根根节点的解呢?
不妨设根节点的解为 \(t\)
根和它的子节点的比例是 \(x_i:y_i\)
子节点 \(v\) 的解就是 $t * \frac {y_i}{x_i} $
以此类推任意一节点的解 \(w\) 都可以写成
为了保证 \(w\) 是一个整数, \(t\) 最小是分母的 \(lcm\)
\(lcm\) 本质上就是对每一个质因数的幂次取 \(max\) 的过程
所以我们可以动态维护每一个节点分母的质因子幂次最大值
需要注意的是,我们还需要考虑分子对质因子的影响
比如 $ \frac{3}{2} * \frac{2}{3} $ 的情况,因为分子和分母会相消,所以在维护分母质因子幂次最大值时要先将分子的质因子幂次减去
如此求出质因子幂次最大值,把它们乘起来就是 \(t\)
有了 \(t\) 答案就很显然了
#include<bits/stdc++.h>
#define yes puts("yes");
#define inf 0x3f3f3f3f
#define ll long long
#define linf 0x3f3f3f3f3f3f3f3f
#define debug(x) cout<<'>' << ' ' << x<<endl;
#define ull unsigned long long
#define endl '\n'
#define lowbit(x) x&-x
#define int long long
using namespace std;
typedef pair<int,int> PII;
const int MAXN =10 + 2e5 ,mod=998244353;
map<int,int> ps[MAXN];// 预处理出每个数的质因数与幂次
int inv[MAXN];
void init() {
inv[0] = inv[1] = 1;
for(int i = 2;i < MAXN;i ++) {
inv[i] = (mod - mod / i) * inv[mod % i] % mod;
}
for(int i = 2;i < MAXN;i ++) {
int now = i;
for(int j = 2;j <= now / j;j ++) {
while(now % j == 0) {
now /= j;
ps[i][j] ++;
}
}
if(now != 1) ps[i][now] ++;
}
}
int ksm(int a,int b) {
int ans = 1;
a %= mod;
while(b) {
if(b & 1) ans = ans * a % mod;
a = a * a % mod;
b >>= 1;
}
return ans;
}
void solve()
{
int N;
cin >> N;
vector<tuple<int,int,int>> tr[N + 1];
for(int i = 0;i < N - 1;i ++) {
int u,v,x,y;
cin >> u >> v >> x >> y;
tr[u].push_back({v,x,y});
tr[v].push_back({u,y,x});
}
vector<int> cnt(N + 1,0);
vector<int> mxp(N + 1,0);// 全局质因子幂次最大值
function<void(int,int)> dfs = [&](int u,int fa) {// 求t
for(auto [v,x,y] : tr[u]) {
if(v == fa) continue;
for(auto [r,e] : ps[y]) {
cnt[r] -= e;
}
for(auto [r,e] : ps[x]) {
cnt[r] += e;
mxp[r] = max(mxp[r],cnt[r]);
}
dfs(v,u);
for(auto [r,e] : ps[y]) {
cnt[r] += e;
}
for(auto [r,e] : ps[x]) {
cnt[r] -= e;
}
}
};
dfs(1,1);
int t = 1;
for(int i = 1;i <= N;i ++) {
t = (t * ksm(i,mxp[i])) % mod;
}
vector<int> w(N + 1);
w[1] = t;
function<void(int,int)> dfs2 = [&](int u,int fa) {
for(auto [v, x, y] : tr[u]) {
if(v != fa) {// a : b = x : y b = ya / x
w[v] = w[u] * y % mod * inv[x] % mod;
dfs2(v,u);
}
}
};
dfs2(1,1);
int ans = 0;
for(int i = 1;i <= N;i ++) ans = (ans + w[i]) % mod;
cout << ans << endl;
}
signed main()
{
ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
init();
int T;cin>>T;
while(T--)
solve();
return 0;
}