Codeforces Round 976(Div 2) 題解 (A-E)
A
題意
給你兩個整數 \(n\) 和 \(k\) 。
在一次操作中,你可以從 \(n\) 中減去 \(k\) 的任意次冪。形式上,在一個操作中,你可以用 \((n-k^x)\) 替換任何非負整數 \(x\) 的 \(n\) 。
求使 \(n\) 等於 \(0\) 所需的最小運算次數。
Solution
將 \(n\) 轉為 \(k\) 進位制,答案即為每位的數的和。時間複雜度 \(O(\sum logn)\) 。
#include <bits/stdc++.h>
#define LL long long
#define mod 1000000007
#define lowbit(x) (x&(-x))
void write(LL x){
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10+48);
}
LL read(){
LL x=0,f=1;char ch=getchar();
while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0' && ch<='9'){x=(x<<3)+(x<<1)+ch-48;ch=getchar();}
return x*f;
}
using namespace std;
const int N=60;
int n,k,a[N],cnt;
void solve(){
n=read(),k=read(),cnt=0;
if(k==1){
write(n),puts("");
return;
}
while(n){
a[++cnt]=n%k;
n/=k;
}
LL ans=0;
for(int i=1;i<=cnt;++i){
ans+=a[i];
}
write(ans),puts("");
}
int main(){
int T=read();
while(T--){
solve();
}
return 0;
}
B
題意
假設你有編號為 \(1, 2, \ldots, n\) 的 \(n\) 燈泡。最初,所有的燈泡都是亮著的。翻轉燈泡的狀態意味著如果它以前是開著的,就把它關掉,否則就把它開啟。
接下來,執行以下操作:
- 對於每個 \(i = 1, 2, \ldots, n\) ,翻轉所有 \(j\) 的燈泡狀態,使 \(j\) 能被 \(i^\dagger\) 整除。
在完成所有操作後,將有幾個燈泡仍然亮著。您的目標是使這個數字恰好為 \(k\) 。
找到最小的合適的 \(n\) ,使得在執行操作後恰好有 \(k\) 個燈泡亮著。我們可以證明一個答案總是存在的。
\(^\dagger\) 一個整數 \(x\) 能被 \(y\) 整除,如果存在一個整數 \(z\) 能被 \(x = y\cdot z\) 整除。
Solution
第 \(i\) 個燈會被操作 \(i\) 的因數個數次。第 \(i\) 個燈操作之後仍是亮著的,當且僅當 \(i\) 是完全平方數。二分即可。
時間複雜度 \(O(TlogV)\) 。
#include <bits/stdc++.h>
#define LL long long
#define mod 1000000007
#define lowbit(x) (x&(-x))
void write(LL x){
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10+48);
}
LL read(){
LL x=0,f=1;char ch=getchar();
while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0' && ch<='9'){x=(x<<3)+(x<<1)+ch-48;ch=getchar();}
return x*f;
}
using namespace std;
LL k;
void solve(){
k=read();
LL L=2,R=2e18,res;
auto chk=[&](LL x)->bool{
return x-(LL)sqrtl(x)>=k;
};
while(L<=R){
LL mid=L+R>>1;
if(chk(mid)) R=mid-1,res=mid;
else L=mid+1;
}
write(res),puts("");
}
int main(){
int T=read();
while(T--) solve();
return 0;
}
C
題意
給定三個非負整數 \(b\) , \(c\) 和 \(d\) 。
請找到一個非負整數 \(a \in [0, 2^{61}]\) ,使得 \((a | b)-(a \& c)=d\) ,其中 \(|\) 和 \(\&\) 分別表示位或操作和位與操作。
如果存在 \(a\) ,則列印其值。如果沒有解,則列印單個整數 \(-1\) 。如果有多個解決方案,列印其中任何一個。
Solution
將 \(a,b,c\) 轉為二進位制,分類討論每一位上的情況。時間複雜度 \(O(TlogV)\) 。
#include <bits/stdc++.h>
#define LL long long
#define mod 1000000007
#define lowbit(x) (x&(-x))
void write(LL x){
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10+48);
}
LL read(){
LL x=0,f=1;char ch=getchar();
while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0' && ch<='9'){x=(x<<3)+(x<<1)+ch-48;ch=getchar();}
return x*f;
}
using namespace std;
const int N=65;
LL a;
void solve(){
LL x=read(),y=read(),z=read();
auto cal=[&](LL x)->vector<int>{
vector<int> ans(N);
int cnt=0;
while(x){
ans[++cnt]=x%2;
x>>=1;
}
return ans;
};
vector<int> b=cal(x);
vector<int> c=cal(y);
vector<int> d=cal(z);
for(int i=64;i>=1;--i){
LL opt;
if(d[i]==0){
if(b[i]==0 && c[i]==0) opt=0;
else if(b[i]==1 && c[i]==1) opt=1;
else if(b[i]==0 && c[i]==1) opt=0;
else{
puts("-1");
return;
}
}
else{
if(b[i]==0 && c[i]==0) opt=1;
else if(b[i]==1 && c[i]==1) opt=0;
else if(b[i]==1 && c[i]==0) opt=1;
else{
puts("-1");
return;
}
}
a=(a<<1)+opt;
}
write(a),puts("");
}
int main(){
int T=read();
while(T--) solve();
return 0;
}
D
題意
一個晴朗的晚上,愛麗絲坐下來玩經典的遊戲“串連點”,但有一個轉折。
為了玩這個遊戲,愛麗絲畫了一條直線,並在上面標記了 \(n\) 點,索引從 \(1\) 到 \(n\) 。最初,點之間沒有弧,所以它們都是不相交的。之後,Alice執行如下型別的 \(m\) 操作:
- 她選擇了三個整數 \(a_i\) , \(d_ i\) ( \(1 \le d _ i \le 10\) )和 \(k _ i\) 。
- 她選擇點 \(a _ i, a _ i+d _ i, a _ {i}+2d_ i, a _ i+3d _ i, \ldots, a _ i+k _ i\cdot d _ i\) 並將這些點的每一對用弧連線起來。
在完成所有 \(m\) 操作後,她想知道這些點形成的 \(\dagger\) 連線元件的個數。請幫她找到這個號碼。
\(\dagger\) 如果兩點之間有一條透過若干(可能為零)弧線和其他點的路徑,則稱兩點在一個連通分量中。
Solution
注意到 \(d_i\) 很小,因此對於一個點,它只可能被它前面的 \(10\) 個點內的點連線,用並查集維護。時間複雜度 \(O(\sum nlogn)\) 。
#include <bits/stdc++.h>
#define LL long long
#define mod 1000000007
#define lowbit(x) (x&(-x))
void write(LL x){
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10+48);
}
LL read(){
LL x=0,f=1;char ch=getchar();
while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0' && ch<='9'){x=(x<<3)+(x<<1)+ch-48;ch=getchar();}
return x*f;
}
using namespace std;
const int N=2e5+5;
int n,m,a,d,p,cnt[N][11],fa[N];
int find(int x){
return fa[x]==x?x:fa[x]=find(fa[x]);
}
void solve(){
n=read(),m=read();
for(int i=1;i<=n;++i){
fa[i]=i;
}
for(int i=1;i<=m;++i){
a=read(),d=read(),p=read();
cnt[a][d]=max(cnt[a][d],p);
}
auto merge=[&](int x,int y)->void{
x=find(x),y=find(y);
if(x==y) return;
fa[x]=y;
};
for(int i=1;i<=n;++i){
for(int j=max(1,i-10);j<i;++j){
int k=i-j;
if(!cnt[j][k]) continue;
cnt[i][k]=max(cnt[i][k],cnt[j][k]-1);
merge(i,j);
}
}
int ans=0;
for(int i=1;i<=n;++i){
if(find(i)==i) ans++;
}
write(ans),puts("");
for(int i=1;i<=n;++i){
for(int j=1;j<=10;++j){
cnt[i][j]=0;
}
}
}
int main(){
int T=read();
while(T--) solve();
return 0;
}
E
Solution
考慮 \(dp_{i,j}\) 表示前 \(i\) 個數能構成的集合且其中元素的異或和為 \(j\) 能發生的機率。
答案為
時間複雜度 \(O(\sum 1024n)\) 。
小心寫得不好導致爆long long;多測記得清空。
#include <bits/stdc++.h>
#define LL long long
#define mod 1000000007
#define lowbit(x) (x&(-x))
void write(LL x){
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10+48);
}
LL read(){
LL x=0,f=1;char ch=getchar();
while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0' && ch<='9'){x=(x<<3)+(x<<1)+ch-48;ch=getchar();}
return x*f;
}
using namespace std;
const int N=2e5+5;
LL n,a[N],p[N],inv,dp[2][1050];
LL qpow(LL x,LL power){
LL ans=1;
x%=mod;
while(power){
if(power&1) ans=ans*x%mod;
power>>=1;
x=x*x%mod;
}
return ans;
}
void solve(){
n=read();
for(int i=1;i<=n;++i){
a[i]=read();
}
inv=qpow(10000,mod-2);
for(int i=1;i<=n;++i){
p[i]=read()*inv%mod;
}
memset(dp,0,sizeof(dp));
dp[1][a[1]]=p[1];
dp[1][0]=(1-p[1]+mod)%mod;
for(int i=1;i<n;++i){
for(int j=0;j<1024;++j){
dp[(i+1)&1][j^a[i+1]]=(dp[(i+1)&1][j^a[i+1]]+1ll*dp[i&1][j]*p[i+1]%mod)%mod;
dp[(i+1)&1][j]=(dp[(i+1)&1][j]+1ll*dp[i&1][j]*(1-p[i+1]+mod)%mod)%mod;
}
for(int j=0;j<1024;++j) dp[i&1][j]=0;
}
LL ans=0;
for(int i=0;i<1024;++i){
ans=(ans+1ll*dp[n&1][i]*i%mod*i%mod)%mod;
}
write(ans),puts("");
}
int main(){
int T=read();
while(T--) solve();
return 0;
}