Codeforces 1734 / Codeforces Round #822 (Div. 2)
Contest Link
Codeforces Round #822 (Div. 2)
Problem A Select Three Sticks
题意
给定 \(n\) 根木棍,第 \(i\) 根长度为 \(a_i\)。每次操作可以选一根木棍使得其长度增加 \(1\) 或减少 \(1\),但不能变成 \(0\) 及以下。
问至少要多少次操作才能得到一个等边三角形。
多组数据,数据组数 \(1 \leq T \leq 100\),\(3 \leq n,\sum n \leq 300\)
题解
注意到肯定排序后取相邻的 \(3\) 个。
Problem B Bright, Nice, Brilliant
题意
给定 \(n\) 行的格子,第 \(i\) 行有 \(i\) 个格子。称格子 \(A\) 能到格子 \(B\),当且仅当 \(A\) 可以通过往右或者往右下方走若干步(可以为 \(0\))到达格子 \(B\)。
你现在需要将某些格子加个 buff。记 \(f(A)\) 表示被加了 buff 并能到达 \(A\) 的格子的数量。你需要使得每一行的格子的 \(f\) 值完全相同。
多组数据,数据组数 \(1 \leq T \leq 100\),\(1 \leq n,\sum n \leq 500\)
题解
把每一行第一个和最后一个格子加上 buff 即可。
容易证明这是上界。
Problem C Removing Smallest Multiples
题意
给定 \(S=\{1,2,\cdots,n\}\) 和它的子集 \(T\)。可以花费 \(x\) 的代价删除 \(S\) 内 \(x\) 的最小倍数,问至少要花费多少代价才能把 \(S\) 变成 \(T\)。
多组数据,数据组数 \(1 \leq T \leq 10^4\),\(1 \leq n,\sum n \leq 10^6\)
题解
考虑从小到大枚举 \(x\),然后看 \(x\) 能删掉哪些。肯定只能挨着删 \(x,2x,\cdots\),如果中间某一个元素在 \(T\) 中了就 break。
注意区分一下,如果 \(x\) 的某个倍数曾经被删过,但不在 \(T\) 中,这是 ok 的可以接着删。
int main(void){
int T=read();
while(T--){
n=read();
scanf("%s",s+1);
long long ans=0;
for(int i=1;i<=n;++i){
if(s[i]=='1') continue;
if(s[i]=='0') ans+=i,s[i]='1';
for(int j=2*i;j<=n;j+=i) if(s[j]=='1') break; else if(s[j]) ans+=i,s[j]=0;
}
printf("%lld\n",ans);
}
return 0;
}
Problem D Slime Escape
题意
给定长度为 \(n\) 的数组 \(a_1,a_2,\cdots,a_n\)(元素可以为负)和起点 \(s\),每经过一个位置你的健康值就会加上那个位置的权值,然后该位置权值变为 \(0\)。问能否在健康值非负的情况下到达 \(0\) 或者 \(n+1\)。
数据组数 \(1 \leq T \leq 10^4\),\(1 \leq n,\sum n \leq 10^6\)
题解
我们规定一个初始方向。肯定是贪心地往前走,能走出去就行,如果不能走出去,设当前在 \(c\) 最远能走到 \(d\),那么我们在 \([c,d]\) 中选一个位置停下使得健康值最大,然后换一个方向继续。
选一种你喜欢的数据结构进行模拟即可。
# include <bits/stdc++.h>
# define int long long
const int N=200010,INF=0x3f3f3f3f;
typedef long long ll;
ll pre[N],suf[N];
int a[N];
int T,n,k;
int logt[N];
struct STable{
ll STmin[N][21],STmax[N][21],len;
int mpos[N][21];
inline void init(int siz,ll a[]){
len=siz;
for(int i=1;i<=siz;++i) STmin[i][0]=STmax[i][0]=a[i],mpos[i][0]=i;
for(int j=1;(1<<j)<=siz;++j){
for(int i=1;i+(1<<j)-1<=siz;++i){
STmin[i][j]=std::min(STmin[i][j-1],STmin[i+(1<<(j-1))][j-1]);
int lv=STmax[i][j-1],rv=STmax[i+(1<<(j-1))][j-1];
if(lv>rv) STmax[i][j]=lv,mpos[i][j]=mpos[i][j-1];
else STmax[i][j]=rv,mpos[i][j]=mpos[i+(1<<(j-1))][j-1];
}
}
return;
}
inline int qmax(int l,int r){
int len=logt[r-l+1];
return (STmax[l][len]>STmax[r-(1<<len)+1][len])?mpos[l][len]:mpos[r-(1<<len)+1][len];
}
inline int qmin(int l,int r){
int len=logt[r-l+1];
return std::min(STmin[l][len],STmin[r-(1<<len)+1][len]);
}
}Q[2];
inline int read(void){
int res,f=1;
char c;
while((c=getchar())<'0'||c>'9')
if(c=='-')f=-1;
res=c-48;
while((c=getchar())>='0'&&c<='9')
res=res*10+c-48;
return res*f;
}
signed main(void){
for(int i=2;i<=N-5;++i) logt[i]=logt[i>>1]+1;
T=read();
while(T--){
n=read(),k=read(),suf[n+1]=0;
for(int i=1;i<=n;++i) a[i]=read();
for(int i=1;i<=n;++i) pre[i]=pre[i-1]+a[i];
for(int i=n;i;--i) suf[i]=suf[i+1]+a[i];
Q[0].init(n,pre),Q[1].init(n,suf);
int L=k,R=k,cur=a[k];
for(int i=1;i<=n+10;++i){
int l=1,r=L-1,ans=L;
while(l<=r){
int mid=(l+r)>>1;
if(Q[1].qmin(mid,L-1)-suf[L]+cur>=0) ans=mid,r=mid-1;
else l=mid+1;
}
if(ans==1){
puts("YES");
goto End;
}
int pos=Q[1].qmax(ans,L);
cur+=suf[pos]-suf[L],L=pos;
l=R+1,r=n,ans=R;
while(l<=r){
int mid=(l+r)>>1;
if(Q[0].qmin(R+1,mid)-pre[R]+cur>=0) ans=mid,l=mid+1;
else r=mid-1;
}
if(ans==n){
puts("YES");
goto End;
}
pos=Q[0].qmax(R,ans);
cur+=pre[pos]-pre[R],R=pos;
}
L=k,R=k,cur=a[k];
for(int i=1;i<=n+10;++i){
int l=R+1,r=n,ans=R;
while(l<=r){
int mid=(l+r)>>1;
if(Q[0].qmin(R+1,mid)-pre[R]+cur>=0) ans=mid,l=mid+1;
else r=mid-1;
}
if(ans==n){
puts("YES");
goto End;
}
int pos=Q[0].qmax(R,ans);
cur+=pre[pos]-pre[R],R=pos;
l=1,r=L-1,ans=L;
while(l<=r){
int mid=(l+r)>>1;
if(Q[1].qmin(mid,L-1)-suf[L]+cur>=0) ans=mid,r=mid-1;
else l=mid+1;
}
if(ans==1){
puts("YES");
goto End;
}
pos=Q[1].qmax(ans,L);
cur+=suf[pos]-suf[L],L=pos;
}
puts("NO");
End:;
}
return 0;
}
Problem E Rectangular Congruence
题意
给定 \(n\) 和长度为 \(n\) 的序列 \(b\),你需要构造一个 \(n \times n\) 的矩阵 \(a\),满足以下条件:
- \(0 \leq a_{i,j} <n\)
- \(a_{i,i}=b_i\)
- 对于 \(r_1<r_2,c_1<c_2\),有 \(a_{r_1,c_1}+a_{r_2,c_2} \not \equiv a_{r_1,c_2}+a_{r_2,c_1} \pmod n\)
\(2 \leq n < 350\)
题解
首先注意到,往某一行和某一列上加一个值不影响限制 \(3\),于是我们可以忽略掉限制 \(2\),等构造完了再往某几行加一个值使得对角线满足限制。
事实上,\(a_{i,j}=ij\) 就是一种合法的构造。对于 \(r_1<r_2,c_1<c_2\),有
证毕。
更为一般地,\(i^2,j^2,ij,i,j,1\) 的任意线性组合都是一组合法解。我们可以往对应的行或列加上或减去若干个 \(i^2,j^2,i,j,1\) 使得这组构造变成 \(a_{i,j}=ij\)。
int main(void){
n=read();
for(int i=1;i<=n;++i) b[i]=read();
for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) a[i][j]=i*j%n;
for(int i=1;i<=n;++i){
if(a[i][i]!=b[i]){
int det=b[i]-a[i][i];
for(int j=1;j<=n;++j) a[i][j]=(a[i][j]+det+n)%n;
}
}
for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) printf("%d%c",a[i][j]," \n"[j==n]);
return 0;
}
Problem F Zeros and Ones
题意
有一个无限长的 01 序列 \(S\),按照如下方式构造:
- 初始 \(S=\texttt 0\)
- 每次操作将 \(S\) 按位取反后写在 \(S\) 后面。前几次操作:\(\newcommand\t[1]{\texttt{#1}} \t 0 \to \t{01} \to \t{0110} \to \t{01101001}\to \cdots\)
现在给出 \(n,m\),问 \(S[0,m-1]\) 和 \(S[n,n+m-1]\) 有多少位不同。
多组数据,数据组数 \(1 \leq T \leq 100\),\(1 \leq n,m \leq 10^{18}\)
题解
注意到 \(S\) 的意义。\(S_i\) 表示 \(i\) 二进制位 \(1\) 的个数的奇偶性。注意到 \(T\) 很小,说明单组数据不一定是 \(O( \log n)\) 或者 \(O(1)\)。
考虑数位 DP。我们应该计算 \(x(0 \leq x < m)\) 和 \(x+n\) 在二进制下 \(1\) 个数的奇偶性不同的 \(x\) 的数量,也即:\(\operatorname{popcount}(x)+\operatorname{popcount}(x+n)\) 是奇数的 \(x\) 的数量。按照数位 DP 的惯例,我们需要知道当前处在的位数 \(i\),并且要知道当前是否顶到了上界。同时,我们要记录当前的 \(\operatorname{popcount}\) 的奇偶性。因为有 \(x+n\) 这样的存在,所以我们要处理进位,故还需要记录当前 \(x+n\) 中连续的 \(1\) 的数量。
设 \(\newcommand\op[1]{\operatorname {#1}}f(i,\op{up},\op{sum},\op{ones})\) 表示上述状态。枚举当前位可以选的值,设当前位值为 \(a\),\(n\) 的对应位值为 \(b\),\(\operatorname{nup}\) 表示这一位选完之后的 \(\operatorname{up}\)。下面的 \(\operatorname{sum}\) 默认对 \(2\) 取模。
- \(a=0,b=0\):则 \(\newcommand\op[1]{\operatorname {#1}}f(i,\op{up},\op{sum},\op{ones})\) 转移到 \(\newcommand\op[1]{\operatorname {#1}}f(i-1,\op{nup},\op{sum},\op{0})\)
- \(a=0,b=1\):则 \(\newcommand\op[1]{\operatorname {#1}}f(i,\op{up},\op{sum},\op{ones})\) 转移到 \(\newcommand\op[1]{\operatorname {#1}}f(i-1,\op{nup},\op{sum}+1,\op{ones}+1)\)
- \(a=1,b=0\):则 \(\newcommand\op[1]{\operatorname {#1}}f(i,\op{up},\op{sum},\op{ones})\) 转移到 \(\newcommand\op[1]{\operatorname {#1}}f(i-1,\op{nup},\op{sum}+2,\op{ones}+1)\)
- \(a=1,b=1\):则 \(\newcommand\op[1]{\operatorname {#1}}f(i,\op{up},\op{sum},\op{ones})\) 转移到 \(\newcommand\op[1]{\operatorname {#1}}f(i-1,\op{nup},\op{sum}-\op{ones}+1+1,\op{ones})\),此时进位了,\(x+n\) 中连续的 \(1\) 变为了 \(1\) 个,故减少了 \(\operatorname{ones}-1\) 个 \(1\)。同时 \(x\) 也多了一个 \(1\)。
边界:\(i=0\) 时,如果 \(\operatorname{up} = 0,\operatorname{sum}=1\) 则返回 \(1\),否则返回 \(0\)。
# include <bits/stdc++.h>
const int N=70,INF=0x3f3f3f3f;
typedef long long ll;
ll dp[N][N][2][2];// dp[i][j][0/1][0/1]: 高 i 位有 j 个尾随 1,和的奇偶性,是否顶上界 的方案数
int mp[N],np[N];
const int MAX=62;
inline ll read(void){
ll res,f=1;
char c;
while((c=getchar())<'0'||c>'9')
if(c=='-')f=-1;
res=c-48;
while((c=getchar())>='0'&&c<='9')
res=res*10+c-48;
return res*f;
}
ll n,m;
inline void par(void){
for(int i=MAX;i>=0;--i) mp[i]=((m>>i)&1),np[i]=((n>>i)&1);
return;
}
ll dfs(int i,int tra,int sum,bool u){
if(i<0){
return (sum&&!u);
}
ll &cur=dp[i][tra][sum][u];
if(~cur) return cur;
cur=0;
for(int v=0;v<=1;++v){
if(u&&v>mp[i]) break;
int s=np[i]+v;
bool nu=u&&(v==mp[i]);
if(s==0) cur+=dfs(i-1,0,sum,nu);
if(s==1){
if(np[i]) cur+=dfs(i-1,tra+1,1-sum,nu);
else cur+=dfs(i-1,tra+1,sum,nu);
}
if(s==2) cur+=dfs(i-1,0,(sum+tra)%2,nu); // = (sum - tra + 1 + 1) % 2
}
return cur;
}
signed main(void){
int T=read();
while(T--){
n=read(),m=read();
par(),memset(dp,-1,sizeof(dp)),printf("%lld\n",dfs(MAX,0,0,true));
}
return 0;
}