AtCoder Regular Contest 106 部分題解 A-D
阿新 • • 發佈:2020-10-26
A:給一個N,找出一對數字使得3^x+5^y=N。或者說明無解。
題解:直接列舉x,看N-3^x是不是5的冪次就好了。
#include<bits/stdc++.h> using namespace std; map<long long, int>H; int main(){ long long x; cin>>x; int idx=1; long long t=5; while(t<=x){ H[t]=idx++; t*=5; } long long a=1, b=-1; t=3; for(;t<x;t*=3){ if(H.count(x-t)){ b=H[x-t]; break; } ++a; } if(b!=-1){ cout<<a<<" "<<b; }else{ cout<<"-1"; } return 0; }
B:給你一個無向圖,每個點有點權。每次能把一條邊連線的兩個節點的點權一個加一,一個減一。問你能不能通過一系列操作使得每一個點權到達預設的值。
題解:檢查每一個連通塊的總權值和是不是一樣,一樣的話,我們總是能通過某種方式使得它變化成預設的值。例如通過類似於從葉子開始變化的方式,達到預設值之後就剪去葉子,再開始新的葉子,顯然這種方式是可行的。
#include<bits/stdc++.h> using namespace std; const int N = 200005; typedef long long LL; int a[N], b[N]; int n, m; int f[N], st[N], top; LL sum[N][2]; inline int Find(int x){ return x==f[x]?x:f[x]=Find(f[x]); } int main(){ cin>>n>>m; for(int i=1;i<=n;++i)f[i]=i; 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<=m;++i){ int u, v; cin>>u>>v; int fu=Find(u); int fv=Find(v); if(fu==fv){ continue; }else{ f[fv]=fu; } } for(int i=1;i<=n;++i){ st[++top]=Find(i); } sort(st+1,st+top+1); top=unique(st+1,st+top+1)-st-1; for(int i=1;i<=n;++i){ int fa=lower_bound(st+1,st+1+top,Find(i))-st; sum[fa][0]+=a[i]; sum[fa][1]+=b[i]; } int flag=1; for(int i=1;i<=top;++i){ if(sum[i][0]!=sum[i][1]){ flag=0; break; } } if(flag==1){ cout<<"Yes"; }else{ cout<<"No"; } return 0; }
C:現在A,B各自給定一個計數演算法,現在需要你構造一種方案使得他們的結果相差M。
A:n個區間,按照左端點排序。從左端點最小的開始,如果當前的區間與之前所有所選的區間沒有交,那麼選上這個區間。
B:n個區間,按照右端點排序。從右端點最小的開始,如果當前的區間與之前所有所選的區間沒有交,那麼選上這個區間。
題解:首先,B演算法是正確的。這是一個經典的選取最多無交點區間數的貪心演算法,所以M<0直接白給。M等於0的時候,我們需要構造類似於123123的這種區間。首先可以證明,m>n-2是無解的。m=n,m=n-1會簡單的推出矛盾。現在給出一種m=n-2的構造方案。1334455...212。如果需要減少,那就在區間內334455...的地方進行巢狀,然後注意特判就能過。
#include<bits/stdc++.h>
using namespace std;
int n, m;
int main(){
cin>>n>>m;
if(n==1){
if(m==0){
cout<<"1 2"<<endl;
}else{
cout<<"-1"<<endl;
}
return 0;
}
if(m<0||m>n-2){
cout<<"-1"<<endl;
return 0;
}
if(m==0){
for(int i=1;i<=n;++i){
cout<<i<<" "<<i+n<<endl;
}
}else{
cout<<"1 "<<2*n-1<<endl;
cout<<2*n-2<<" "<<2*n<<endl;
for(int i=0;i<m-1;++i){
cout<<2*i+2<<" "<<2*i+3<<endl;
}
for(int i=0;i<n-m-1;++i){
cout<<i+2*m<<" "<<i+(n+m-1)<<endl;
}
}
return 0;
}
D:對於X=1...K。求下列算式的值。
\[\sum_{i=1}^n\sum_{j=i+1}^{n}(A_iA_j)^X \]題解:推式子:
\[\sum_{i=1}^n\sum_{j=i+1}^{n}(A_iA_j)^X=\frac{1}{2}(\sum_{i=1}^{n}\sum_{j=1}^{n}(A_i+A_j)^X-\sum_{i=1}^{n}(2*A_i)^X) \]其實對於中間的X次冪,可以用二項式進行展開。
\[\sum_{i}\sum_{j}(A_i+A_j)^X=\sum_i\sum_j\sum_{k=0}^{X}C_{X}^{k}A_i^XA_j^X \]更換列舉順序:
\[\sum_i\sum_j\sum_{k=0}^{X}C_{X}^{k}A_i^XA_j^X=\sum_{k=0}^{X}C_{X}^{k}\sum_iA_i^X\sum_jA_j^X \]然後就無了。
#include <bits/stdc++.h>
using namespace std;
const int MOD = 998244353;
const int N = 200005;
int n, k;
int a[N], t[N], t1[N];
int f[305], f1[305];
int C[305][305];
void add(int& x, int y){
x+=y;
if(x>MOD)x-=MOD;
}
void sub(int& x, int y){
x-=y;
if(x<0)x+=MOD;
}
void mult(int& x, int y){
x=1ll*x*y%MOD;
}
int inv2;
int powmod(int x, int y){
int res=1;
while(y){
if(y&1)mult(res,x);
y>>=1;
mult(x,x);
}
return res;
}
int main(){
cin>>n>>k;
for(int i=1;i<=n;++i)cin>>a[i];
f[0]=n;
for(int i=1;i<=n;++i)t[i]=1,t1[i]=1;
for(int i=1;i<=k;++i){
for(int j=1;j<=n;++j){
mult(t[j],a[j]);
mult(t1[j],2*a[j]);
}
for(int j=1;j<=n;++j){
add(f[i],t[j]);
add(f1[i],t1[j]);
}
}
for(int i=0;i<=300;++i){
C[i][0]=C[i][i]=1;
}
for(int i=1;i<=300;++i){
for(int j=1;j<i;++j){
add(C[i][j],C[i-1][j-1]+C[i-1][j]);
}
}
inv2=powmod(2,MOD-2);
for(int x=1;x<=k;++x){
int ans=0;
for(int d=0;d<=x;++d){
int t=1ll*f[d]*f[x-d]%MOD*C[x][d]%MOD;
add(ans,t);
}
sub(ans,f1[x]%MOD);
mult(ans,inv2);
cout<<ans<<endl;
}
return 0;
}