AtCoder Regular Contest 106 部分题解 A-D
A:给一个N,找出一对数字使得3x+5y=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;
}