T1:
题目大意:大概就是在一张无向图(不一定连通)上,删掉一些边,使得这一张图没有一个边双连通分量.
这道题考场上的时候是想这直接把边双求出来,然后每一个边双的最小生成树求出来就好了.
但是正解只需要写一个最小生成树就可以了,我是不是太菜了,没想到...
代码如下:
#include<bits/stdc++.h>
using namespace std;
#define Abigail inline void
#define js(x1,y1,x2,y2) sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2))
typedef long long LL;
const int N=10000;
const int M=50000;
struct side{
int x,y,next;
double v;
bool flag;
}e[M*2+9];
int n,m,top=1,num,dfn[N+9],low[N+9],c[N+9],dcc,lin[N+9],fa[N+9];
double x[N+9],y[N+9],sum,ma;
Abigail ins(int X,int Y,double V){
e[++top].x=X;e[top].y=Y;e[top].v=V;
e[top].next=lin[X];
lin[X]=top;
}
void tarjan(int x,int inedge){
dfn[x]=low[x]=++num;
for (int i=lin[x];i;i=e[i].next)
if (!dfn[e[i].y]){
tarjan(e[i].y,i);
low[x]=min(low[x],low[e[i].y]);
if (low[e[i].y]>dfn[x])
e[i].flag=e[i^1].flag=1;
}else if (i^(inedge^1)) low[x]=min(low[x],dfn[e[i].y]);
}
void dfs(int x){
c[x]=dcc;
for (int i=lin[x];i;i=e[i].next){
if (c[e[i].y]||e[i].flag) continue;
dfs(e[i].y);
}
}
inline bool cmp(side a,side b){
return a.v>b.v;
}
int get(int u){
if (u^fa[u]) return fa[u]=get(fa[u]);
else return u;
}
Abigail kruskal(){
sort(e+2,e+top+2,cmp);
for (int i=1;i<=n;i++) fa[i]=i;
for (int i=2;i<=top;i++)
if (get(e[i].x)^get(e[i].y)&&c[e[i].x]==c[e[i].y]&&!e[i].flag){
ma+=e[i].v;
fa[get(e[i].y)]=get(e[i].x);
}
}
Abigail into(){
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
scanf("%lf%lf",&x[i],&y[i]);
int u,v;
double g;
for (int i=1;i<=m;i++){
scanf("%d%d",&u,&v);
g=js(x[u],y[u],x[v],y[v]);
ins(u,v,g);ins(v,u,g);
}
}
Abigail work(){
for (int i=1;i<=n;i++)
if (!dfn[i]) tarjan(i,0);
for (int i=1;i<=n;i++)
if (!c[i]){
++dcc;dfs(i);
}
for (int i=2;i<top;i+=2)
if (!e[i].flag) sum+=e[i].v;
kruskal();
}
Abigail outo(){
printf("%.9lf\n",sum-ma);
}
int main(){
freopen("plan.in","r",stdin);
freopen("plan.out","w",stdout);
into();
work();
outo();
return 0;
}
T2:
题目大意:A和B玩下棋,棋盘是1*n的,他们之中先手先在第1~m格放一颗棋子,然后之后的人就得在上一个人之后的1~m格选一格放棋子,并且放到的格子就可以获得这个格子的分数A[i].现在他们想知道,当两人都选择最优策略(让差最大)的时候,差是多少.
这道题我一开始在想极大极小搜索写暴力,然后我就想到了可以写逆推DP,用f[i][0]表示当前第i格是A在下棋A-B的差,f[i][1]是B在这格.
然后我推出了方程:
然后我又用线段树来维护这个东西,理论上可以过了.
不过我考场爆0了,因为我把逆推的循环写成了顺推...
考场爆0代码:
#include<bits/stdc++.h>
using namespace std;
#define Abigail inline void
typedef long long LL;
const int N=100000;
struct tree{
int l,r;
LL min,max;
}tr[N*5][2];
LL a[N+9];
int n,m;
Abigail pushup(int k,int t){
int ls=k<<1,rs=k<<1|1;
tr[k][t].min=min(tr[ls][t].min,tr[rs][t].min);
tr[k][t].max=max(tr[ls][t].max,tr[rs][t].max);
}
void build(int L,int R,int t,int k=1){
tr[k][t].l=L;tr[k][t].r=R;
if (L==R)return;
int mid=L+R>>1;
build(L,mid,t,k<<1);build(mid+1,R,t,k<<1|1);
}
void change(int x,LL num,int t,int k=1){
if (tr[k][t].l==tr[k][t].r){
tr[k][t].max=tr[k][t].min=num;
return;
}
int mid=tr[k][t].l+tr[k][t].r>>1;
if (x<=mid) change(x,num,t,k<<1);
else change(x,num,t,k<<1|1);
pushup(k,t);
}
LL query_max(int L,int R,int t,int k=1){
if (L==tr[k][t].l&&R==tr[k][t].r) return tr[k][t].max;
int mid=tr[k][t].l+tr[k][t].r>>1;
if (R<=mid) return query_max(L,R,t,k<<1);
else if (L>mid) return query_max(L,R,t,k<<1|1);
else return max(query_max(L,mid,t,k<<1),query_max(mid+1,R,t,k<<1|1));
}
LL query_min(int L,int R,int t,int k=1){
if (L==tr[k][t].l&&R==tr[k][t].r) return tr[k][t].min;
int mid=tr[k][t].l+tr[k][t].r>>1;
if (R<=mid) return query_min(L,R,t,k<<1);
else if (L>mid) return query_min(L,R,t,k<<1|1);
else return max(query_min(L,mid,t,k<<1),query_min(mid+1,R,t,k<<1|1));
}
Abigail into(){
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
scanf("%lld",&a[i]);
}
Abigail work(){
build(1,n,0);build(1,n,1);
change(n,a[n],0);change(n,-a[n],1);
for (int i=1;i<n;i++){
change(i,query_max(i+1,min(n,i+m),1),0);
change(i,query_min(i+1,min(n,i+m),0),1);
}
}
Abigail outo(){
printf("%lld\n%lld\n",query_max(1,min(n,m),0),query_min(1,min(n,m),1));
}
int main(){
freopen("game.in","r",stdin);
freopen("game.out","w",stdout);
into();
work();
outo();
return 0;
}
然后我进行了一通魔改之后代码就变成了单调队列,AC代码:
#include<bits/stdc++.h>
using namespace std;
#define Abigail inline void
typedef long long LL;
const int N=100000;
struct Now{
int id;
LL num;
}q0[N+9],q1[N+9];
int n,m,h0,h1,t0,t1;
LL a[N+9],f[N+9][2]; //我把0和1换一下应该没事>_<
Abigail push0(LL num,int id){
while (h0<=t0&&q0[t0].num>=num) --t0;
q0[++t0].num=num;q0[t0].id=id;
}
Abigail push1(LL num,int id){
while (h1<=t1&&q1[t1].num<=num) --t1;
q1[++t1].num=num;q1[t1].id=id;
}
Abigail pop0(int id){
while (q0[h0].id>=id) ++h0;
}
Abigail pop1(int id){
while (q1[h1].id>=id) ++h1;
}
LL front0(){
return q0[h0].num;
}
LL front1(){
return q1[h1].num;
}
Abigail into(){
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
scanf("%lld",&a[i]);
}
Abigail work(){
h0=h1=0;t0=t1=-1;
push0(-a[n],n);
push1(+a[n],n);
for (int i=n-1;i>=0;i--){
f[i][0]=front1();
f[i][1]=front0();
pop0(i+m);pop1(i+m);
push0(f[i][0]-a[i],i);
push1(f[i][1]+a[i],i);
}
}
Abigail outo(){
printf("%lld\n%lld\n",f[0][0],f[0][1]);
}
int main(){
freopen("game.in","r",stdin);
freopen("game.out","w",stdout);
into();
work();
outo();
return 0;
}
T3:
题目大意:维护树在没有两棵子树x和y时候的直径,边权一定是1.
啊我的两遍bfs求树的直径写挂了...
这道题我们可以考虑使用线段树维护dfs序来做.
我们考虑维护每一个区间的直径ans和直径两端u和v,当这点u和v不在这个区间中的同一个连通块(或者说lca不在这个区间内)的时候我们不用慌,因为我们最后找的时候绝对是一棵子树,所以绝对是在一个区间里的.
合并的时候我们考虑将直径设成四个点的最远点对.
证明:
我们考虑若直径不用跨越两个区间,则直径就是两条直径取max.
而若要跨越,显然,新直径的两个端点一定是旧直径的端点.
证毕.
那么如何找最远点对?LCA就可以了.
代码如下(这代码太难调了,重新对着dalao的代码写了一遍):
#include<bits/stdc++.h>
using namespace std;
#define Abigail inline void
typedef long long LL;
const int N=100009;
struct TR{
int x,y,len;
TR(int X=0,int Y=0,int z=0){x=X;y=Y;len=z;}
};
TR tr[N*4];
struct side{
int y,next;
}e[N*2];
int top,lin[N],n,q,fa[N*2][20],de[N],ap[N*2],fir[N*2],Log[2*N],er[20];
void ins(int X,int Y){
e[++top].y=Y;
e[top].next=lin[X];
lin[X]=top;
}
void start(){
for (int i=1;i<=ap[0];i++)
fa[i][0]=ap[i],Log[i]=log(i)/log(2);
for (int i=0;i<=19;i++) er[i]=1<<i;
for (int j=1;j<=19;j++)
for (int i=1;i<=ap[0];i++){
fa[i][j]=fa[i][j-1];
if (i+er[j-1]<=ap[0]&&de[fa[i+er[j-1]][j-1]]<de[fa[i][j]])
fa[i][j]=fa[i+er[j-1]][j-1];
}
}
int lca(int x,int y){
x=fir[x];y=fir[y];
if (x>y) swap(x,y);
int t=Log[y-x+1];
if (de[fa[x][t]]<de[fa[y-er[t]+1][t]]) return fa[x][t];
else return fa[y-er[t]+1][t];
}
int st[N],en[N],sum,Tbh[N];
void dfs(int k,int fa){
de[k]=de[fa]+1;
ap[++ap[0]]=k;fir[k]=ap[0];
Tbh[++sum]=k;st[k]=sum;
for (int i=lin[k];i;i=e[i].next)
if (e[i].y^fa){
dfs(e[i].y,k);
ap[++ap[0]]=k;
}
en[k]=sum;
}
int dis(int x,int y){
return de[x]+de[y]-de[lca(x,y)]*2;
}
TR merge(TR a,TR b){
if (a.len==-1) return b;
TR c;
if (a.len>b.len) c=a;
else c=b;
if (dis(a.x,b.x)>c.len) c=TR(a.x,b.x,dis(a.x,b.x));
if (dis(a.x,b.y)>c.len) c=TR(a.x,b.y,dis(a.x,b.y));
if (dis(a.y,b.x)>c.len) c=TR(a.y,b.x,dis(a.y,b.x));
if (dis(a.y,b.y)>c.len) c=TR(a.y,b.y,dis(a.y,b.y));
return c;
}
void build(int L,int R,int k=1){
if (L==R){
tr[k].x=tr[k].y=Tbh[L];
tr[k].len=0;
return;
}
int mid=L+R>>1;
build(L,mid,k<<1),build(mid+1,R,k<<1|1);
tr[k]=merge(tr[k<<1],tr[k<<1|1]);
}
TR query(int L,int R,int l=1,int r=n,int k=1){
if (L==l&&R==r) return tr[k];
int mid=l+r>>1;
if (R<=mid) return query(L,R,l,mid,k<<1);
else if (L>mid) return query(L,R,mid+1,r,k<<1|1);
else return merge(query(L,mid,l,mid,k<<1),query(mid+1,R,mid+1,r,k<<1|1));
}
Abigail into(){
scanf("%d%d",&n,&q);
for (int i=1;i<n;i++){
int x,y;
scanf("%d%d",&x,&y);
ins(x,y);ins(y,x);
}
}
Abigail work(){
dfs(1,0);
start();
build(1,n);
for (int i=1;i<=q;i++){
int x,y;
scanf("%d%d",&x,&y);
if (st[x]>st[y]) swap(x,y);
TR ans=TR(0,0,-1);
if (1<st[x]) ans=merge(ans,query(1,st[x]-1));
if (en[x]+1<st[y]) ans=merge(ans,query(en[x]+1,st[y]-1));
int En=max(en[x],en[y]);
if (En<n) ans=merge(ans,query(En+1,n));
printf("%d\n",(ans.len==-1)?0:ans.len);
}
}
Abigail outo(){
}
int main(){
freopen("find.in","r",stdin);
freopen("find.out","w",stdout);
into();
work();
outo();
return 0;
}