P2533 [AHOI2012] 信号塔
题意
给出 nnn 个点,求这 nnn 个点的最小圆覆盖
其中 n≤106n \leq 10^6n≤106
题解
考虑最小圆有以下性质
1.\verb!1.!1. 最小圆唯一
证明
考虑反证法,如果有两个最小圆,那么所有点一定在两个圆的相交部分
而不难证明这个部分一定能用更小的圆将其覆盖
所以不可能有两个最小圆,即最小圆唯一
2.\verb!2.!2. 若有 [1,i−1][1,i-1][1,i−1] 的最小圆,而 iii 不在这个最小圆内,那么 iii 一定在 [1,i][1,i][1,i] 的最小圆上
这是好证的,因为如果相交,那么只要让圆心往原来的圆心移动即可
那么有了这两个结论,就可以使用最小圆覆盖算法 ------ 随机增量法
我们可以先枚举 iii ,假使 iii 在当前求出的 [1,i−1][1,i-1][1,i−1] 的最小圆中,那么就 continuecontinuecontinue
否则,我们令当前最小圆为 (xi,yi,0)(x_i,y_i,0)(xi,yi,0),然后枚举 jjj
若 jjj 不在当前最小圆内,令当前最小圆为 (xi+xj2,yi+yj2,disi,j2)(\frac{x_i+x_j}{2},\frac{y_i+y_j}{2},\frac{dis_{i,j}}{2})(2xi+xj,2yi+yj,2disi,j),然后枚举 kkk
若 kkk 不在当前最小圆内,那么最小圆直接设为 (i,j,k)(i,j,k)(i,j,k) 的外接圆
这个算法看似是 O(n3)O(n^3)O(n3) 的,实际上在随机数据下不难证明是 O(n)O(n)O(n) 的
代码
#include<bits/stdc++.h>
# define Maxn 1000005
# define db double
using namespace std;
int n;
struct Node{db x,y;}a[Maxn];
struct Circle{db x,y,r;}p;
bool isIn(int x) {
db dis=sqrt((a[x].x-p.x)*(a[x].x-p.x)+((a[x].y-p.y)*(a[x].y-p.y)));
return dis<=p.r;
}
void Find(int x,int y,int z) {
db A=2*(a[y].x-a[x].x);
db B=2*(a[y].y-a[x].y);
db C=a[y].x*a[y].x-a[x].x*a[x].x+a[y].y*a[y].y-a[x].y*a[x].y;
db D=2*(a[z].x-a[y].x);
db E=2*(a[z].y-a[y].y);
db F=a[z].x*a[z].x-a[y].x*a[y].x+a[z].y*a[z].y-a[y].y*a[y].y;
p.x=(C*E-F*B)/(A*E-D*B);
p.y=(A*F-D*C)/(A*E-D*B);
p.r=sqrt((a[x].x-p.x)*(a[x].x-p.x)+((a[x].y-p.y)*(a[x].y-p.y)));
}
int main() {
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%lf%lf",&a[i].x,&a[i].y);
for(int i=1;i<=n;i++) {
if(!isIn(i)) {
p={a[i].x,a[i].y,0};
for(int j=1;j<i;j++) {
if(!isIn(j)) {
p={(a[i].x+a[j].x)/2,(a[i].y+a[j].y)/2,sqrt((a[i].x-a[j].x)*(a[i].x-a[j].x)+(a[i].y-a[j].y)*(a[i].y-a[j].y))/2};
for(int k=1;k<j;k++)
if(!isIn(k)) Find(i,j,k);
}
}
}
}
printf("%.2lf %.2lf %.2lf\n",p.x,p.y,p.r);
return 0;
}
P3732 [HAOI2017] 供给侧改革
题意
给出一个长为 nnn 的 010101 串 SSS
令 data(L,R)data(L,R)data(L,R) 表示:在字符串 SSS 中,起始位置在 [L,R][L,R][L,R] 之间的这些后缀之中,具有最长公共前缀的两个后缀的最长公共前缀的长度。
现有每个询问 (L,R)(L,R)(L,R)
求 ∑L≤i<Rdata(i,R)\sum_{L\leq i <R} data(i,R)∑L≤i<Rdata(i,R)
其中,n⩽105n \leqslant 10^5n⩽105,Q⩽105Q \leqslant 10^5Q⩽105,1⩽L<R⩽n1 \leqslant L < R \leqslant n1⩽L<R⩽n,01\texttt{01}01 串随机生成
题意
由于数据随机,所以最长公共前缀长度至多 404040 ,于是随便打棵字典树就行了
代码
#include<bits/stdc++.h>
# define Maxn 100005
# define pr pair<int,int>
# define fir first
# define sec second
using namespace std;
int n,q,l,r;
int ans[Maxn];
char a[Maxn];
vector<pr> vec[Maxn];
struct Trie{
int trie[Maxn*42][2],num[Maxn*42],mx[45],tot;
void insert(int x) {
int rt=0;
for(int i=x;i<=n&&i-x+1<=40;i++) {
if(!trie[rt][a[i]-'0']) trie[rt][a[i]-'0']=++tot;
rt=trie[rt][a[i]-'0'];
num[rt]=max(num[rt],x);
}
}
void Calc(int r) {
int rt=0;
for(int i=r;i<=n&&i-r+1<=40;i++) {
if(!trie[rt][a[i]-'0']) break;
mx[i-r+1]=max(mx[i-r+1],num[trie[rt][a[i]-'0']]);
rt=trie[rt][a[i]-'0'];
}
}
void Solve(int l,int id) {
for(int i=1;i<=40;i++)
if(mx[i]>=l) ans[id]+=mx[i]-l+1;
}
}tr;
int main() {
scanf("%d%d%s",&n,&q,a+1);
for(int i=1;i<=q;i++) {
scanf("%d%d",&l,&r);
vec[r].push_back({l,i});
}
for(int i=1;i<=n;i++) {
tr.Calc(i);
for(pr it:vec[i]) tr.Solve(it.fir,it.sec);
tr.insert(i);
if(i==11) {
int rt=0;
rt=tr.trie[rt][0];
rt=tr.trie[rt][0];
// printf("NUM %d\n",tr.num[rt]);
}
}
for(int i=1;i<=q;i++) printf("%d\n",ans[i]);
return 0;
}
P4589 [TJOI2018] 智力竞赛
题意
小豆报名参加智力竞赛,他带上了 nnn 个好朋友作为亲友团一块来参加比赛。比赛规则如下:
一共有 mmm 道题目,每个人都有 111 次答题机会,每次答题为选择一道题目回答,在回答正确后,可以继续回答这个题目的后续题目,直到答错题目或者没有后续题目。
每个问题都会有一个价值,比赛最后参赛选手获得的奖励价值等价于该选手和他的亲友团没有回答的问题中的最低价值。
我们现在知道小豆和他的亲友团实力非常强,能够做出这次竞赛中的所有题目。
小豆想知道在知道题目和后续题目的条件下,他最大能获得的价值是多少?
其中, 1<n≤50,1<m≤500,vi≤109,ki,ai,j≤m1<n\leq50,1<m\leq500,v_i\leq10^9,k_i,a_{i,j}\leq m1<n≤50,1<m≤500,vi≤109,ki,ai,j≤m。
赛时思路 以及 题解
考虑网络流
不妨先按价值从小到大排序
枚举前 kkk 小的,然后判断能否将其走完
然后判断可以费用流,将每个点拆成 111 和 222 两个部分
111 向 222 连两条边:(1,1),(inf,0)(1,1),(inf,0)(1,1),(inf,0)
然后给点一共 kkk 的流,判断最大费用是否有 kkk 即可
代码
#include<bits/stdc++.h>
# define Maxn 55
# define Maxm 505
# define inf 2e9+1
using namespace std;
int n,m,p[Maxm],rk[Maxm<<1],st1,st2,ed;
struct Node{
int v,k;
int s[Maxm];
}a[Maxm];
bool cmp(int x,int y) {return a[x].v<a[y].v;}
int head[Maxm<<1],tot=2;
struct EDGE{int to,next,w,cost;}e[(Maxm*2+Maxm*Maxm)<<1];
void add(int u,int v,int w,int cost) {
e[tot]={v,head[u],w,cost};
head[u]=tot++;
}
void addedge(int u,int v,int w,int cost) {add(u,v,w,cost),add(v,u,0,-cost);}
bool vis[Maxm<<1],Vis[Maxm<<1];
int dis[Maxm<<1],cur[Maxm<<1],ans;
queue<int> q;
bool spfa() {
for(int i=1;i<=ed;i++) dis[i]=-inf,cur[i]=head[i];
dis[st1]=0,q.push(st1),vis[st1]=1;
while(!q.empty()) {
int h=q.front();q.pop(),vis[h]=0;
for(int i=head[h];i;i=e[i].next) {
if(e[i].w&&dis[e[i].to]<dis[h]+e[i].cost) {
dis[e[i].to]=dis[h]+e[i].cost;
if(!vis[e[i].to]) vis[e[i].to]=1,q.push(e[i].to);
}
}
}return dis[ed]!=(-inf);
}
int dfs(int rt,int flow) {
if(rt==ed) {ans+=dis[rt]*flow;return flow;}
int res=0;Vis[rt]=1;
for(int i=head[rt];i;i=e[i].next) {
if((!Vis[e[i].to])&&e[i].w&&dis[e[i].to]==dis[rt]+e[i].cost) {
int nowflow=dfs(e[i].to,min(flow,e[i].w));
if(!nowflow) dis[e[i].to]=-inf;
else {
flow-=nowflow;
res+=nowflow;
e[i].w-=nowflow;
e[i^1].w+=nowflow;
if(!flow) {Vis[rt]=0;return res;}
}
}
}Vis[rt]=0;return res;
}
void Build(int x) {
for(int i=1;i<=ed;i++) head[i]=0;
tot=2;
for(int i=1;i<=x;i++) {p[i]=i;
for(int j=1;j<=a[i].k;j++) {
if(a[i].s[j]<=x)
addedge(i+m,a[i].s[j],inf,0);
}
addedge(i,i+m,1,1);
addedge(i,i+m,inf,0);
}
addedge(st1,st2,n+1,0);
for(int i=1;i<=x;i++) addedge(st2,i,inf,0),addedge(i+m,ed,inf,0);
}
int main() {
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++) {p[i]=i;
scanf("%d%d",&a[i].v,&a[i].k);
for(int j=1;j<=a[i].k;j++) {
scanf("%d",&a[i].s[j]);
// addedge(i+m,a[i].s[j],inf,0);
}
// addedge(i,i+m,1,a[i].v);
// addedge(i,i+m,inf,0);
}
st1=2*m+1,st2=2*m+2,ed=2*m+3;
// addedge(st1,st2,n+1,0);
// for(int i=1;i<=m;i++) addedge(st2,i,inf,0),addedge(i+m,ed,inf,0);
sort(p+1,p+m+1,cmp);
for(int i=1;i<=m;i++) rk[p[i]]=rk[p[i]+m]=i;
int ans1=0;
// while(spfa(m)) ans1+=dfs(st1,inf,m);
// printf("%d %d\n",ans1,ans);
for(int i=1;i<=m;i++) {
Build(i);
ans=0;
while(spfa()) ans1+=dfs(st1,inf);
// printf("%d %d\n",ans1,ans);
if(ans<i) {
printf("%d\n",a[p[i]].v);
break;
}
if(i==m) printf("AK\n");
}
return 0;
}