题解-CF1485
哇,我回到家了!太好了!终于可以像样地学习了!
写篇比赛题解恢复一下状态。
A. Add and Divide
如果 \(b=1\),那么必须先把 \(b\) 加 \(1\)。
否则答案上界是 \(\log_ba\),枚举 \(b\) 加的次数即可。
int mylog(int a,int b){
int res=0;
while(b) b/=a,++res;
return res;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
int cas;
for(cin>>cas;cas--;){
int a,b;
cin>>a>>b;
int res=0;
if(b==1) b=2,++res;
int x=mylog(b,a),y=x;
R(t,x)if(t){
y=min(y,mylog(b+t,a)+t);
}
cout<<res+y<<'\n';
}
return 0;
}
B. Replace and Keep Sorted
有点细节 /ch
。
对于区间 \([l,r]\),改变其中一个点,满足题目条件。
显然只有 \(l,r\) 两个点是不可以预处理的,所以前缀和 \(+\) 端点特殊处理即可。
int main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
int n,q,k;
cin>>n>>q>>k;
vi a(n);
vector<i64> pre(n+1);
R(i,n) cin>>a[i],--a[i];
R(i,n) pre[i+1]=pre[i]
+(i==n-1?k:a[i+1])
-(i==0?0:a[i-1]+1)-1;
while(q--){
int l,r;
cin>>l>>r,--l;
if(r-l==1){
cout<<k-1<<'\n';
} else {
i64 res=pre[r-1]-pre[l+1];
// cout<<res<<'\n';
res+=a[l+1]-0-1;
res+=k-a[r-2]-1-1;
cout<<res<<'\n';
}
}
return 0;
}
C. Floor and Mod
写了个乱七八糟的做法不知道怎么过的,写个良心做法。
设 \(c=a\bmod b=\lfloor\frac ab\rfloor\)。
由于 \(c<b\) 且 \(cb\le a\le x\),所以 \(c^2\le x\),可以枚举这个 \(c\)。
对于每个 \(c\),需要统计有多少个 \(c<b\le y\) 满足 \(1\le c(b+1)\le x\)。
这个东西就是 \(\max(0,\min(y,\lfloor\frac{x}{c}\rfloor-1)-c)\)。
int main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
int cas;
for(cin>>cas;cas--;){
int x,y;
cin>>x>>y;
i64 res=0;
for(int c=1;c*c<=x;++c){
res+=max(0,
min(y,x/c-1)-c);
}
cout<<res<<'\n';
}
return 0;
}
D. Multiples and Power Differences
关键点:\(1\le a\le 16\),\(a^4\le 65536\)。
考虑如果 \(b\) 可以为 \(0\),那么直接黑白间隔染色,然后白格子填 \(0\),黑的填 \(a^4\)。
否则有个神奇的结论:
大家都加个 \(720720\) 即可。
// 下次别写 mygcd,写 gcd 好啊
int mygcd(int a,int b){
return a?mygcd(b%a,a):b;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
int n,m;
cin>>n>>m;
vector<vi> a(n,vi(m));
R(i,n)R(j,m) cin>>a[i][j];
R(i,n)R(j,m){
int x=720720;
if((i+j)&1) x+=a[i][j]
*a[i][j]*a[i][j]*a[i][j];
cout<<x<<" \n"[j==m-1];
}
return 0;
}
E. Move and Swap
这个 E
有 E
点点毒瘤,建议先做 F
。
一步一步转化问题:
-
转化为不准交换,没层两个棋子可以不按边走一次。
-
转化为在每个边层中选一条边。如果上下两层的边选出来没有共同点(
type 0
),那么这一层两个棋子的位置就是这两个点;否则(type 1
),这一层还有一个棋子可以是任意位置。对于最后一层有一个点可以随意选。
设 \(f[u]\) 表示选到 \(u\) 和父亲的边这一层,且下端的点是 \(u\) 的最优方案。
对于 type 0
情况可以离散化然后前后缀最大值维护 dp
。
对于 type 1
情况可以对于每个点单独转移,这个任意点必然是同层内最小或最大的点。
细节看代码吧。
int main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
int cas;
for(cin>>cas;cas--;){
int n;
cin>>n;
vector<vi> adj(n);
R(u,n)if(u){
int v;
cin>>v,--v;
adj[u].push_back(v);
adj[v].push_back(u);
}
vi a(n);
R(u,n)if(u) cin>>a[u];
vi fa(n,-1),d(n);
vi mx(n,-inf32),mn(n,inf32);
vector<vi> at(n);
function<void(int)> dfs=[&](int u){
mn[d[u]]=min(mn[d[u]],a[u]);
mx[d[u]]=max(mx[d[u]],a[u]);
at[d[u]].push_back(u);
for(const int &v:adj[u]){
if(v==fa[u]) continue;
fa[v]=u,d[v]=d[u]+1;
dfs(v);
}
};
dfs(0);
int maxd=*max_element(all(d));
vector<i64> f(n,-inf64);
f[0]=0;
R(i,maxd){
vi ds;
for(const int &u:at[i])
ds.push_back(a[u]);
sort(all(ds));
ds.erase(unique(all(ds)),ds.end());
int sn=sz(ds);
vector<i64> pg(sn+1,-inf64),sg(sn+1,-inf64);
for(const int &u:at[i]){
int x=lower_bound(all
(ds),a[u])-ds.begin();
pg[x+1]=max(pg[x+1],f[u]-a[u]);
sg[x]=max(sg[x],f[u]+a[u]);
}
R(i,sn+1)if(i) pg[i]=max(pg[i],pg[i-1]);
L(i,sn) sg[i]=max(sg[i],sg[i+1]);
for(const int &u:at[i+1]){
int x=lower_bound(all(ds),
a[fa[u]])-ds.begin();
f[u]=max(pg[x]+a[fa[u]],sg[x]-a[fa[u]]);
f[u]=max(f[u],f[fa[u]]+
max(mx[i]-a[fa[u]],a[fa[u]]-mn[i]));
}
}
i64 res=0;
for(const int &u:at[maxd])
res=max(res,f[u]+max(
mx[maxd]-a[u],a[u]-mn[maxd]));
cout<<res<<'\n';
}
return 0;
}
F. Copy or Prefix Sum
剖析一下题目:如果不去重,答案应该是 \(2^n\)。
设 \(s_i=\sum_{i=1}^n b_i\)。
设选择 \(b_i=a_i\) 是 type 0
,否则是 type 1
。
正解是个非常 naive
的 dp
,设 \(f[i][j]\) 表示决定到了 \(b_i\),上一个 type 1
的位置是 \(j\)。
所以可以把 dp
前一维压掉,剩下一维如下修改:
设 \(g[x]\) 表示当前 \(s_j-b_j=x\) 的 \(f[i][j]\) 之和。
再维护一个总的 dp
的和,用 std::map
维护 dp
,时间复杂度 \(\Theta(n\log n)\)。
int main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
int cas;
for(cin>>cas;cas--;){
int n;
cin>>n,++n;
vector<i64> a(n),s(n);
R(i,n)if(i){
cin>>a[i];
s[i]=s[i-1]+a[i];
}
map<i64,int> g;
g[0]=1;
int sum=1;
R(i,n)if(i){
int F=(mint(sum)-g[s[i-1]]).x;
g[s[i]-a[i]]=(mint(g[s[i]-a[i]])+F).x;
sum=(mint(sum)+F).x;
}
cout<<sum<<'\n';
}
return 0;
}
我好菜,好菜,好菜。