B-Distance_2023牛客暑期多校训练营6 (nowcoder.com)
定义 C ( A , B ) C(A,B) C(A,B)为:给定两个集合,每次操作可以选取 A A A或 B B B中的一个元素+1,让 A A A和 B B B相等的最少操作数,若无法实现则为0,现给定集合ST,求 ∑ A ⊆ S ∑ B ⊆ T C ( A , B ) \sum_{A\subseteq S}\sum_{B\subseteq T}C(A,B) ∑A⊆S∑B⊆TC(A,B)。
最小的操作一定是
排序
后一一对应的操作。考虑枚举每一对 ( i , j ) (i,j) (i,j),计算 a b s ( s [ i ] − t [ j ] ) abs(s[i]-t[j]) abs(s[i]−t[j])的贡献,每一对的贡献是当他为集合中的某一个元素时,所有的集合数量*该对的贡献。集合数量为 ∑ k = 1 k = m i n ( i − 1 , j − 1 ) ∑ c = 1 c = m i n ( n − i , n − j ) C ( i − 1 , k ) × C ( j − 1 , k ) × C ( n − i , c ) × C ( n − j , c ) \sum_{k=1}^{k=min(i-1,j-1)}\sum_{c=1}^{c=min(n-i,n-j)}C(i-1,k)\times C(j-1,k)\times C(n-i,c)\times C(n-j,c) ∑k=1k=min(i−1,j−1)∑c=1c=min(n−i,n−j)C(i−1,k)×C(j−1,k)×C(n−i,c)×C(n−j,c),根据范德蒙德卷积 - OI Wiki (oi-wiki.org)推论4,即 ∑ i = 0 m C ( n , i ) × C ( m , i ) = C ( n + m , m ) \sum_{i=0}^m C(n,i)\times C(m,i)=C(n+m,m) i=0∑mC(n,i)×C(m,i)=C(n+m,m)可以化简上式为 C ( i − 1 + j − 1 , m i n ( i − 1 , j − 1 ) ) × C ( n − i + n − j , m i n ( n − i , n − j ) ) C(i-1+j-1,min(i-1,j-1))\times C(n-i+n-j,min(n-i,n-j)) C(i−1+j−1,min(i−1,j−1))×C(n−i+n−j,min(n−i,n−j)),预处理后可 O ( n 2 ) O(n^2) O(n2)求得答案。
#include
using namespace std;
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
//#define int ll
#define pb push_back
#define eb emplace_back
#define m_p make_pair
const int mod = 998244353;
#define mem(a,b) memset(a,b,sizeof a)
#define pii pair<int,int>
#define fi first
#define se second
const int inf = 0x3f3f3f3f;
const int N = 2e3+ 50;
//__builtin_ctzll(x);后导0的个数
//__builtin_popcount计算二进制中1的个数
int s[N],t[N];
int fac[2*N],inv[2*N];
void init(){
fac[0]=fac[1]=inv[0]=inv[1]=1;
for(int i=2;i<=2*N;++i){
fac[i]=1ll*fac[i-1]*i%mod; //阶乘
inv[i]=1ll*inv[mod%i]*(mod-mod/i)%mod; //逆元
}
for(int i=2;i<=2*N;++i){
inv[i]=1ll*inv[i]*inv[i-1]%mod;
}
}
ll C(int a,int b){
if(a<b or a<0 or b<0)
return 0;
return 1ll*fac[a]*inv[b]%mod*inv[a-b]%mod;
}
void work() {
int n;cin>>n;
init();
for(int i=1;i<=n;++i){
cin>>s[i];
}for(int i=1;i<=n;++i){
cin>>t[i];
}
ll ans=0;
for(int i=1;i<=n;++i){
for(int j=1;j<=n;++j){
int x=abs(s[i]-t[j]);
ll res=x%mod*C(i-1+j-1,min(i,j)-1)%mod*C(n-i+n-j,min(n-i,n-j))%mod;
ans=(ans+res)%mod;
}
}
cout<<ans<<'\n';
}
signed main() {
io;
int t=1;
//cin >> t;
while (t--) {
work();
}
return 0;
}
A-Tree_2023牛客暑期多校训练营6 (nowcoder.com)
给定一颗边带权的树,树上每个节点都有一个颜色(0/1),颜色可以通过 c [ i ] c[i] c[i]的代价翻转,两个异色点的贡献是路径上边权最大值,同色点没有贡献,问经过任意翻转后,树的贡献-代价的最大值。
按照边权排序,重构
最大生成树
,对于每对点 ( u , v ) (u,v) (u,v),若异色,贡献值即为当前的边权,能使当前边权的做出贡献的点对数量即uv的子树中相互异色的点相乘
。
d p [ i ] [ j ] dp[i][j] dp[i][j]表示以i为根节点,树中有j个黑色点的最大答案,转移时 d p [ u ] [ j + k ] = m a x ( d p [ u ] [ j ] + d p [ v ] [ k ] + w ∗ ( ( s z [ u ] − j ) ∗ k + j ∗ ( s z [ v ] − k ) ) dp[u][j+k]=max(dp[u][j]+dp[v][k]+w*((sz[u]-j)*k+j*(sz[v]-k)) dp[u][j+k]=max(dp[u][j]+dp[v][k]+w∗((sz[u]−j)∗k+j∗(sz[v]−k)),树上背包 即可,最后遍历最终根节点的黑色点数即可求得答案。
#include
using namespace std;
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
#define int ll
#define pb push_back
#define eb emplace_back
#define m_p make_pair
const int mod = 998244353;
#define mem(a,b) memset(a,b,sizeof a)
#define pii pair<int,int>
#define fi first
#define se second
const int inf = 0x3f3f3f3f;
const int N = 3e3+ 50;
//__builtin_ctzll(x);后导0的个数
//__builtin_popcount计算二进制中1的个数
int a[N],f[N],c[N],sz[N];
ll tmp[N],dp[N][N];//黑色
struct node{
int u,v,w;
}e[N];
int find(int x){
if(f[x]==x)return x;
return f[x]=find(f[x]);
}
void work() {
int n;cin>>n;
for(int i=1;i<=n;++i){
cin>>a[i];f[i]=i;sz[i]=1;
}
for(int i=1;i<=n;++i){
cin>>c[i];
if(a[i]){//黑色
dp[i][0]=-c[i];
}else{
dp[i][1]=-c[i];
}
}
for(int i=1;i<n;++i){
cin>>e[i].u>>e[i].v>>e[i].w;
}
sort(e+1,e+n,[](node &x,node &y)->bool {
return x.w<y.w;
});
for(int i=1;i<n;++i){
int u=find(e[i].u),v=find(e[i].v);
mem(tmp,-0x3f3f3f3f);
for(int j=0;j<=sz[u];++j){
for(int k=0;k<=sz[v];++k){
tmp[j+k]=max(tmp[j+k],dp[u][j]+dp[v][k]+e[i].w*(j*(sz[v]-k)+(sz[u]-j)*k));
}
}
for(int j=0;j<=sz[u]+sz[v];++j){
dp[u][j]=tmp[j];
}
sz[u]+=sz[v];f[v]=u;
}
int root=find(1);
ll ans=0;
for(int i=1;i<=n;++i){
ans=max(ans,dp[root][i]);
}
cout<<ans<<'\n';
}
signed main() {
io;
int t=1;
//cin >> t;
while (t--) {
work();
}
return 0;
}