Codeforces Round #729 (Div. 2)题解
Codeforces Round #729 (Div. 2)
A - Odd Set
思路:理解了题意基本上就能做了,模拟一下。
\(Code\)
/* -*- encoding: utf-8 -*-
'''
@File : F.cpp
@Time : 2021/07/02 08:32:34
@Author : puddle_jumper
@Version : 1.0
@Contact : 1194446133@qq.com
'''
# here put the import lib*/
#include<set>
#include<iostream>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<map>
#include<algorithm>
#include<vector>
#include<queue>
#define ch() getchar()
#define pc(x) putchar(x)
#include<stack>
#include<unordered_map>
#define rep(i,a,b) for(auto i=a;i<=b;++i)
#define bep(i,a,b) for(auto i=a;i>=b;--i)
#define lowbit(x) x&(-x)
#define ll long long
#define ull unsigned long long
#define pb push_back
#define mp make_pair
#define PI acos(-1)
using namespace std;
template<typename T>void read(T&x){
static char c;
static int f;
for(c=ch(),f=1; c<'0'||c>'9'; c=ch())if(c=='-')f=-f;
for(x=0; c>='0'&&c<='9'; c=ch())x=x*10+(c&15);
x*=f;
}
template<typename T>void write(T x){
static char q[65];
int cnt=0;
if(x<0)pc('-'),x=-x;
q[++cnt]=x%10,x/=10;
while(x)
q[++cnt]=x%10,x/=10;
while(cnt)pc(q[cnt--]+'0');
}
const int N = 2e5+10;
int _,n;
void solve(){
read(_);
while(_--){
read(n);
int cnt=0;
rep(i,1,2*n){
int x;
read(x);
if(x%2)cnt++;
}
if(cnt == n)puts("Yes");
else puts("No");
}
}
signed main(){solve();return 0; }
B - Plus and Multiply
思路:贪心,我们发现,一个数如果在集合中,那么一定能表示成 \(a^k + pb\)的形式。直接枚举指数 \(k\)
\(Code\)
/* -*- encoding: utf-8 -*-
'''
@File : F.cpp
@Time : 2021/07/02 08:32:34
@Author : puddle_jumper
@Version : 1.0
@Contact : 1194446133@qq.com
'''
# here put the import lib*/
#include<set>
#include<iostream>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<map>
#include<algorithm>
#include<vector>
#include<queue>
#define ch() getchar()
#define pc(x) putchar(x)
#include<stack>
#include<unordered_map>
#define rep(i,a,b) for(auto i=a;i<=b;++i)
#define bep(i,a,b) for(auto i=a;i>=b;--i)
#define lowbit(x) x&(-x)
#define ll long long
#define ull unsigned long long
#define pb push_back
#define mp make_pair
#define PI acos(-1)
using namespace std;
template<typename T>void read(T&x)
{
static char c;
static int f;
for(c=ch(),f=1; c<'0'||c>'9'; c=ch())if(c=='-')f=-f;
for(x=0; c>='0'&&c<='9'; c=ch())x=x*10+(c&15);
x*=f;
}
template<typename T>void write(T x)
{
static char q[65];
int cnt=0;
if(x<0)pc('-'),x=-x;
q[++cnt]=x%10,x/=10;
while(x)
q[++cnt]=x%10,x/=10;
while(cnt)pc(q[cnt--]+'0');
}
const int N = 2e5+10;
const ll mod = 998244353;
int _,n;
map<ll,pair<ll,ll> > S;
vector<ll>G;
map<ll,ll>cnt;
void solve()
{
scanf("%d", &_);
while (_--)
{
ll cnts, x, y;
scanf("%lld%lld%lld", &cnts, &x, &y);
if (x == 1)
{
if (!((cnts - 1) % y))
{
puts("Yes");
}
else puts("No");
}
else
{
bool flag = false;
ll cur = 1ll;
while (cur <= cnts)
{
if ((cnts - cur) % y == 0)
{
flag = true;
break;
}
cur = cur * x;
}
if (flag) puts("Yes");
else puts("No");
}
}
}
signed main()
{
solve();
return 0;
}
C - Strange Function
思路:枚举答案,我们从 \(2\) 开始枚举答案,算一下在范围\([1,n]\)里,答案为 \(2\) 的共有多少个,然后再算答案为 \(3\) 的,答案为 \(4\) 的,现在我们假设算到答案为 \(k\) ,我们看一下如何求出范围在 \([1,n]\) 里且答案为 \(k\) 的数有多少个。设一个从一开始就维护的前缀 \(now\),这个前缀是代表我们算到答案为 \(k\) **这个数的因数中一定要有 \(now\) ** 且 \(now\) 一定是最小的满足条件的那一个。那么我们先假设我们知道了 \(now\) 该怎么计算当前的答案。我们发现 \(cnt1=\lfloor \frac{n}{now} \rfloor\) 代表在 \([1,n]\) 范围里以 \(now\) 为因子的个数,但这并不是答案,因为他可能满足被当前的 \(now\) 整除的前提下,被下一个 \(now\) 也整除,这就会导致答案计算重复,所以我们要减去一个在 \([1,cnt1]\) 中且能被下一个 \(now\) 整除的。我们发现,假如我们设 \(gcd(now,k)=1\)时,那么在 \([1,cnt1]\) 中只要不被 \(k\) 整除就可以了,我们发现在 \(1[1,cnt1]\) 中被 \(k\) 整除的数有 \(cnt2 = \lfloor \frac{cnt1}{k} \rfloor\) 个,所以答案为 \(k\) 的个数就是 \(cnt1-cnt2\) 。那么我们考虑如果 \(gcd(now,k) \neq 1\) 时答案。\(gcd(now,k) \neq 1\) 我们可以将 \(k\) 变为 \(\frac{k}{gcd(now,k)}\) 这样一来,\(gcd\)就变为 \(1\) 了。
还有一个问题是 \(now\) 的转移,和刚才那个操作很像,设当前进行枚举的答案是 \(val\) ,那么 \(now = \frac{now*val}{gcd(now,val)}\) 就是 \(lcm(now,val)\) 。
可以发现枚举答案的话每一次转移是 \(O(1)\) 然后最大答案不超过 \(50\) 的样子。
\(Code:\)
/* -*- encoding: utf-8 -*-
'''
@File : C.cpp
@Time : 2021/07/05 11:00:31
@Author : puddle_jumper
@Version : 1.0
@Contact : 1194446133@qq.com
'''
# here put the import lib*/
#include<set>
#include<iostream>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<map>
#include<algorithm>
#include<vector>
#include<queue>
#define ch() getchar()
#define pc(x) putchar(x)
#include<stack>
#include<unordered_map>
#define rep(i,a,b) for(auto i=a;i<=b;++i)
#define bep(i,a,b) for(auto i=a;i>=b;--i)
#define lowbit(x) x&(-x)
#define ll long long
#define ull unsigned long long
#define pb push_back
#define mp make_pair
#define PI acos(-1)
using namespace std;
template<typename T>void read(T&x){
static char c;
static int f;
for(c=ch(),f=1; c<'0'||c>'9'; c=ch())if(c=='-')f=-f;
for(x=0; c>='0'&&c<='9'; c=ch())x=x*10+(c&15);
x*=f;
}
template<typename T>void write(T x){
static char q[65];
int cnt=0;
if(x<0)pc('-'),x=-x;
q[++cnt]=x%10,x/=10;
while(x)
q[++cnt]=x%10,x/=10;
while(cnt)pc(q[cnt--]+'0');
}
const ll mod = 1e9+7;
int _;
ll x;
ll qpow(ll a,ll b){
ll res = 1ll;
while(b){
if(b&1){res *= a;res %= mod; }
b>>=1;
a*=a;
a%=mod;
}
return res ;
}
ll inv(ll a){
return qpow(a,mod-2);
}
void solve(){
read(_);
while(_--){
read(x);
ll now = 1ll;
ll id = 2ll;
ll ans = 0ll;
// 一个数x,y
// ll cnt = 0ll;
while(now <= x){
ll k = x/now;
ll f = __gcd(now,id);
ll p = id / f;
ll cnt = k - k/p;
//printf("%lld %lld %lld %lld\n",id-1,cnt,now,f);
//cnt += k-k/id;
//k = (k%mod + mod) %mod;
ans += cnt * id;
ans %= mod;
now *= id/__gcd(now,id);
//now *= id;
id++;
//id %= mod;
}
// printf("%lld \n",now);
write(ans);pc('\n');
}
}
signed main(){solve();return 0;}
D - Priority Queue
思路:我们考虑每一个值在全部集合中能出现多少次,对于每一个 \(a_x\) 进行 \(dp\) (如果是 \(“-”\) 那么设当前 \(a_x = -1\)),设 \(f(i,j)\) 代表当前进行到第 \(i\) 个数,且前面有 \(j\) 个比 \(a_x\) 要小的数。考虑转移:
- 如果当前 \(a_i = -1\) 的话,那么如果不选当前的 \("-"\) ,有 \(f(i-1,j)\) 种方案,如果选择当前的\("-"\) 那么有$ f(i-1,j+1)$ 种方案数 ,特别的,如果 \(j = 0\) 且 \(i < x\) 再额外加上一个 \(f(i-1,j)\) ,这个可以这样理解,当 \(j=0\) 且 \(i<x\) 时,我们选择当前的 \("-"\) ,\(f(i-1,j)\) 中包括的方案数中,可以根据空集和非空集将他们分为两种,我们先看空集,如果当前为空集的话,\(”-“\) 并不会影响什么,\(f(i-1,j)\) 的空集部分是可以算进 \(f(i,j)\) 内的,那么我们看如果当前为非空集的话,又因为 \(i<x\) ,所以减掉一个比 \(a_x\) 大的数并没有影响 \(a_x\) 的贡献,所以 \(f(i-1,j)\) 的非空集部分也是可以算进 \(f(i,j)\) 内。这样就是为什么再加一遍 \(f(i-1,j)\) 的原因。
- 如果当前 \(a_i > a_x\),\(f(i,j) = f(i-1,j)*2\) 。分为选和不选两种方案,都是 \(f(i-1,j)\) 种 。
- 如果当前\(a_i<a_x\) ,\(f(i,j) = f(i-1,j-1) + f(i-1,j)\) ,选的话是只需要满足前面有 \(j-1\) 个比他小的数。
- 如果当前\(a_i = a_x\) :
-
- \((1)\) 如果\(i >x\) ,那么将 \(a_i\) 看成比 \(a_x\) 大的数。
- (2)否则将 \(a_i\) 看成比\(a_x\) 小的数。
- 这一步是通过字典序去重,因为如果有多个相同的数,可以取任意一个,我们不妨设定为每次取下标最小的一个,越靠后的数越大。
如果还有什么不懂的地方,看代码:
\(Code:\)
/* -*- encoding: utf-8 -*-
'''
@File : D.cpp
@Time : 2021/07/06 10:09:22
@Author : puddle_jumper
@Version : 1.0
@Contact : 1194446133@qq.com
'''
# here put the import lib*/
#include<set>
#include<iostream>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<map>
#include<algorithm>
#include<vector>
#include<queue>
#define ch() getchar()
#define pc(x) putchar(x)
#include<stack>
#include<unordered_map>
#define rep(i,a,b) for(auto i=a;i<=b;++i)
#define bep(i,a,b) for(auto i=a;i>=b;--i)
#define lowbit(x) x&(-x)
#define ll long long
#define ull unsigned long long
#define pb push_back
#define mp make_pair
#define PI acos(-1)
using namespace std;
template<typename T>void read(T&x){
static char c;
static int f;
for(c=ch(),f=1; c<'0'||c>'9'; c=ch())if(c=='-')f=-f;
for(x=0; c>='0'&&c<='9'; c=ch())x=x*10+(c&15);
x*=f;
}
template<typename T>void write(T x){
static char q[65];
int cnt=0;
if(x<0)pc('-'),x=-x;
q[++cnt]=x%10,x/=10;
while(x)
q[++cnt]=x%10,x/=10;
while(cnt)pc(q[cnt--]+'0');
}
const int N = 5e2+10;
const ll mod = 998244353;
int n;
ll a[N],f[N][N];
ll work(){
ll ans = 0ll;
rep(i,1,n){
if(a[i] == -1)continue;
memset(f,0,sizeof f);
f[0][0] = 1;
rep(p,1,n){
if(p == i){memcpy(f[p],f[p-1],sizeof f[p-1]); continue;}
rep(k,0,p){
if(a[p] > 0){
ll c = 0;if(k-1>=0)c = f[p-1][k-1];
if(a[p] > a[i])f[p][k] = (f[p-1][k]*2ll)%mod;
else if(a[p]<a[i] or p<i)f[p][k] = (c + f[p-1][k])%mod;//选的小,那么能当替死鬼
else f[p][k] = (f[p-1][k]*2ll)%mod;
} else {
f[p][k] = (f[p-1][k+1] + f[p-1][k])%mod;
if(!k and p<i)(f[p][k] += f[p-1][k])%mod;
}
}
}
ll sum = 0ll;
rep(j,0,n)sum = (sum + f[n][j])%mod;
ans += sum * a[i];
ans %= mod;
}
return ans;
}
void solve(){
read(n);
rep(i,1,n){
char op[3];
scanf("%s",op);
if(*op == '-'){
a[i] = -1;
} else {
read(a[i]);
}
}
write(work());pc('\n');
}
signed main(){solve();return 0;}
E1 - Abnormal Permutation Pairs (easy version)
思路:对于字典序的题目,我们可以枚举前面相同的位数来进行 \(dp\)。我们设前 \(x-1\) 位相同。 且设 \(i = n-x\) 。因为前\(x-1\) 位相同,所以前 \(x-1\) 位内部的逆序对数相同,并且前 \(x-1\) 位对于后 \(i+1\) 位来说,逆序对数也相同,那么要保证 \(p\) 串逆序对数大于 \(q\) 串,就说明是后 \(i+1\) 位内部逆序对数 \(p\) 串大于 \(q\) 串 。设 \(u\) 为 \(p\) 串后 \(i\) 位内部的逆序对数, \(v\) 为 \(q\) 串后 \(i\) 位内部的逆序对数。\(s\) 为 \(p\) 串后第 \(i\) 位对于后面 \(i\) 位的逆序对数, \(t\) 为 \(q\) 串后第 \(i\) 位对于后面 \(i\) 位的逆序对数。有 \(u+s>v+t\) 所以 \(t-s < u-v\) ,对于每一组 \(u,v\) 计算符合要求的 \(t-s\) 的个数,则有
设
意为对于 \(i\) 位不同且对于 \(1\) 组 \((u,v)\) 所满足的\((s,t)\) 的方案数。我们设 \(dp(i,j)\) 代表长度为 \(i\) 的排列中,逆序对数为 \(j\) 的方案数。可以通过前缀和 \(O(n^3)\) 计算出来。那么对于每对 \((u,v)\) 满足的方案数就是 \(dp_{i,u}dp_{i,v}f(i,u-v)\) ,那么枚举 \((u,v)\) 的总方案数就是
还得再乘上前面的相同部分:在 \(n\) 位中选择 \(n-i-1\) 位的方案数 \(C^{n-i+1}_{n}\) ,全排列 \(n-i-1\) 位 \((n-i-1)!\) 因此答案是
复杂度 \(O(n^5)\) 考虑到 \(v<u-i\) 时,\(f(i,u-v) = \frac{i(i+1)}{2}\) 那么可以维护 \(dp_{i,j}\) 的前缀和结合 \(f(i,u-v)\) 达到 \(O(n^4)\)
参考dalao的代码
\(Code:\)
/* -*- encoding: utf-8 -*-
'''
@File : E1.cpp
@Time : 2021/07/06 15:40:33
@Author : puddle_jumper
@Version : 1.0
@Contact : 1194446133@qq.com
'''
# here put the import lib*/
#include<set>
#include<iostream>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<map>
#include<algorithm>
#include<vector>
#include<queue>
#define ch() getchar()
#define pc(x) putchar(x)
#include<stack>
#include<unordered_map>
#define rep(i,a,b) for(auto i=a;i<=b;++i)
#define bep(i,a,b) for(auto i=a;i>=b;--i)
#define lowbit(x) x&(-x)
#define ll long long
#define ull unsigned long long
#define pb push_back
#define mp make_pair
#define PI acos(-1)
using namespace std;
template<typename T>void read(T&x){
static char c;
static int f;
for(c=ch(),f=1; c<'0'||c>'9'; c=ch())if(c=='-')f=-f;
for(x=0; c>='0'&&c<='9'; c=ch())x=x*10+(c&15);
x*=f;
}
template<typename T>void write(T x){
static char q[65];
int cnt=0;
if(x<0)pc('-'),x=-x;
q[++cnt]=x%10,x/=10;
while(x)
q[++cnt]=x%10,x/=10;
while(cnt)pc(q[cnt--]+'0');
}
const int N = 55;
#define int long long
int n;ll mod,f[N][N*N];
ll C[N][N],A[N],sum[N][N*N];
void init(){
f[1][0] = 1ll;
rep(i,0ll,N*N)sum[1][i] = 1ll;
for(int i=2ll;i<=n;i++){
for(int j=0ll;j<=i*(i-1)/2;j++){
f[i][j]=sum[i-1][j];
// printf("%d %d %d\n",i,j,f[i][j]);
if(j-i>=0) f[i][j]=(f[i][j]-sum[i-1][j-i]+mod)%mod;
// printf("%d %d %d\n",i,j,f[i][j]);
} sum[i][0]=f[i][0];
for(int j=1;j<=N*N;j++) sum[i][j]=(sum[i][j-1]+f[i][j])%mod;
}
//printf("%lld\n",f[10][5]);
C[0][0] = 1ll;
//C[1][1] = C[1][0] = 1ll;
rep(i,1ll,n){
rep(j,0ll,i){
ll now = 0ll;if(j)now = C[i-1][j-1];
C[i][j] = (C[i-1][j] + now)%mod;
//printf("%lld\n",C[i][j]);
}
}
A[0] =1ll ;
rep(i,1ll,n)A[i] = (A[i-1] * i)%mod;
}
int a[N],b[N];
void solve(){
read(n);read(mod);
init();
//枚举前面有几个一样的
ll ans = 0ll;
rep(idx,0ll,n-1){
// ll idx = n - i;// 后面有idx+1位,
//枚举u,v
ll lin = idx*(idx+1)/2;
ll now = 0ll;
rep(u,1ll,(idx*(idx-1))/2ll){
now += (lin%mod*sum[idx][u-1]%mod)%mod*f[idx][u]%mod;
//printf("%lld %lld %lld\n",idx,u-1,sum[idx][u-1]);
now %= mod;
rep(v,max(0ll,u-idx),u-1){
ll k = ((idx-u+v+1)*(idx-u+v+2)/2%mod*f[idx][v])%mod;
now = ((now - k*f[idx][u])%mod+mod)%mod;
}
}
ans += (now * C[n][n-idx-1]%mod*A[n-idx-1])%mod;
ans %= mod;
// printf("%lld %lld %lld\n",C[n][n-idx-1],A[n-idx-1],);
}
write(ans);pc('\n');
}
signed main(){ solve();return 0;}