反思:最小生成树没怎么见过题,单调题看得出来打不来。。。
T1 最大约数和
题面:给定一个正整数 S,现在要求你选出若干个互不相同的正整数,使得它们的和不大于 S,而且每个数的因数(不包括本身)之和最大。(S<=1000)
题解:O(n2)预处理约数和,然后01背包完事
#include<bits/stdc++.h>
using namespace std;
const int N=1e3+10;
int n,w[N];
int dp[N];
int main()
{
for(int i=1;i<=1000;i++)
for(int j=1;j<i;j++)
if(i%j==0)w[i]+=j;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
for(int j=n-i;j>=0;j--)
{
dp[j+i]=max(dp[j+i],dp[j]+w[i]);
}
}
printf("%d",dp[n]);
}
T2 最佳序列
题目:给一个长度为 n 的数组 A,给定 L, R,求所有满足长度大等于 L,小等于 R 的 A 数组的子区间的平均值的最大值。
题解:二分平均值,然后转成子问题,减去平均值后是否存在一段合法区间总值为正。这里我们可以用单调队列(区间滑动)
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e5+10;
int n,L,R;
int a[N];
double b[N],s[N];
int z[N],p1,p2;
bool check(double x)
{
for(int i=1;i<=n;i++)b[i]=a[i]*1.0-x,s[i]=s[i-1]+b[i];
double ans=0;p1=1;p2=0;
for(int i=L;i<=n;i++)
{
while(p1<=p2&&i-z[p1]>R)p1++;
while(p1<=p2&&s[z[p2]]>s[i-L])p2--;
z[++p2]=i-L;
ans=max(ans,s[i]-s[z[p1]]);
}return ans>0;
}
int main()
{
double maxx=0;
scanf("%d%d%d",&n,&L,&R);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]),maxx=max(maxx,(double)a[i]);
double l=0,r=maxx,mid;
while(r-l>1e-10)
{
mid=(l+r)/2;
if(check(mid))l=mid;
else r=mid;
}
printf("%.4lf",l);
}
T3 贸易
题目:选择若干条边,使每个点的入度为1,问最小总边权
题解:很容易想到最小生成树+生成树外最短边(基环树)。但考虑联通块可以拆开建最小生成树。于是我就不会了。。。
//-------------------------------------------------
考虑最小生成树的加边法(忘名了),用并查集维护是否在树内,另开数组维护每个并查集是否成环;
按边权排序后:1,两点在同一树中,无环就加边变有环。2,两点都在有环树中(异树),就不连边;3,两点异树(可能某个在有环树中)或未分配,连边。
最后统计是否每个点都连过了,不然输出No。
//---------听说费用流可以骗60分-----------
#include<bits/stdc++.h>
using namespace std;
inline int read()
{
int ans=0;bool f=1;char s=getchar();
while(s>'9'||s<'0'){if(s=='-')f=1;s=getchar();}
while(s>='0'&&s<='9')
{
ans=(ans<<1)+(ans<<3)+(s&15);
s=getchar();
}return f?ans:-ans;
}
const int N=5e5+10;
int n,m;
struct qu{int u,v,w;bool operator < (const qu x)const{return w<x.w;}}e[N];
int f[N];
int fa(int x){return (x==f[x])?x:f[x]=fa(f[x]);}
bool h[N];
int main()
{
n=read();m=read();
for(int i=1;i<=m;i++)
{
e[i].u=read();e[i].v=read();e[i].w=read();
}sort(e+1,e+m+1);
for(int i=1;i<=n;i++)f[i]=i;
int cnt=0;long long ans=0;
for(int i=1;i<=m;i++)
{
int u=fa(e[i].u),v=fa(e[i].v);
if(u==v)
{
if(!h[u])//情况1
{
ans+=e[i].w;
cnt++;h[u]=1;
}
}
else
{
if(h[u]&&h[v])continue;//情况2
h[u]=h[u]|h[v];
f[v]=u;cnt++;
ans+=e[i].w;//情况3
}
}
if(cnt==n)printf("%lld",ans);
else printf("No");
}