Task.1 矩阵乘法
题目大意:给你一个 (N imes N) 的矩阵,(Q) 次询问每次询问一个子矩形的第 (K) 小数。
数据范围:(1leq Nleq 500,Qleq 60000)
树套树(主席树)、整体二分、分块。大概那么搞就能过去...但是我还不是 ds 大师。
(Source:) 聪明人才能看到的BZOJ 2738 Luogu P1527
代码:
Task.2 Tree
题目大意:给定一张 (n) 个点 (m) 条带权边(权值(c))的连通图,保证存在最小生成树。求最小标准差生成树。
数据范围:(1leq Nleq 100,N-1leq Mleq 2000,c_ileq 100)
求最小标准差生成树...就是对于生成树的 (n-1) 条边要最小化这个东西:(sqrt{frac{sum(c_i-overline{c})^2}{n-1}})
本质上就是最小化 (sum (c_i-overline c)^2)。接下来的操作就比较套路了。
令 (f(x)=sum (c_i-x)^2),可以看出这个式子是把 (x) 当平均数对某些 (c_i) 求一个类似方差的东西,当 (x=overline c) 时 (f(x)) 等于上式。
一种比较初步的思路就是枚举一个 (x) 去求可能的 (c) 有哪些,然后对确定下来的这些 (c) 求标准差。我们把边按照 ((c_i-x)^2) 排序后求“最小”的生成树就能确定 (overline c) 最接近 (x) 时选哪些 (c)能最小化 (f(x)),不停的增加 (x) 同时更新答案即可。
(Source:) 聪明人才能看到的BZOJ 3754
代码:
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>
using namespace std;
template<class T>void read(T &x){
x=0; char c=getchar();
while(c<'0'||'9'<c)c=getchar();
while('0'<=c&&c<='9'){x=(x<<1)+(x<<3)+(c^48); c=getchar();}
}
typedef double db;
const int N=105,M=2005;
int n,m;
int fa[N];
struct edge{int x,y,c;}e[M];
int q[N]; db ave,ans=1e9;
db sqr(db x){return x*x;}
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
bool cmp(edge e1,edge e2){return sqr(e1.c-ave)<sqr(e2.c-ave);}
void kruskal(){
int x,y,fx,fy,cnt=0;
db tmp=0,sum=0;
sort(e+1,e+m+1,cmp);
for(int i=1;i<=n;i++)fa[i]=i;
for(int i=1;i<=m&&cnt<n;i++){
x=e[i].x; y=e[i].y;
if((fx=find(x))==(fy=find(y)))continue;
fa[fx]=fy; tmp+=e[i].c; q[++cnt]=e[i].c;
}
if(cnt!=n-1)return ;
tmp/=(n-1);
for(int i=1;i<n;i++)sum+=sqr(q[i]-tmp);
ans=min(ans,sum);
return ;
}
int main(){
// freopen("in","r",stdin);
read(n); read(m);
for(int i=1;i<=m;i++){
read(e[i].x); read(e[i].y); read(e[i].c);
}
for(ave=0.25;ave<=100;ave+=0.25)kruskal();
printf("%.4lf
",sqrt(ans/(n-1)));
return 0;
}
Task.3 Points and Segments
题目大意:在数轴上有 (n) 条线段([l,r]),现在要把每条线段染成红色或蓝色。求一个满足数轴上所有点被两种颜色的线段覆盖次数 (|c_{red}-c_{blue}|leq 1)的一种合法的染色方案。
数据范围:(1leq Nleq 10^5,0leq l_i,r_ileq 10^9)
吓死我了我还以为要求方案数
可以想到把染两种颜色分别看做 (+1 -1),然后就是求区间加减之后结果每个位置绝对值不超过 (1)。
然后还有一种想法是离散后建图,连接 (l_i,r_i) ,然后让每个位置被尽可能相等的两种颜色的边覆盖。
如果把两种思路结合一下,把建的边看成有权值的。分配权值的方法就是给边定向。就是要平衡一个点往左往右的边数。从一个点出发向一个方向走过去,再回来...若能平衡这个次数,我们就一定能找到一种定向方案。
回路?什么回路?我们发现这个东西有点像欧拉回路。
若建好图图中没有奇点的话,欧拉回路一定存在,那么我们就可以构造出一种合法方案。
若存在奇点。那么奇点的个数一定是偶数个(想想为什么)。我们从左到右枚举每一个奇点,和他之后最近的一个奇点连边把他们俩都变成偶点,再往后跳,这样就能变成上面的那种情况,再求解欧拉回路就可以了。至于奇点之间连的那些边删掉也不会使答案变得不合法(想想为什么)。
代码:
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<vector>
using namespace std;
template<class T>void read(T &x){
x=0; char c=getchar();
while(c<'0'||'9'<c)c=getchar();
while('0'<=c&&c<='9'){x=(x<<1)+(x<<3)+(c^48); c=getchar();}
}
const int N=200050;
int n;
struct seg{int l,r;}a[N];
int t[N],cnt;
int d[N],ans[N];
int head[N],tot=1,pos[N];
struct edge{int to,next;}e[N<<1];
int vis[N],ve[N];
void add(int x,int y){e[++tot]=(edge){y,head[x]}; head[x]=tot;}
void dfs(int x,int dep=1){
vis[x]=1;
for(int i=head[x],y;i;i=e[i].next){
y=e[i].to; if(ve[i>>1])continue;
ve[i>>1]=1; ans[pos[i>>1]]=x<y;
dfs(y);
}
}
int main(){
// freopen("in","r",stdin);
read(n);
int x,y;
for(int i=1;i<=n;i++){
read(x); read(y); ++y;
a[i]=(seg){x,y};
t[++cnt]=x; t[++cnt]=y;
}
sort(t+1,t+cnt+1);
cnt=unique(t+1,t+cnt+1)-t-1;
for(int i=1;i<=n;i++){
x=lower_bound(t+1,t+cnt+1,a[i].l)-t;
y=lower_bound(t+1,t+cnt+1,a[i].r)-t;
++d[x]; ++d[y];
add(x,y); add(y,x);
pos[tot>>1]=i;
}
for(x=1,y=-1;x<=cnt;x++)if(d[x]&1){
if(y==-1)y=x;
else {add(x,y); add(y,x); y=-1;}
}
for(x=1;x<=cnt;x++)
if(!vis[x])dfs(x);
for(int i=1;i<=n;i++)printf("%d ",ans[i]);
return 0;
}