Task.1 矩阵乘法
题目大意:给你一个 \(N\times N\) 的矩阵,\(Q\) 次询问每次询问一个子矩形的第 \(K\) 小数。
数据范围:\(1\leq N\leq 500,Q\leq 60000\)
树套树(主席树)、整体二分、分块。大概那么搞就能过去...但是我还不是 ds 大师。
\(Source:\) 聪明人才能看到的BZOJ 2738 Luogu P1527
代码:
Task.2 Tree
题目大意:给定一张 \(n\) 个点 \(m\) 条带权边(权值\(c\))的连通图,保证存在最小生成树。求最小标准差生成树。
数据范围:\(1\leq N\leq 100,N-1\leq M\leq 2000,c_i\leq 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\n",sqrt(ans/(n-1)));
return 0;
}
Task.3 Points and Segments
题目大意:在数轴上有 \(n\) 条线段\([l,r]\),现在要把每条线段染成红色或蓝色。求一个满足数轴上所有点被两种颜色的线段覆盖次数 \(|c_{red}-c_{blue}|\leq 1\)的一种合法的染色方案。
数据范围:\(1\leq N\leq 10^5,0\leq l_i,r_i\leq 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;
}