略
略
略
显然原点发出的各条射线上选择方案可以分别考虑。
考虑对于某条射线上的 m m m个点按到原点的距离考虑,距离分别为 d 1 < d 2 < ⋯ < d m d_1
再注意到在同一条射线上每选择一个点对答案的增量单调不增,因此有一个经典的贪心做法是将每条射线上选择 1 ∼ k 1\sim k 1∼k个的增量全部取出来排序后,取前 k k k大的增量之和即可,这也对应了一个方案。
时间复杂度为 O ( n log n ) \mathcal O(n\log n) O(nlogn)。
#include
#define FR first
#define SE second
using namespace std;
typedef long long ll;
typedef double db;
typedef pair<ll,ll> pr;
db val[500005];
int sz;
bool cmp(pr x,pr y) {
return x.FR*x.FR+x.SE*x.SE<y.FR*y.FR+y.SE*y.SE;
}
inline db dis(pr x) {
return sqrt(x.FR*x.FR+x.SE*x.SE);
}
void solve(vector<pr> &vt,int k) {
int m=(k>>1),n=vt.size();
sort(vt.begin(),vt.end(),cmp);
for(int i=1;i<=min(m,n);i++) {
pr x=vt[n-i];
val[++sz]=dis(x)*((k-i)-(i-1));
}
db s=0;
for(int i=m+1;i<=min(n,k);i++) {
pr x=vt[i-m-1];
val[++sz]=dis(x)*((k-m-1)-m)-2.0*s;
s+=dis(x);
}
}
map <pr,int> mp;
vector <pr> vt[500005];
int main() {
int n,k;
scanf("%d%d",&n,&k);
int cnt=0;
for(int i=1;i<=n;i++) {
int x,y;
scanf("%d%d",&x,&y);
if (!x&&!y) vt[++cnt].push_back(pr(x,y));
else {
int d=__gcd(abs(x),abs(y));
pr t(x/d,y/d);
if (!mp.count(t)) mp[t]=++cnt;
vt[mp[t]].push_back(pr(x,y));
}
}
for(int i=1;i<=cnt;i++)
solve(vt[i],k);
sort(val+1,val+sz+1);
reverse(val+1,val+sz+1);
db s=0;
for(int i=1;i<=k;i++) s+=val[i];
printf("%.10f\n",s);
return 0;
}
简单分析一下可以知道点 a a a是有趣的的条件,即以点 a a a为根求DFS树,包含每个点且所有非树边均为返祖边。
注意到题目要求当 < 20 % <20\% <20%的点是有趣的的时候直接输出 − 1 -1 −1,这启发我们采用随机化算法。即我们随机 T T T个点,暴力DFS验证它们中是否存在一个有趣的点,不存在的话直接输出 − 1 -1 −1,显然判断错误(将有解判为无解)的概率不超过 ( 1 − 20 % ) T = ( 4 5 ) T (1-20\%)^T=(\frac{4}{5})^T (1−20%)T=(54)T,当 T = 50 T=50 T=50的时候错误率已经非常小了。
如果存在的话,我们取其中一个点 x x x作为根,考虑求出所有有趣的的点。注意到以 x x x为根的DFS树包含所有点,且只包含树边与返祖边。这是一个很好的性质,考虑对于一个点 y ≠ x y\neq x y=x,以它为根的DFS树显然能包含它在 x x x为根的DFS树的子树中的所有点,且显然在以 x x x为根的DFS树的子树中必须恰好有一条返祖边(否则容易构造出到返祖边端点中较深的一个祖先的两条简单路径)。考虑返祖边端点的祖先 z z z,若 z = x z=x z=x显然是有趣的,否则分析一下可知 y y y是有趣的当且仅当 z z z是有趣的,那么可以用树上差分预处理一下子树中返祖边的信息后,一遍DFS判定所有点。
单组数据时间复杂度为 O ( T ( n + m ) ) \mathcal O(T(n+m)) O(T(n+m)),取 T = 50 T=50 T=50可以轻松通过。
#include
#define FR first
#define SE second
#define inf 0x3f3f3f3f
#define MOD 1000000007
using namespace std;
typedef long long ll;
typedef pair<int,int> pr;
vector <int> e[100005];
bool vis[100005],in[100005];
int dep[100005];
bool dfs1(int x) {
vis[x]=in[x]=1;
for(int i=0;i<e[x].size();i++) {
int u=e[x][i];
if (!vis[u]) {
dep[u]=dep[x]+1;
if (!dfs1(u)) return 0;
}
else if (!in[u]) return 0;
}
in[x]=0;
return 1;
}
int siz[100005];
pr minn[100005];
void dfs2(int x) {
for(int i=0;i<e[x].size();i++) {
int u=e[x][i];
if (dep[u]<dep[x]) {
siz[x]++;
siz[u]--;
minn[x]=min(minn[x],pr(dep[u],u));
}
else {
dfs2(u);
siz[x]+=siz[u];
minn[x]=min(minn[x],minn[u]);
}
}
}
bool f[100005];
void dfs3(int x) {
if (dep[x]>1) f[x]=(siz[x]==1&&f[minn[x].SE]);
for(int i=0;i<e[x].size();i++)
if (dep[e[x][i]]>dep[x]) dfs3(e[x][i]);
}
int randint() {
return ((ll)rand()*9116111716LL+rand())%1000000007;
}
int ans[100005];
int main() {
srand(time(0));
int cases;
scanf("%d",&cases);
for(;cases;cases--) {
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) e[i].clear();
for(int i=1;i<=m;i++) {
int x,y;
scanf("%d%d",&x,&y);
e[x].push_back(y);
}
int rt=0;
for(int i=1;i<=50;i++) {
int x=randint()%n+1;
for(int j=1;j<=n;j++) vis[j]=in[j]=0;
dep[x]=1;
if (dfs1(x)) {
rt=x;
break;
}
}
if (!rt) {
puts("-1");
continue;
}
for(int i=1;i<=n;i++) {
siz[i]=0;
minn[i]=pr(inf,inf);
}
dfs2(rt);
for(int i=1;i<=n;i++) f[i]=0;
f[rt]=1;
dfs3(rt);
int sz=0;
for(int i=1;i<=n;i++)
if (f[i]) ans[++sz]=i;
if (sz*5<n) {
puts("-1");
continue;
}
for(int i=1;i<=sz;i++) printf("%d ",ans[i]);
printf("\n");
}
return 0;
}
考虑建出 W W W序列的笛卡尔树,且令 P P P序列每个点成为左右两侧相邻的 W W W序列较大的点对应方向的孩子。那么可以发现一次操作中 M = x M=x M=x的话可能的左右端点是确定的,且相当于交换了笛卡尔树上点 x x x的左右子树。
注意到是否交换点 x x x的左右子树只会决定 x x x的左子树与右子树之间的逆序对数,且这些逆序对与其他点是否交换是无关的,因此可以对每个点分别决策,求出交换与不交换较少的逆序对数贡献到答案中。
注意到初始序列 W W W是随机的,因此根据Treap的理论分析,笛卡尔树期望最大深度是 O ( log n ) \mathcal O(\log n) O(logn)的。那么每次修改操作显然只会影响它们到根路径上的点,用数据结构维护每个点左右子树中的权值集合,每次修改相当于删去一个权值再加入一个权值,在另一棵子树对应的数据结构中二分即可求出这个点交换后逆序对数的该变量,进而求出答案。
这里的数据结构我选择了动态开点线段树,期望时间复杂度为 O ( ( n + q ) log 2 n ) \mathcal O((n+q)\log^2n) O((n+q)log2n),也可以优化到期望 O ( n log n + q log 2 n ) \mathcal O(n\log n+q\log^2n) O(nlogn+qlog2n)。
#include
#define last last2
using namespace std;
typedef long long ll;
namespace SGT {
const int Maxn=80000000;
int ch[Maxn][2],siz[Maxn],tot;
int update(int l,int r,int o,int p,int q) {
if (!o) o=++tot;
siz[o]+=q;
if (l==r) return o;
else {
int m=((l+r)>>1);
if (m>=p) ch[o][0]=update(l,m,ch[o][0],p,q);
else ch[o][1]=update(m+1,r,ch[o][1],p,q);
return o;
}
}
int query1(int l,int r,int o,int p) {
if (!o) return 0;
if (l==r) return siz[o];
else {
int m=((l+r)>>1);
if (m>=p) return query1(l,m,ch[o][0],p);
else return siz[ch[o][0]]+query1(m+1,r,ch[o][1],p);
}
}
int query2(int l,int r,int o,int p) {
if (!o) return 0;
if (l==r) return siz[o];
else {
int m=((l+r)>>1);
if (m<p) return query2(m+1,r,ch[o][1],p);
else return siz[ch[o][1]]+query2(l,m,ch[o][0],p);
}
}
}
int num[200005],val[200005],n;
ll ans;
namespace Treap {
int ch[200005][2];
int fa[200005],dep[200005];
bool dir[200005];
void dfs1(int x) {
if (ch[x][0]) {
fa[ch[x][0]]=x;
dep[ch[x][0]]=dep[x]+1;
dir[ch[x][0]]=0;
dfs1(ch[x][0]);
}
if (ch[x][1]) {
fa[ch[x][1]]=x;
dep[ch[x][1]]=dep[x]+1;
dir[ch[x][1]]=1;
dfs1(ch[x][1]);
}
}
int root1[200005],root2[200005],siz[200005][2];
vector <int> vt[200005][2];
ll sum[200005];
void dfs2(int x) {
if (ch[x][0]) dfs2(ch[x][0]);
if (ch[x][1]) dfs2(ch[x][1]);
siz[x][0]=vt[x][0].size();
siz[x][1]=vt[x][1].size();
for(int i=0;i<siz[x][0];i++)
root1[x]=SGT::update(1,n,root1[x],vt[x][0][i],1);
for(int i=0;i<siz[x][1];i++) {
sum[x]+=SGT::query1(1,n,root1[x],vt[x][1][i]);
root2[x]=SGT::update(1,n,root2[x],vt[x][1][i],1);
}
ans+=min(sum[x],(ll)siz[x][0]*siz[x][1]-sum[x]);
}
int st[200005];
void build(int n) {
int top=0;
for(int i=1;i<n;i++) {
int last=0;
while (top&&val[i]<val[st[top]]) {
ch[st[top]][1]=last;
last=st[top--];
}
ch[i][0]=last;
st[++top]=i;
}
while (top>1) {
ch[st[top-1]][1]=st[top];
top--;
}
dep[st[1]]=1;
dfs1(st[1]);
for(int i=1;i<=n;i++) {
int cur;
bool v;
if (dep[i-1]>dep[i]) {
cur=i-1;
v=1;
}
else {
cur=i;
v=0;
}
while (cur) {
vt[cur][v].push_back(num[i]);
v=dir[cur];
cur=fa[cur];
}
}
dfs2(st[1]);
}
void update(int x,bool v,int p,int q) {
while (x) {
ans-=min(sum[x],(ll)siz[x][0]*siz[x][1]-sum[x]);
if (!v) {
sum[x]-=SGT::query2(1,n,root2[x],p);
sum[x]+=SGT::query2(1,n,root2[x],q);
root1[x]=SGT::update(1,n,root1[x],p,-1);
root1[x]=SGT::update(1,n,root1[x],q,1);
}
else {
sum[x]-=SGT::query1(1,n,root1[x],p);
sum[x]+=SGT::query1(1,n,root1[x],q);
root2[x]=SGT::update(1,n,root2[x],p,-1);
root2[x]=SGT::update(1,n,root2[x],q,1);
}
ans+=min(sum[x],(ll)siz[x][0]*siz[x][1]-sum[x]);
v=dir[x];
x=fa[x];
}
}
}
int main() {
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&num[i]);
for(int i=1;i<n;i++) scanf("%d",&val[i]);
Treap::build(n);
int m;
scanf("%d",&m);
for(int i=1;i<=m;i++) {
int x,y;
scanf("%d%d",&x,&y);
if (x!=y) {
Treap::update(((Treap::dep[x-1]>Treap::dep[x])?x-1:x),(Treap::dep[x-1]>Treap::dep[x]),num[x],num[y]);
Treap::update(((Treap::dep[y-1]>Treap::dep[y])?y-1:y),(Treap::dep[y-1]>Treap::dep[y]),num[y],num[x]);
swap(num[x],num[y]);
}
printf("%lld\n",ans);
}
return 0;
}