1. 程式人生 > >小P的單調數列

小P的單調數列

再次強調樹狀陣列求解最長上升子序列。
再次強調能寫樹狀陣列就不用寫線段樹。
樹狀陣列能幹的事:

1:快速簡潔求解字首最值
2:快速簡潔求解字首和,單點修改區間和,區間修改單點值。
3:並不簡潔的求解區間修改區間求和(但也比線段樹簡潔)
4:快速維護樹上每個點到根的路徑權值和
5:快速維護樹上每個點的子樹和(與4不相容)
6:用來輕鬆愉快的套主席樹,Splay等各類動態資料結構
7:維護帶刪除\插入操作的排名,不過需要二分,兩個log不推薦,推薦線段樹一個log

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cctype>
#include<cmath>
#define maxn 205
#define LL long long
using namespace std;
 
int n,m;
int F[maxn];
int x[maxn],y[maxn],c1[maxn],c2[maxn];
LL c3[maxn];
int Find(int now){ return !F[now] ? now : F[now] = Find(F[now]); }
struct Point
{
    LL x,y;
    Point(LL x=0,LL y=0):x(x),y(y){}
    Point operator -(const Point &B)const{ return Point(x-B.x,y-B.y); }
    double operator *(const Point &B)const{ return x * B.y - y * B.x; }
};
 
int c[maxn];
inline bool cmp(const int &u,const int &v){ return c3[u] > c3[v]; }
 
LL maxc=0,maxt=0;
Point calc(int A,int B)
{
    for(int i=1;i<=m;i++) c3[i] = 1ll * c1[i] * A + 1ll * c2[i] * B;
    sort(c+1,c+1+m,cmp);
    memset(F,0,sizeof F);
     
    LL ans1=0,ans2=0;
    for(int i=1;i<=m;i++)
        if(Find(x[c[i]]) != Find(y[c[i]])){
            F[Find(x[c[i]])] = Find(y[c[i]]);
            ans1+=c1[c[i]] , ans2+=c2[c[i]];
        }
         
    if(maxc*maxc+maxt*maxt<ans1*ans1+ans2*ans2) maxc=ans1,maxt=ans2;
    return Point(ans1,ans2);
}
 
void Solve(Point A,Point B)
{
    Point C = calc(A.y-B.y,B.x-A.x);
    if((B-A) * (C-A) <= 0) return;
    Solve(A,C),Solve(C,B);
}
 
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++) scanf("%d%d%d%d",&x[i],&y[i],&c1[i],&c2[i]),c[i] = i;
    Point A = calc(-1000000,1) , B = calc(1,-1000000);
    Solve(A,B),Solve(B,A);
    printf("%.6lf\n",sqrt(maxc*maxc+maxt*maxt));
}