题意
有
n
n
n个套娃,第
i
i
i个套娃的大小为
a
i
a_i
ai。求将这
n
n
n个套娃分成
k
k
k组,且每组排序后相邻两个套娃大小之差不小于
r
r
r的方案数。
分析
看数据范围可以猜到是
n
2
n^2
n2的DP,用
f
[
i
]
[
j
]
f[i][j]
f[i][j]表示前
i
i
i个数分为
j
j
j组的方案数。下面考虑状态转移,假设当前状态为
f
[
i
]
[
j
]
f[i][j]
f[i][j],有两种情况:(1)
a
[
i
]
a[i]
a[i]单独作为一组,从
f
[
i
−
1
]
[
j
−
1
]
f[i-1][j-1]
f[i−1][j−1]转移;(2)
a
[
i
]
a[i]
a[i]和前面某个数一组,两个数之差大于等于
r
r
r,从
f
[
i
−
1
]
[
j
]
f[i-1][j]
f[i−1][j]转移。对于第二种情况,需要预处理能和
a
[
i
]
a[i]
a[i]放在一组的元素的数量。
AC代码
typedef long long ll;
const int mod=998244353;
const int N=5010;
int a[N],L[N],f[N][N]; //L[i]:a[i]左边有多少个数不能和a[i]一组
int main()
{
int T;
cin>>T;
while(T--)
{
int n,k,r;
cin>>n>>k>>r;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++)
{
L[i]=0;
for(int j=i-1;j>=1;j--)
{
if(abs(a[i]-a[j])<r) L[i]++;
}
}
//初始化,在本题不是必须的
for(int i=0;i<=n;i++)
{
for(int j=0;j<=n;j++)
{
f[i][j]=0;
}
}
f[0][0]=1;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=i;j++)
{
f[i][j]=(ll)(f[i-1][j-1]+(ll)f[i-1][j]*(j-L[i])%mod)%mod;
}
}
cout<<f[n][k]<<endl;
}
return 0;
}
题意
给定含有
n
n
n个点的完全图,边权为两个点的
g
c
d
gcd
gcd。有
q
q
q次询问,对于每次询问,求两点之间的最短路的长度以及最短路的条数。
分析
如果两个点
x
x
x和
y
y
y的
g
c
d
gcd
gcd为1,从
x
x
x直接到
y
y
y路径最短,最短路的长度为1,条数为1。如果
g
c
d
gcd
gcd不为1,可以先让
x
x
x走到与
x
x
x和
y
y
y都互质的点,再走到
y
y
y,最短路长度不超过2,直接从
x
x
x到
y
y
y的路径长度大于等于2,因此最短路径长度为2。对于
x
x
x和
y
y
y不互质的情况,有两种走法,
x
x
x走到和两个数互质的点,再走到
y
y
y,或者
x
x
x直接走到
y
y
y,此时有
g
c
d
(
x
,
y
)
=
2
gcd(x,y)=2
gcd(x,y)=2。对于第二种情况特判计科,对于第一种情况,可以用容斥原理,因为两个数含有的不同质因数最多只有12个。时间复杂度
O
(
q
∗
2
k
)
O(q*2^k)
O(q∗2k)(
k
k
k是两个数含有的不同质因数的个数)。
AC代码
typedef long long ll;
const int N=1e7+10;
int vis[N],prime[N];
int a[105];
int n,q,ans,cnt,k;
void dfs(int x,int sum,int c)
{
if(x==cnt+1)
{
if(c&1) ans-=n/sum;
else ans+=n/sum;
return ;
}
dfs(x+1,sum,c);
if((ll)sum*a[x]<=n) dfs(x+1,sum*a[x],c+1);
}
void get_primes(int n)
{
for(int i=2;i<=n;i++) {
if(vis[i]==0) { vis[i]=i; prime[++k]=i; }
for(int j=1;j<=k;j++) {
if(prime[j]>vis[i]||prime[j]>n/i) break;
vis[i*prime[j]]=prime[j];
}
}
}
int main()
{
cin>>n>>q;
get_primes(10000000);
while(q--)
{
int x,y;
cin>>x>>y;
if(__gcd(x,y)==1)
{
cout<<1<<" "<<1<<endl;
}
else
{
bool flag=false;
if(__gcd(x,y)==2) flag=true;
set<int> s;
while(x>1)
{
s.insert(vis[x]);
int z=vis[x];
while(x%z==0) x/=z;
}
while(y>1)
{
s.insert(vis[y]);
int z=vis[y];
while(y%z==0) y/=z;
}
cnt=0;
for(auto x:s) a[++cnt]=x;
ans=0;
dfs(1,1,0);
if(flag) ans++;
cout<<2<<" "<<ans<<endl;
}
}
return 0;
}
题意
有
n
n
n个数,每次随机取出两个数
a
a
a和
b
b
b,再放入
a
∗
b
+
a
+
b
a*b+a+b
a∗b+a+b,直到只剩下一个数,求最终得到的数的期望。
分析
最终得到的数和选择顺序无关,直接模拟即可。时间复杂度
O
(
n
)
O(n)
O(n)。
AC代码
int main()
{
int T;
cin>>T;
while(T--)
{
int n;
cin>>n;
vector<ll> v(n);
for(int i=0;i<n;i++) cin>>v[i];
while(v.size()>=2)
{
ll x=v.back();
v.pop_back();
ll y=v.back();
v.pop_back();
v.push_back((x+y+x*y%mod)%mod);
}
cout<<v[0]<<endl;
}
return 0;
}