AtCoder Regular Contest 116 总结
A
判断奇偶因子谁多谁少
容易发现如果质因数分解,有 n 个 2 和 m 个非 2 质因子
则奇数因子有 $2^m$ 个 ,偶数因子有 $2^{n+m-1}$ 个(没有 2 时没有)
可知当 n=0 时奇数多, n=1 时一样, n=2 时偶数多
#include <iostream> #include <cstdio> using namespace std; typedef long long ll; int t; ll n; int main() { for (scanf("%d",&t);t;t--) { scanf("%lld",&n); if (n%2!=0) {printf("Odd\n");continue;} n/=2; if (n%2!=0) {printf("Same\n");continue;} else printf("Even\n"); } }
B
求对于一个序列 A ,它的子序列 B 的 $\sum_B max(B)*min(B)$
将 A 排序,枚举左右端点为 l,r ,则当前答案为 $a[l]*a[r]*2^{r-l-1} (r>l)$ 再加上 $a[l]*a[r] (r=l)$
考虑用后缀和维护 $a[r]*2^{r-1}$ 则枚举左端点 l 时答案为 $a[l]*s[r]*2^{-l} (r>l)$ 再加上 $a[l]*a[r] (r=l)$
时间复杂度 O(n)
#include <iostream> #include <cstdio> #include <algorithm> using namespace std; typedef long long ll; const ll P=998244353; const int N=2e5+10; int n; ll a[N],_2[N],suf[N],ans; ll Pow(ll x,ll y) {ll ans=1;for (;y;y>>=1,x=x*x%P) if (y&1) ans=ans*x%P;return ans;} int main() { scanf("%d",&n); _2[0]=1;for (int i=1;i<=n;i++) scanf("%lld",&a[i]),_2[i]=(_2[i-1]<<1)%P; sort(a+1,a+n+1); for (int i=n;i;i--) suf[i]=(suf[i+1]+a[i]*_2[i-1]%P)%P; for (int i=1;i<=n;i++) (ans+=a[i]*suf[i+1]%P*Pow(_2[i],P-2)%P+a[i]*a[i]%P)%=P; printf("%lld\n",ans); }
C
在一个序列中不同的数至多有 $log_2m$ 个
则设 f[i][j] 表示第 i 个不同的数是 j , 从上一个 j 的因子里转移过来,复杂度是调和级数 O(nlnn)
最后枚举有 i 个不同的数,在 n-1 个空隙里插 i-1 个板即可
#include <iostream> #include <cstdio> #include <cmath> using namespace std; typedef long long ll; const ll P=998244353; const int N=2e5+10; int n,m,lg; ll fact[N],inv[N],f[21][N],ans; ll Pow(ll x,ll y) {ll ans=1;for (;y;y>>=1,x=x*x%P) if (y&1) ans=ans*x%P;return ans;} ll C(int n,int m) {return fact[n]*inv[n-m]%P*inv[m]%P;} int main() { scanf("%d%d",&n,&m);lg=log2(m); fact[0]=1;for (int i=1;i<N;i++) fact[i]=fact[i-1]*i%P; inv[N-1]=Pow(fact[N-1],P-2);for (int i=N-2;i>=0;i--) inv[i]=inv[i+1]*(i+1)%P; for (int i=1;i<=m;i++) f[0][i]=1; for (int i=1;i<=lg;i++) for (int j=1;j<=m;j++) for (int k=2;k*j<=m;k++) (f[i][j*k]+=f[i-1][j])%=P; for (int i=0;i<=min(lg,n-1);i++) for (int j=1;j<=m;j++) (ans+=f[i][j]*C(n-1,i))%=P; printf("%lld\n",ans); }
D
范围很小,做法很暴力
从低位到高位,设 f[i][j] 表示做到第 i 位,前面 i-1 位满足相消为 0 的方案数
O(mlogm)
#include <iostream> #include <cstdio> #include <cmath> using namespace std; typedef long long ll; const ll P=998244353; const int N=5e3+10; int n,m,lg; ll fact[N],inv[N],f[15][N]; ll Pow(ll x,ll y) {ll ans=1;for (;y;y>>=1,x=x*x%P) if (y&1) ans=ans*x%P;return ans;} ll C(int n,int m) {return fact[n]*inv[n-m]%P*inv[m]%P;} int main() { scanf("%d%d",&n,&m);lg=log2(m); if (m&1) return printf("0"),0; fact[0]=1;for (int i=1;i<=n;i++) fact[i]=fact[i-1]*i%P; inv[n]=Pow(fact[n],P-2);for (int i=n-1;i>=0;i--) inv[i]=inv[i+1]*(i+1)%P; f[0][0]=1; for (int i=0,k=1;i<=lg;i++,k<<=1) for (int j=0;j<=m;j++) { for (int l=2;j+k*l<=m&&l<=n;l+=2) (f[i+1][j+k*l]+=f[i][j]*C(n,l)%P)%=P; (f[i+1][j]+=f[i][j])%=P; } printf("%lld\n",f[lg+1][m]); }
在日渐沉没的世界里,我发现了你。