TopCoder SRM621 DIV1

250

题意不在说明了,理解应该不难,注意点就是非法区间是一个开区间。相切的情况是合法,这边没注意的话submit可以过,但过不了系统测试。

思想就是,统计出每一个city的不合法区间,得到两个端点,把每个区间的端点都放在一个数组中,排序去重,

然后[0,Z]这区间被上面处理得到的点分成了很多条线段。

判断一条线段是否合法的方法是用该线段的中点去测试,判断该点是否在不和法区间中,如不在则把它加到合法长度中去。

最后用合法长度除以总长度,就可以得到要求的概率。

// BEGIN CUT HERE

// END CUT HERE
#include <string>
#include <vector>
#include <algorithm>
#include <cstdio>
#include <iostream>
#include <map>
#include <utility>
#include <cmath>
#include <queue>
#include <stack>
#include <cstring>
#include <cstdlib>
#include <set>
#include <iterator>
#include <sstream>
#include <ctime>

using namespace std;

typedef long long LL;

#define sz(x) x.size()
#define pb push_back
#define mp make_pair
#define clr(x,a) memset(x,a,sizeof(x))
#define cpy(x,a) memcpy(x,a,sizeof(x))


#define fst first
#define sed second
#define eps 1e-6
class RadioRange {
	public:
    int sig(double x){
        if(fabs(x)<eps)
            return 0;
        return x>0?1:-1;
    }
    double dist(double x,double y){
        return sqrt(x*x+y*y);
    }
	double RadiusProbability(vector <int> X, vector <int> Y, vector <int> R, int Z) {
	   vector<double> dis;
	   for(int i=0;i<sz(X);i++)
            dis.pb(dist(X[i],Y[i]));
       vector<pair<double,double> > vpi;
       vector<double> vd;
       vector<double>::iterator vdi;
       for(int i=0;i<sz(X);i++){
            double l,r;
            l=max(dis[i]-R[i],0.0);
            l=min(l,Z*1.0);
            r=min(dis[i]+R[i],Z*1.0);
            vpi.pb(mp(l,r));
            vd.pb(l);
            vd.pb(r);
       }
       vd.pb(0);
       vd.pb(Z);
       sort(vd.begin(),vd.end());
       vdi=unique(vd.begin(),vd.end());
       vd.erase(vdi,vd.end());
       double ret=0;
       for(int i=1;i<sz(vd);i++){
            bool valid=1;
            double mid=(vd[i]+vd[i-1])/2;
            for(int j=0;j<sz(vpi);j++){
                double l=vpi[j].fst;
                double r=vpi[j].sed;
                if(sig(mid-l)>0&&sig(r-mid)>0){
                    valid=0;
                    break;
                }
            }
            if(valid){
                ret+=vd[i]-vd[i-1];
            }
       }
       return ret/Z;
	}

// BEGIN CUT HERE

// END CUT HERE

};
// BEGIN CUT HERE

// END CUT HERE


500

题目中给了两个树。

现在要求从两棵树中各取一条边,其中任何一棵树去掉一条边后都会变成两部分。

从每一棵树中选一个集合。求出选中两个集合交集的数量。其中数量最大值就是我们要求的值

最后,要求的答案就是枚举任意两条边,然后求值之后平方累加求和。

因为点最多有4000所以要用n^2的办法才能解决。

做法是利用树形DP的方法,

枚举TREE1中的边 N

首先对TREE1DFS,得到其中一个点的集合A 对A取反就是集合B。  N

然后对TREE2树形DP,记录以每一个节点为root的子树的节点总数和在集合A中的点数。 N

对TREE2处理之后可以快速求得对于当前TREE1中所选取边得到的集合A,B,在TREE2中去掉任何一条边之后集合相交的最大值。

具体做法是

max(cntA[rt],cnt[rt]-cntA[rt],size(A)-cntA[rt],size(B)-cnt[rt]+cntA[rt])

之后平方累加就可以了。

时间复杂度大概是

n*(n+n)

// BEGIN CUT HERE

// END CUT HERE
#include <string>
#include <vector>
#include <algorithm>
#include <cstdio>
#include <iostream>
#include <map>
#include <utility>
#include <cmath>
#include <queue>
#include <stack>
#include <cstring>
#include <cstdlib>
#include <set>
#include <iterator>
#include <sstream>
#include <ctime>

using namespace std;

typedef long long LL;

#define sz(x) x.size()
#define pb push_back
#define mp make_pair
#define clr(x,a) memset(x,a,sizeof(x))
#define cpy(x,a) memcpy(x,a,sizeof(x))


#define MAXN 4500

class TreesAnalysis {
	public:
    vector<int> t1,t2;
    vector<int> T[MAXN];
    vector<int> G[MAXN];
    bool A[MAXN];
    int Asize;
    int cnt[MAXN];
    int cntA[MAXN];

    void build(int n){
        for(int i=0;i<n-1;i++){
            G[i].pb(t1[i]);
            G[t1[i]].pb(i);
        }
    }

    void colorTree1(int u,int f){
        A[u]=1;
        Asize++;
        for(int i=0;i<sz(G[u]);i++){
            int v=G[u][i];
            if(v==f) continue;
            colorTree1(v,u);
        }
    }

    void make_tree(int u,int f){
        cnt[u]=1;

        for(int i=0;i<sz(t2);i++){
            int a=t2[i];
            int b=i;
            if(b==u)
                swap(a,b);
            if(a==u&&b!=f){
                T[u].pb(b);
                make_tree(b,a);
                cnt[u]+=cnt[b];
            }
        }
    }

    void countA_in_Tree2(int u,int f){
        cntA[u]=0;
        if(A[u])
            cntA[u]++;
        for(int i=0;i<sz(T[u]);i++){
            countA_in_Tree2(T[u][i],u);
            cntA[u]+=cntA[T[u][i]];
        }
    }

	long long treeSimilarity(vector <int> tree1, vector <int> tree2) {
	    int n=sz(tree1)+1;
	    t1=tree1;
	    t2=tree2;
        build(n);
        clr(cnt,0);
        make_tree(0,-1);
        LL ret=0;
        for(int i=0;i<n-1;i++){
            clr(A,0);Asize=0;
            colorTree1(tree1[i],i);
            countA_in_Tree2(0,-1);
            for(int j=1;j<n;j++){
                int tmp=max(cntA[j],cnt[j]-cntA[j]);
                tmp=max(tmp,Asize-cntA[j]);
                tmp=max(tmp,n-cnt[j]-Asize+cntA[j]);
                ret+=tmp*tmp;
            }

        }
        return ret;
	}

// BEGIN CUT HERE
	

// END CUT HERE

};
// BEGIN CUT HERE

// END CUT HERE


900

貌似是后缀自动机。。。待我学习之后再补上。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值