CodeTON Round 1 (Div. 1 + Div. 2, Rated)
F.Parametric MST
题目描述
解法
现在菜到连 \(2600\) 的题目都做不起了,难受。
首先考虑如何判断答案为 INF
,考虑所有方案对应 \(t\) 的系数,如果系数只有正或者只有负,那么一定可以通过设置 \(t\) 使得答案为 INF
接着考虑固定 \(t\) 之后如何计算答案,我们可以把代价改写成 \((a_i+t)(a_j+t)-t^2\),减去的项是常数,所以我们令 \(a_i\leftarrow a_i+t\),那么此时的 \(a\) 数组一定有正有负。我们把 \(a\) 从小到大排序,然后考虑贪心,也就是把所有正数和 \(a_1\) 相连,把所有负数和 \(a_n\) 相连。
那么问题变成了确定 \(t\),有一个关键的 \(\tt observation\) 是:可能的 \(t\) 的取值是 \(a_1,a_2...a_n\),这是因为首先 \(t\) 的取值范围在 \([-a_n,-a_1]\)(要保证有正有负),然后如果 \(t\) 不取端点值,由于此时的方案是固定的,那么调整到临近的端点值一定更优,时间复杂度 \(O(n\log n)\)
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
const int M = 200005;
#define int long long
int read()
{
int x=0,f=1;char c;
while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
int T,n,ans,a[M],b[M];
int work(int x,int t)
{
return (a[1]+t)*(b[n-1]-b[x]+t*(n-x-1))
+(a[n]+t)*(b[x]+t*x)-t*t*(n-1);
}
void work()
{
n=read();ans=-1e18;
for(int i=1;i<=n;i++)
a[i]=read();
sort(a+1,a+1+n);
for(int i=1;i<=n;i++)
b[i]=b[i-1]+a[i];
if(a[1]*(n-2)+b[n]>0 || a[n]*(n-2)+b[n]<0)
{
puts("INF");
return ;
}
for(int i=1;i<n;i++)
ans=max(ans,work(i,-a[i]));
printf("%lld\n",ans);
}
signed main()
{
T=read();
while(T--) work();
}
H.Equal LCM Subsets
题目描述
有一个大小为 \(n\) 的集合 \(A\),有一个大小为 \(m\) 的集合 \(B\),构造 \(A\) 的子集和 \(B\) 的子集,使得它们的 \(\tt lcm\) 相等。
\(n,m\leq 1000\),\(a_i,b_i\leq 4\cdot 10^{36}\)
解法
考虑把问题变成:从 \(A\) 和 \(B\) 中删除一些数,使得剩下的数 \(\tt lcm\) 相等。
考虑如果一个数需要被删除的条件,就是它的某个质数次幂大于另一个集合中对应质数次幂的最大值,由于本题不支持质因数分解,所以我们转化成下列形式:
那么对于每一个位置都开一棵线段树维护 \(\gcd\),这样就可以在删除的时候支持修改了,时间复杂度 \(O(n^2\log n\log a)\)
#include <cstdio>
#include <iostream>
#include <queue>
using namespace std;
#define int __int128
const int M = 1005;
int read()
{
int x=0,f=1;char c;
while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
void write(int x)
{
if(x>=10) write(x/10);
putchar(x%10+'0');
}
int T,n,m,a[2][M],b[2][M],c[2],d[2];
int gcd(int a,int b) {return !b?a:gcd(b,a%b);}
struct node {int x,y;};queue<node> q;
struct tree
{
int t[M<<2];
void build(int i,int l,int r,int f,int x)
{
if(l==r) {t[i]=x/gcd(a[f][l],x);return ;}
int mid=(l+r)>>1;
build(i<<1,l,mid,f,x);
build(i<<1|1,mid+1,r,f,x);
t[i]=gcd(t[i<<1],t[i<<1|1]);
}
void rem(int i,int l,int r,int x)
{
if(l==r) {t[i]=0;return ;}
int mid=(l+r)>>1;
if(mid>=x) rem(i<<1,l,mid,x);
else rem(i<<1|1,mid+1,r,x);
t[i]=gcd(t[i<<1],t[i<<1|1]);
}
int ask() {return t[1]>1;}
}f[2][M];
void work()
{
n=read();m=read();
c[0]=n;c[1]=m;d[0]=d[1]=0;
for(int i=1;i<=n;i++) a[0][i]=read(),b[0][i]=0;
for(int i=1;i<=m;i++) a[1][i]=read(),b[1][i]=0;
for(int w=0;w<2;w++)
for(int i=1;i<=c[w];i++)
{
f[w][i].build(1,1,c[w^1],w^1,a[w][i]);
if(f[w][i].ask())
q.push({w,i}),b[w][i]=1,d[w]++;
}
while(!q.empty())
{
int w=q.front().x,i=q.front().y;q.pop();
for(int j=1;j<=c[w^1];j++) if(!b[w^1][j])
{
f[w^1][j].rem(1,1,c[w],i);
if(f[w^1][j].ask())
q.push({w^1,j}),b[w^1][j]=1,d[w^1]++;
}
}
if(c[0]==d[0] || c[1]==d[1]) {puts("NO");return ;}
puts("YES");
write(c[0]-d[0]);putchar(' ');
write(c[1]-d[1]);puts("");
for(int w=0;w<2;w++,puts(""))
for(int i=1;i<=c[w];i++) if(!b[w][i])
write(a[w][i]),putchar(' ');
}
signed main()
{
T=read();
while(T--) work();
}