AtCoder Beginner Contest 474
AtCoder Beginner Contest 474
A - Not X

要使得输入1,2,3后反回与输入不同且在1,2,3内的数.考虑在一个大小为3的环上移动,输出 \(x \bmod3+1\).
void solve(){
int x;cin>>x;
cout<<(x)%3+1<<endl;
}
B - Exit Order

判定当前离开的人是否属于该分组,先得出该位置隶属分组,然后计算出分组统率区间,最后判定当前人是否属于该分组。
void solve(){
int n;cin>>n;
vector<ll>a(n+1);
for(int i=1;i<=n;i++){
cin>>a[i];
int rk=(i-1)/10+1;//得出分组
int r=rk*10;//得出分组右区间
int l=(rk-1)*10+1;//得出分组左区间
if(a[i]>=l&&a[i]<=r) continue;
cout<<"No"<<endl;
return;
}
cout<<"Yes"<<endl;
}
C - Remove and Append

每次删除数值为x的数,然后插入末尾。求q次删除后数组状态。
维护排列中值为 \(x\) 的位置,考虑删除数后直接对该值的原位置打一个标记,然后在末尾加入该值。
每次如果遍历到删除标记就不进行输出。
这样维护时空复杂度均为 \(O(n+q)\)
void solve(){
int n,q;
cin>>n>>q;
vector<ll>a(n+q+1),p(n+1);
for(int i=1;i<=n;i++){
cin>>a[i];
p[a[i]]=i;
}
int ed=n+1;
for(int i=1;i<=q;i++){
int x;cin>>x;
a[p[x]]=0;
a[ed]=x;
p[x]=ed;
++ed;
}
for(int i=1;i<=n+q;i++) {
if(a[i]) cout<<a[i]<<' ';
}
cout<<endl;
}
D - Outweigh

如果存在某个位置$ a_i>b_i$ 直接赋值极大值\(w_i=10^{18}\),否则赋值极小值 \(w_i=1\),显然这样赋值最优,
且当至少一个 \(a_i>b_i\) 存在时一定有解。
void solve(){
int n;cin>>n;
const ll INF=1e18;
vector<ll>a(n+1),b(n+1);
vector<__int128>w(n+1);
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++) cin>>b[i];
for(int i=1;i<=n;i++){
if(a[i]>b[i]) w[i]=INF;
else w[i]=1;
}
__int128 s=0;
for(int i=1;i<=n;i++){
s+=w[i]*a[i]-w[i]*b[i];
}
if(s>0){
cout<<"Yes"<<endl;
for(int i=1;i<=n;i++) cout<<(ll)w[i]<<' ';
cout<<endl;
}
else cout<<"No"<<endl;
}
E - One Time Coupon

先默认都不使用优惠券这样总计可以获得 \(n\)张优惠券并消耗 \(s=\sum{a_i}\)的代价
然后因为可以重复购买某件商品,设$ mn=\min_{i=1}^n{a_i} $
然后按\(b_i-a_i\) 排序后进行贪心操作,
使得某件商品从 \(a_i\) 优惠到 \(b_i\)有两种方式:
- 选择最便宜的商品购置获得优惠券.
- 消耗已经获得的某张优惠券,并使得获得优惠券的位置不优惠(优惠后代价不优).
仅考虑 \(i\),\(j\)位置,初始代价为 \(a_i+a_j\),
使用方式1的产生代价为 \(2mn+b_i+b_j\)
使用方式2的产生代价为 \(b_i+a_j\)
因此只需要贪心比较 \(a_j-b_j\)与 \(2mn\)大小后操作一定最优
当 \(a_i-b_i \geq 2mn\) 时选用2操作,否则选用1操作.
void solve(){
int n;cin>>n;
multiset<ll>st;
ll ans=0;
vector<ll>a(n+1),b(n+1);
ll mn=INF;
for(int i=1;i<=n;i++) {
cin>>a[i]>>b[i];
ans+=a[i];
mn=min(mn,a[i]);
st.emplace(b[i]-a[i]);
}
int cnt=0;
while(st.size()>=2&&ans+*st.begin()<ans){
ll L=*st.begin(),R=*st.rbegin();
if(R+mn*2<0) break;
ans+=L;
st.erase(st.find(L));
st.erase(st.find(R));
}
while(!st.empty()){
ll L=*st.begin();
if(ans+L+mn<ans) ans+=L+mn;
st.erase(st.find(L));
}
cout<<ans<<endl;
}
F - Increment All Divisors

设每个位置的最终状态值为\(C\),初始状态值为 \(A_i\),则每个位置需要的增量为 \(B_i =C-A_i\)。
根据倍数关系可以设 每个位置操作次数为 \(x_i\),有\(B_i= \sum _{i \mid j} x_j\) 成立,现在假定 \(C\) 是已知常数,那么 \(B_i\)也是确定的,可以
进行莫比乌斯反演(倍数容斥)得出
要取得C的下界,枚举 \(i\) ,设 \(Q=\sum _{k=1}^{\lfloor \frac{n}{i} \rfloor}\mu(k),P= \sum _{k=1}^{\lfloor \frac{n}{i} \rfloor}\mu{(k)}A_{ik}\),
要满足\(x_i \geq 0\),对于每一个\(i\),对应一个不等式按 \(Q\) 的符号进行分类
- \(Q>0\),使得上界增加,\(R:=\max(R,\lceil \frac{P}{Q} \rceil)\)
- \(Q=0\),若 \(P>0\) 无解.
- \(Q<0\),\(L:=\min(L,\lfloor \frac{P}{Q} \rfloor)\)
最后判定 \(L>R\)则无解,否则输出 \(L-a_1\)(因为每次操作都会选中1位置).
const int N=5e5+5;
int mu[N];
ll pmu[N],a[N];
bool isp[N];
vector<int>p;
const ll INF=1e18;
void solve(){
memset(isp,true,sizeof isp);
isp[0]=isp[1]=false;
mu[1]=1;
int n;cin>>n;
ll L=0,R=INF;
for(int i=1;i<=n;i++) cin>>a[i],L=max(a[i],L);
for(int i=2;i<=n;i++){
if(isp[i]){
mu[i]=-1;
p.push_back(i);
}
for(int x:p){
if(x*i>n) break;
mu[i*x]=-mu[i];
isp[i*x]=false;
if(i%x==0) {
mu[i*x]=0;
break;
}
}
}
for(int i=1;i<=n;i++) pmu[i]=pmu[i-1]+mu[i];
for(int i=1;i<=n;i++){
ll del=0;
for(int k=1;k<=n/i;k++) del+=a[i*k]*mu[k];
ll Q=pmu[n/i],P=del;
if(Q>0) L=max(L,(ll)ceill(1.0*P/Q));
else if(Q==0){
if(P>0){
cout<<-1<<endl;
return;
}
}
else R=min(R,(ll)floorl(1.0*P/Q));
}
if(L>R) cout<<-1<<endl;
else cout<<L-a[1]<<endl;
}
方法二:由于每个 $B_i=C−A_i $都等于 \(x_i\) 加上所有 \(i\) 的真倍数的 \(x\) 之和,如果我们从大到小处理下标
\(i\),当我们处理到 \(i\) 时,所有满足 \(j>i\) 且 \(i \mid j\) 的 \(x_j\) 都已经“确定”,此时
\(B_i\) 中剩下的就只有 \(x_i\) 一项。于是可以立即利用 \(x_i \geq 0\) 得到关于 C 的一个线性约束。对 \(x_i\) 的线性式可以统一表示为:
初始时 \(b_i=1,c_i=−A_i\),即 \(x_i\) 尚未分离,\(b_iC+c_i\) 代表的是当前的 \(B_i\)。随着处理更大的倍数,我们会从
\(B_i\) 中减去那些已确定的 \(x_j\),从而更新 \(b_i,c_i\) 最终它们将恰好表示 \(x_i\)。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll INF = 1e18;
int main() {
cin.tie(0)->ios::sync_with_stdio(false);
int n;cin>>n;
vector<vector<int>> divs(n + 1);
for (int i = 1; i <= n; i++) {
for (int j = i; j <= n; j += i) {
divs[j].push_back(i);
}
}
vector<ll> a(n + 1), b(n + 1), c(n + 1);
ll l = 1, r = INF;
for (int i = 1; i <= n; i++) {
cin >> a[i];
c[i] = -a[i];
b[i] = 1;
}
for (int i = n; i >= 1; i--){
// 处理 x_i = b_i * C + c_i >= 0 的约束
if (b[i] > 0) {
if (c[i] < 0) {
l = max(l, (-c[i] + b[i] - 1) / b[i]);// C >= ceil(-c[i] / b[i])
}
}
else if (b[i] == 0){
if (c[i] < 0) {
cout << -1 << '\n';
return 0;
}
}
else{
r = min(r, c[i] / (-b[i]));// C <= floor(c[i] / (-b[i]))
}// b[i] < 0
for (int d : divs[i]) {
if (d == i) continue;
b[d] -= b[i];
c[d] -= c[i];
}// 从所有因子中消去 x_i
}
if (l > r) {
cout << -1 << '\n';
return 0;
}
// 按最小 C = l 模拟操作,计算总次数
ll ans = 0;
for (int i = n; i >= 1; i--) {
ll add = l - a[i];
ans += add;
for (int d : divs[i]) a[d] += add;
}
cout << ans << '\n';
return 0;
}

浙公网安备 33010602011771号