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
貌似是后缀自动机。。。待我学习之后再补上。