ARC135 题解
为什么不写 AGC,因为我还不会 ARC
A - Floor, Ceil - Decomposition
-
给定一个数 \(x\),你可以执行操作任意次。
-
每次操作从中选出一个数 \(x\),将其拆分成 \(\lfloor \frac{x}{2} \rfloor\) 和 \(\lceil \frac{x}{2} \rceil\)。求最后所有数的积的最大值。
-
\(1 \leq x \leq 10^{18}\)
当 \(x\leq 4\) 的时候不用再分,否则必须要分。
记忆化搜索。时间复杂度 \(O(\log x\log\log x)\)?
#include<bits/stdc++.h>
using namespace std;
using namespace my_std;
#define mod 998244353
map<ll,ll> mp;
ll x;
ll f(ll x){
if(x<=4) return x;
if(mp.count(x)) return mp[x];
return mp[x]=f(x/2)*f((x+1)/2)%mod;
}
int main(){
x=read();
write(f(x));
}
B - Sum of Three Terms
-
给定一个长为 \(n\) 的序列 \(S\),要求构造一个长为 \(n+2\) 的非负整数序列 \(A\),使得 \(\forall i \in [1,n],A_i+A_{i+1}+A_{i+2}=S_i\),或判断无解。
-
\(1 \leq n \leq 3\times 10^5,0 \leq S_i \leq 10^9\)
如果 \(A_1,A_2,A_3\) 知道了,那么所有 \(A_i\) 都可以推出来,并且 \(A_{3k+1},A_{3k+2},A_{3k+3}\) 互不影响。所以先算出来 \(A_{3k+i}\) 与 \(A_i\) 的差 \(B_{3k+i}\)。
先根据 \(S_1\) 判断后面的 \(S_i\) 合不合法。因为要求 \(A\) 非负,所以需要满足 \(A_i+B_{3k+i} \geq 0\),即 \(A_i \geq -B_{3k+i}\)。
得出 \(A_1,A_2,A_3\) 的最小值后再判断合不合法,然后随便构造。
时间复杂度 \(O(n)\)
#include<bits/stdc++.h>
using namespace std;
using namespace my_std;
ll n,s[300030],a[300030],minn[4];
int main(){
n=read();
fr(i,1,n) s[i]=read();
fr(i,1,n-1) a[i+3]=a[i]+s[i+1]-s[i];
fr(i,1,n){
if(s[i]!=(s[1]+a[i]+a[i+1]+a[i+2])){
pf("No");
return 0;
}
}
fr(i,1,3) minn[i]=inf;
for(ll i=1;i<=(n+2);i+=3) minn[1]=min(minn[1],a[i]);
for(ll i=2;i<=(n+2);i+=3) minn[2]=min(minn[2],a[i]);
for(ll i=3;i<=(n+2);i+=3) minn[3]=min(minn[3],a[i]);
if((-minn[1]-minn[2]-minn[3])>s[1]){
pf("No");
return 0;
}
a[1]=-minn[1];
a[2]=-minn[2];
a[3]=s[1]-a[1]-a[2];
fr(i,1,n-1) a[i+3]=a[i]+s[i+1]-s[i];
pf("Yes\n");
fr(i,1,n+2) writesp(a[i]);
}
C - XOR to All
-
给定一个长度为 \(n\) 的序列 \(a\),你可以进行若干次操作。
-
每次操作你可以从 \(a\) 当中选定一个数,并将 \(a\) 中所有数异或上它。求最后 \(\displaystyle\sum_{i=1}^{n}{a_i}\) 的最大值。
-
\(1 \leq n \leq 3 \times 10^5,0 \leq a_i < 2^{30}\)
首先可以发现,无论执行多少次操作,都等价于只执行最后那个操作。
证明
WLOG,我们先后选择第 \(n-1\) 个位置上的数和第 \(n\) 个位置上的数。
那么第一次操作后,序列变成了
第二次操作后,序列变成了
所以枚举要选的数,然后计算答案。计算答案直接按位考虑,看第 \(i\) 位有多少个数与选的数相反。
注意可以不选。时间复杂度 \(O(n\log A)\)
#include<bits/stdc++.h>
using namespace std;
using namespace my_std;
ll n,a[300030][31],sum[31],ans;
int main(){
n=read();
fr(i,1,n){
ll x=read();
ans+=x;
fr(j,0,30) if(x&(1ll<<j)) a[i][j]=1;
fr(j,0,30) sum[j]+=a[i][j];
}
fr(i,1,n){
ll res=0;
fr(j,0,30){
if(a[i][j]) res+=(1ll<<j)*(n-sum[j]);
else res+=(1ll<<j)*sum[j];
}
ans=max(ans,res);
}
write(ans);
}
前三题切的不必大声喊叫,看看就好。
后三题切的就可以出门左转了。
D - Add to Square
-
给定一个 \(n\times m\) 的网格,每个格子有一个整数 \(A_{i,j}\),你可以执行任意多次操作。
-
每次操作选定一个位置 \((i,j)(1 \leq i < n,1 \leq j < m)\),再选定一个整数 \(x\),将 \(A_{i,j},A_{i,j+1},A_{i+1,j}\) 和 \(A_{i+1,j+1}\) 加 \(x\)。你要使得最终的 \(\displaystyle\sum_{i=1}^{n}\sum_{j=1}^{m}{|A_{i,j}|}\) 最小。
-
\(2 \leq n,m \leq 500,|A_{i,j}| \leq 10^9\)
不妨设最终的网格为 \(B\)。
对于每一行,因为每次只会同时给相邻两个数加 \(x\),说明其交替和是不会变的,即 \(\displaystyle\sum_{j=1}^{m}{A_{i,j}\times (-1)^j}\) 不变。每一列同理。
所以我们得到了 \(B\) 的一个必要条件,就是每一行、每一列的交替和与 \(A\) 相同。
同时,这也是 \(B\) 的一个充分条件。
证明
要证它是充分条件,相当于证明每一个交替和与 \(A\) 相同的 \(B\) 都可以通过若干次操作得到。
由题目易知,能从 \(A\) 到 \(B\),也能从 \(B\) 到 \(A\)。那我们尝试将 \(B\) 还原成 \(A\)。
将 \(B\) 网格从上到下,从左到右进行操作,每次将左上角 \(B_{i,j}\) 还原成 \(A_{i,j}\)。这样我们就将所有 \(1 \leq i < n,1 \leq j < m\) 的 \((i,j)\) 还原了。
至于最后一行,因为我们操作后 \(B\) 的交替和是不变的,所以可以通过交替和与前 \(n-1\) 个数来得到第 \(n\) 个数究竟是什么。最后一列同理。
因为 \(B\) 与 \(A\) 的交替和相同,所以最后一行与最后一列也与 \(A\) 相同。
交替和这种东西有点难搞,所以先将每个 \(A_{i,j}\) 变成 \(A_{i,j} \times (-1)^{i+j}\) ,然后我们改一下题目:
给定两个序列 \(\{x_1,\ldots,x_n\}\),\(\{y_1,\ldots,y_m\},\sum{x_i}=\sum{y_j}\),你要构造一个矩阵 \(B\),使得第 \(i\) 行的和是 \(x_i\),第 \(j\) 列的和是 \(y_j\)。求 \(\sum{|B_{i,j}|}\) 的最小值。
先将 \(B\) 中的元素全部设为 \(0\),然后一个一个在 \((i,j)\) 上加一些值,题目变成了:
给定两个序列 \(\{x_1,\ldots,x_n\}\),\(\{y_1,\ldots,y_m\},\sum{x_i}=\sum{y_j}\),你可以进行任意次操作。
每次操作 \((i,j,x)\) 表示让 \(x_i\) 和 \(y_j\) 减 \(x\),代价为 \(|x|\)。你需要让所有 \(x\) 和 \(y\) 变为 \(0\),并且求代价最小值。
首先有个很显然的下界:\(Ans \geq \max(\sum{|x_i|},\sum{|y_j|})\)。下面我们通过构造操作证明答案可以取到下界。
当存在 \(x_i\) 与 \(y_j\) 正负性相同时,可以将它们同时减(加)一,如此循环下去,这肯定是最优的。
当存在 \(x_i > 0\) 和 \(x_j < 0\) 时,可以先将 \(x_i\) 与 \(y_1\) 减一,再把 \(x_j\) 与 \(y_1\) 加一,这样也可以紧贴下界。对于 \(y\) 同理。
通过上述操作,我们将所有 \(x_i\) 的正负性统一了,也将 \(y_j\) 的正负性统一了,而且 \(x_i\) 与 \(y_j\) 正负性相反。又因为 \(\sum{x_i}=\sum{y_j}\),所以 \(x\) 与 \(y\) 只能全是 \(0\)。这就符合条件了。
时间复杂度 \(O(n^3)\)。
#include<bits/stdc++.h>
using namespace std;
using namespace my_std;
ll n,m,a[550][550],x[550],y[550],b[550][550];
ll pos1[550],pos2[550],cnt1=0,cnt2=0,ans=0;
void solve(){
fr(i,1,n) if(x[i]>0) pos1[++cnt1]=i;
fr(i,1,m) if(y[i]>0) pos2[++cnt2]=i;
while(cnt1&&cnt2){
ll tmp=min(x[pos1[cnt1]],y[pos2[cnt2]]);
b[pos1[cnt1]][pos2[cnt2]]+=tmp;
x[pos1[cnt1]]-=tmp;
y[pos2[cnt2]]-=tmp;
if(!x[pos1[cnt1]]) cnt1--;
if(!y[pos2[cnt2]]) cnt2--;
}
cnt1=cnt2=0;
fr(i,1,n) if(x[i]<0) pos1[++cnt1]=i;
fr(i,1,m) if(y[i]<0) pos2[++cnt2]=i;
while(cnt1&&cnt2){
ll tmp=min(-x[pos1[cnt1]],-y[pos2[cnt2]]);
b[pos1[cnt1]][pos2[cnt2]]-=tmp;
x[pos1[cnt1]]+=tmp;
y[pos2[cnt2]]+=tmp;
if(!x[pos1[cnt1]]) cnt1--;
if(!y[pos2[cnt2]]) cnt2--;
}
cnt1=cnt2=0;
fr(i,1,n){
if(x[i]>0) pos1[++cnt1]=i;
if(x[i]<0) pos2[++cnt2]=i;
}
while(cnt1&&cnt2){
ll tmp=min(x[pos1[cnt1]],-x[pos2[cnt2]]);
b[pos1[cnt1]][1]+=tmp;
b[pos2[cnt2]][1]-=tmp;
x[pos1[cnt1]]-=tmp;
x[pos2[cnt2]]+=tmp;
if(!x[pos1[cnt1]]) cnt1--;
if(!x[pos2[cnt2]]) cnt2--;
}
cnt1=cnt2=0;
fr(i,1,m){
if(y[i]>0) pos1[++cnt1]=i;
if(y[i]<0) pos2[++cnt2]=i;
}
while(cnt1&&cnt2){
ll tmp=min(y[pos1[cnt1]],-y[pos2[cnt2]]);
b[1][pos1[cnt1]]+=tmp;
b[1][pos2[cnt2]]-=tmp;
y[pos1[cnt1]]-=tmp;
y[pos2[cnt2]]+=tmp;
if(!y[pos1[cnt1]]) cnt1--;
if(!y[pos2[cnt2]]) cnt2--;
}
}
int main(){
n=read();
m=read();
fr(i,1,n) fr(j,1,m) a[i][j]=read();
fr(i,1,n) fr(j,1,m) if((i+j)&1) a[i][j]=-a[i][j];
fr(i,1,n) fr(j,1,m) x[i]+=a[i][j];
fr(j,1,m) fr(i,1,n) y[j]+=a[i][j];
solve();
fr(i,1,n) fr(j,1,m) if((i+j)&1) b[i][j]=-b[i][j];
fr(i,1,n) fr(j,1,m) ans+=abs(b[i][j]);
writeln(ans);
fr(i,1,n){
fr(j,1,m) writesp(b[i][j]);
enter;
}
}
E - Sequence of Multiples
-
给定 \(a_1\),你要构造一个长度为 \(n\) 的序列 \(a_n\),使得其严格递增且 \(\forall i \in [1,n],i \mid a_i\)。求 \(\displaystyle\sum_{i=1}^{n}{a_i}\) 的最小值,结果对 \(998244353\) 取模。
-
\(1 \leq n \leq 10^{18},1 \leq a_1 \leq 10^{18}\),有不超过 \(10\) 组询问。
先设 \(a_i=p_i\times i\),那么 \(a_i < a_{i+1} \Rightarrow p_{i+1}=p_i-\lceil \frac{p_i}{i+1} \rceil+1\),所以 \(p_i\) 单调不升。
因为 \(p_{i}-p_{i+1}=\lceil \frac{p_i}{i+1} \rceil-1\),所以又有 \(p_{i}-p_{i+1}\) 单调不升。
我们将相同的 \(p_i-p_{i+1}\) 看成一段,假设 \(i \in [s,t],p_i-p_{i+1}=\Delta p\)。由于它是等差数列,那么每一项的 \(p\) 和 \(\displaystyle\sum_{i=s}^{t}{p_i\times i}\) 都可以快速算出来。
别告诉我你不会求和
如何通过 \(s\) 找到 \(t\) 呢?因为 \(p_{t+1}-p_{t+2}<\Delta p\),所以可以推出来 \(t=\lceil \frac{p_s+(s-3)\Delta p}{2\Delta p} \rceil-1\)。于是我们可以在 \(O(1)\) 的时间内找到下一段并算出答案。
同时,通过推导也可以证明出最多有 \(2\lceil \sqrt[3]{a_1} \rceil\) 段。
证明
首先,\(a_i-a_{i-1} \leq i\),否则与 \(a_i\) 最小矛盾。
所以 \(a_n \leq a_1+\frac{(n+2)(n-1)}{2} <a_1+n^2(n \geq 2)\)
\(\Rightarrow p_n < \frac{a_1}{n}+n\)
令 \(n=\sqrt[3]{a_1}\),则 \(p_n < n^2+n\)
因为 \(p_n-p_{n+1}=\lceil \frac{p_n}{n+1} \rceil-1<\lceil \frac{n^2+n}{n+1} \rceil-1 < n\)
所以在 \(n\) 后面最多存在 \(n\) 段。而在 \(n\) 前面最多存在 \(n\) 段,故最多有 \(2n=2\lceil \sqrt[3]{a_1} \rceil\) 段。
时间复杂度为 \(O(\sqrt[3]{a_1})\)。
#include<bits/stdc++.h>
#define ll long long
#define fr(i,x,y) for(register ll i=(x);i<=(y);i++)
#define il inline
#define mod 998244353
using namespace std;
int t,inv2=(mod+1)/2,inv6=(mod+1)/6,ans=0;
ll n,x,tmp1,tmp2,tmp3;
il void calc(ll s,ll t,ll p,ll delta){
if(s>t) return;
if(s==t){
ans=(ans+(p%mod)*(s%mod)%mod)%mod;
return;
}
tmp1=(p+(s%mod)*(delta%mod)%mod)%mod*((s+t)%mod)%mod*((t-s+1)%mod)%mod*inv2%mod;
tmp2=t%mod*((t+1)%mod)%mod*((2*t+1)%mod)%mod;
tmp3=s%mod*((s-1)%mod)%mod*((2*s-1)%mod)%mod;
tmp2-=tmp3;
if(tmp2<0) tmp2+=mod;
tmp2=tmp2*inv6%mod;
tmp2=tmp2*(delta%mod)%mod;
tmp1-=tmp2;
if(tmp1<0) tmp1+=mod;
ans+=tmp1;
if(ans>=mod) ans-=mod;
}
il ll ceill(ll x,ll y){
return (x+y-1)/y;
}
int main(){
scanf("%d",&t);
while(t--){
ans=0;
scanf("%lld %lld",&n,&x);
if(n==1){
printf("%lld\n",x%mod);
continue;
}
ll s=1,delta=ceill(x,2)-1,p=x;
while(s<=n&&delta){
ll t=min(n,ceill(p+(s-3)*delta,2*delta));
calc(s,t,p,delta);
p=p-(t+1-s)*delta;
s=t+1;
delta=ceill(p,s+1)-1;
}
calc(s,n,p,delta);
printf("%d\n",ans);
}
}
F - Delete 1, 4, 7, ...
-
给定一个正整数 \(n\),初始有一个序列 \({1,2,\ldots,n}\),你需要进行 \(k\) 次操作。
-
每次操作,你要将位置为 \(1,4,7,\ldots,3i+1(i \in \mathbb{N})\) 的数删除。求最后剩下的数的和。
-
\(1 \leq n \leq 10^{14},1 \leq k \leq 100\)
设 \(f(i)=\lfloor \frac{3i+1}{2} \rfloor\),那么 \(f(i)\) 其实就是第一次操作后,第 \(i\) 个位置的数。同理,\(k\) 次操作后,位置为 \(i\) 的数就是 \(f(f(\cdots f(f(i)) \cdots))=f^{k}(i)\)。
不妨设 \(cnt_k\) 为 \(k\) 次操作后剩下的数的个数,这个很容易递推出来,那么最后相当于求 \(\displaystyle\sum_{i=1}^{cnt_k}{f^k(i)}\)。如果我们采用暴力递推的方式求出 \(f^k(i)\),那么我们会得到一个“优秀”的复杂度:\(O(k\times cnt_k)\)。
因为我们只需处理出来前 \(cnt_k\) 个数的 \(f\),而 \(cnt_k \leq n\times (\frac{2}{3})^{k}\),所以时间复杂度大约是 \(O(nk(\frac{2}{3})^k)\)。所以这道F就被我们切了
实际上,这种做法只在 \(k\) 很大的时候有用,当 \(k\) 小一点的时候我们要另寻他法。我们现在只找到了 \(f^k\) 与 \(f^{k-1}\) 之间的关系,那 \(f^k(i)\) 与 \(f^k(j)\) 之间有什么关系呢?
有一个直觉上很对的等式:\(f^k(n+2^k)=f^k(n)+3^k\)。
证明
当 \(k=1\) 时,根据题意显然得证。
当 \(k>1\) 时:
由数学归纳法得证。
所以我们只需要处理出前面 \(2^k\) 个 \(f^k\) 值,时间复杂度降到了 \(O(k\times 2^k)\),但还是不够。
考虑将 \(f^k(i)\) 拆成 \(f^x(f^y(i)),x+y=k\),那么 \(f^k(i+2^yj)=f^x(f^y(i+2^yj))=f^x(f^y(i)+3^yj)\)。
枚举 \(i \in [1,2^y]\),那么相当于要算 \(\displaystyle\sum_{j}{f^x(f^y(i)+3^yj)}\),因为 \(i+2^yj \leq cnt_k\),所以 \(j\) 的上界为 \(\lfloor \frac{cnt_k-i}{2^y} \rfloor+1\)(要保证所有数都被算到,而且多出来的部分本来就是 \(0\),不用管)。
如何快速计算?设 \(g(a,i)\) 为 \(\displaystyle\sum_{j=0}^{2^i-1}{f^x(a+3^yj)}\),那么 \(g(a,i)=g(a,i-1)+g(a+3^y2^{i-1},i-1)\),最后 \(g(a,0)=f^x(a)\)。
还有一个小优化,就是当 \(a>2^x\) 的时候,递归到 \(g((a-1)\mod 2^x+1,i)\),最后在答案上面加上剩下的 \(3^x\)。可用记搜优化。
将 \(j\) 的上界 \(\lceil \frac{cnt_k-i}{2^y} \rceil\) 二进制拆分,如果第 \(p\) 位是 \(1\) 就计算对应的 \(g\),然后在原来的 \(a\) 加上 \(3^y2^p\)。原来的 \(a\) 其实就是 \(f^y(i)\)。
所以这部分的时间复杂度大概是 \(O(2^y\log n+2^x k)\),当 \(x=y=\frac{k}{2}\) 时可以做到 \(O((k+\log n)2^{k/2})\)。于是我们在 \(k \leq 40\) 时使用此算法,在 \(k > 40\) 时使用上面的暴力。
#include<bits/stdc++.h>
using namespace std;
using namespace my_std;
#define mod 998244353
ll n,k,cnt[110],X,Y,pw3[110],mp[1100000][51],ans=0;
ll f(ll x,ll k){
fr(i,1,k) x=(3*x+1)/2;
return x;
}
void sub(){
fr(i,1,cnt[k]) ans=(ans+f(i,k)%mod)%mod;
write(ans);
}
il ll ceill(ll x,ll y){
return (x+y-1)/y;
}
ll g(ll a,ll i){
if(a>(1ll<<X)){
ll tmp=((a-1)/(1ll<<X))%mod;
return (g((a-1)%(1ll<<X)+1,i)+tmp*(pw3[X]%mod)%mod*((1ll<<i)%mod)%mod)%mod;
}
if(!i) return f(a,X)%mod;
if(mp[a][i]) return mp[a][i];
return mp[a][i]=(g(a,i-1)+g(a+pw3[Y]*(1ll<<(i-1)),i-1))%mod;
}
int main(){
n=read();
k=read();
cnt[0]=n;
fr(i,1,k) cnt[i]=(2*cnt[i-1])/3;
if(k>40){
sub();
return 0;
}
X=k/2;
Y=k-X;
pw3[0]=1;
fr(i,1,30) pw3[i]=pw3[i-1]*3;
fr(i,1,min(cnt[k],1ll<<Y)){
ll A=f(i,Y),maxx=(cnt[k]-i)/(1ll<<Y)+1;
fr(j,0,50){
if(maxx&(1ll<<j)){
ans=(ans+g(A,j))%mod;
A+=pw3[Y]*(1ll<<j);
}
}
}
write(ans);
}