[HDOJ3436 Queue-jumpers]【Splay练手】

【题目大意】

一开始给定n个人,他们按编号1..n地排好了。

有3个操作。

分别是:

Top x:把编号为x个人放到a[1]。然后a[1..x-1]的数都向后移一位。

Query x:返回编号为x个人现在在哪里位置。

Rank x:返回排名在x的这个人是编号是多少?

【算法分析】

我采用的是Splay来离线解决。

由于n高达10^8。所以不能完全模拟。

首先是把询问都读进来,然后对于Top和Query碰到的编号,开一个List取出来,排序并去重。

然后开始构建Splay。

对于在List中的编号,我们把他构建成一个结点。

然后对于List[i]和List[i-1]之间的间隙结点,我把它们都压缩成一个结点。(当然Size的计算方法就有点不同了。)

然后这样,Splay上最多有2*Q个点了。

对于Top操作,我们就相当于删掉这个点,再插入一次这个点。

对于Query,我们可以直接把这个点Splay到根,然后算他的左子树的Size,+1就是答案。

对于Rank,我们就像找第k个元素那样,找到那个对应位置,输出就行了。(就是压缩块有点不同而已)

【其它】

2TLE。写惯了SBT。。。那个父亲的域老是忘了维护,导致死循环了。

【CODE】

#include

#include

#include #define update(p) (Tr[p].Size=Tr[ch[p][0]].Size+Tr[ch[p][1]].Size+Tr[p].weight)
const int N=305555;

struct Splay_t{
       int root,tot,Lasttop;
       int ch[N][2],pre[N];
       struct Node{int St,Size,weight;}Tr[N];
      
       void init(){
            root=tot=1;
            Lasttop=2;
            ch[1][0]=ch[1][1]=0;
            Tr[0].Size=0;
            pre[1]=0;
       }
      
       void rotate(int x,int T){
            int y=pre[x];
            if (T){
              ch[y][0]=ch[x][1];
              ch[x][1]=y;
              pre[ch[y][0]]=y;
              pre[x]=pre[y];
              pre[y]=x;
            }
            else{
              ch[y][1]=ch[x][0];
              ch[x][0]=y;
              pre[ch[y][1]]=y;
              pre[x]=pre[y];
              pre[y]=x;
            }
            if (ch[pre[x]][0]==y) ch[pre[x]][0]=x;
                             else ch[pre[x]][1]=x;
            update(y);
            update(x);
       }
      
       void Splay(int x,int cut){
            if (x==cut) return;
            int y,z;
            while (pre[x]!=cut){
                  y=pre[x]; z=pre[y];
                  if (z==cut) rotate(x,ch[y][0]==x);
                  else
                  if (ch[z][0]==y)
                    if (ch[y][0]==x) {rotate(y,1); rotate(x,1);}
                                else {rotate(x,0); rotate(x,1);}
                  else
                    if (ch[y][1]==x) {rotate(y,0); rotate(x,0);}
                                else {rotate(x,1); rotate(x,0);}
            }
       }
      
       void Ins(int St,int weight){
            tot++;
            ch[tot][0]=ch[tot][1]=0;
            Tr[tot].St=St;
            Tr[tot].weight=Tr[tot].Size=weight;
            if (tot==2){
              ch[root][0]=2;
              pre[2]=root;
            }
            else{
              Splay(tot-1,root);
              ch[tot-1][1]=tot;
              update(tot-1);
              pre[tot]=tot-1;
            }
            Splay(tot,root);
       }
      
       void Top(int x){
            if (Lasttop==x) return;
            Splay(x,root);
            int p,q;
            q=x; p=ch[x][1];
            while (p){
                  q=p;
                  p=ch[p][0];
            }
            if (q==x){
              ch[1][0]=ch[x][0];
              pre[ch[x][0]]=1;
            }
            else{
              Splay(q,x);
              ch[q][0]=ch[x][0];
              pre[q]=1;
              pre[ch[x][0]]=q;
              ch[1][0]=q;
              update(q);
            }
            Splay(Lasttop,root);
            ch[Lasttop][0]=x;
            pre[x]=Lasttop;
            ch[x][0]=ch[x][1]=0;
            update(x);
            update(Lasttop);
            Lasttop=x;
       }      
      
       int Query(int x){
           Splay(x,root);
           return Tr[ch[x][0]].Size+1;
       }
      
       int rank(int x){
           int p,done=0;
           p=ch[root][0];
           while (1){
                 if (x<=Tr[ch[p][0]].Size+done)
                   p=ch[p][0];
                 else
                 if (x>Tr[ch[p][0]].Size+done+Tr[p].weight){
                   done+=Tr[ch[p][0]].Size+Tr[p].weight;
                   p=ch[p][1];
                 }
                 else break;
           }
           Splay(p,root);
           return x-Tr[ch[p][0]].Size+Tr[p].St-1;
       }
      
}Splay;
int n,Q,Lt;
int op[N],a[N],List[N],pos[N];

int cmp(const void *A,const void *B){return *((int*)A) – *((int*)B);}
void init(){
     int i,cnt;
     Lt=List[0]=0;
     char Str[100];
     scanf("%d%d",&n,&Q);
     for (i=1;i<=Q;i++){
         scanf("%s%d",Str,&a[i]);
         switch (Str[0]){
                case ‘T’:op[i]=1; break;
                case ‘Q’:op[i]=2; break;
                case ‘R’:op[i]=3; break;
         }
         if (op[i]!=3) List[++Lt]=a[i];
     }
     qsort(List+1,Lt,sizeof(int),cmp);
     for (i=1,cnt=0;i<=Lt;i++){
         cnt+=(!cnt || List[i]!=List[cnt]);
         List[cnt]=List[i];
     }
     Lt=cnt;
}

void pre_Insert(){
     int i;
     Splay.init();
     for (List[0]=0,i=1;i<=Lt;i++){
         if (List[i]-List[i-1]>1)
           Splay.Ins(List[i-1]+1,List[i]-List[i-1]-1);
         Splay.Ins(List[i],1);
         pos[i]=Splay.tot;
     }
}

int Find(int x){
    int l=1,r=Lt,mid;
    while (l          mid=(l+r)/2;
          if (x>List[mid]) l=mid+1;
                      else r=mid;
    }
    return pos[l];
}

void solve(){
     int i;
     for (i=1;i<=Q;i++)
       switch (op[i]){
              case 1:Splay.Top(Find(a[i])); break;
              case 2:printf("%dn",Splay.Query(Find(a[i]))); break;
              case 3:
                   if (a[i]>List[Lt]) printf("%dn",a[i]);
                                 else printf("%dn",Splay.rank(a[i]));
                   break;
       }
}

int main(){
    int Tc,i;
    scanf("%d",&Tc);
    for (i=1;i<=Tc;i++){
        printf("Case %d:n",i);
        init();
        pre_Insert();
        solve();
    }
}

[HDOJ3435 A new Graph Game]【最佳匹配】

【题目大意】

给出N个点,M条带权无向边。

让你选一些边,使得能用多个圈把图上所有点精确覆盖。输出所有圈的长度之和。

如果不存在这样的圈,输出-1.

【算法分析】

这个是经典题。。。

如果整个图是一个哈密顿回路的话,那么每个点的度都为2。

那么我们把每个点拆开。并给哈密顿圈定义一个方向。

那么一个负责出度,一个负责入度。最佳匹配即可。

【其它】1Y

【CODE】

#include const int N=20005;
const int E=200005;
const int INF=1000000000;
int n,m,e,S,T,cost,Flow;
int v[N],d[N],L[N];
struct edge{int x,y,c,w;edge *next,*op;}g[E],*ls[N],*fa[N];

void addedge(int x,int y,int c,int w){
g[++e].x=x; g[e].y=y; g[e].c=c; g[e].w= w; g[e].next=ls[x]; ls[x]=&g[e]; g[e].op=&g[e+1];
g[++e].x=y; g[e].y=x; g[e].c=0; g[e].w=-w; g[e].next=ls[y]; ls[y]=&g[e]; g[e].op=&g[e-1];
}

void init(){
e=0;
memset(ls,0,sizeof(ls));
int i,x,y,w;
scanf("%d%d",&n,&m);
S=0; T=n+m+1;
for (i=1;i<=m;i++){
scanf("%d%d%d",&x,&y,&w);
addedge(x,y+n,1,w);
addedge(y,x+n,1,w);
}
for (i=1;i<=n;i++){
addedge(n+i,T,1,0);
addedge(S,i,1,0);
}
}

bool spfa(){
int i,head=0,tail=1;
for (i=S;i<=T;i++){
d[i]=INF;
v[i]=0;
}
d[S]=0; v[S]=1; L[1]=S;
while (head!=tail){
head++; if (head>=N) head=0;
for (edge *t=ls[L[head]];t;t=t->next)
if (t->c && d[t->x]+t->wd[t->y]=d[t->x]+t->w;
fa[t->y]=t;
if (!v[t->y]){
v[t->y]=1;
tail++; if (tail>=N) tail=0;
L[tail]=t->y;
}
}
v[L[head]]=0;
}
return d[T]!=INF;
}

void change(){
cost+=d[T];
for (int i=T;i!=S;i=fa[i]->x){
fa[i]->c–;
fa[i]->op->c++;
}
Flow++;
}

void MCMF(){
cost=Flow=0;
while (spfa()) change();
if (Flow!=n) puts("NO");
else printf("%dn",cost,Flow);
}

int main(){
int Tc,i;
scanf("%d",&Tc);
for (i=1;i<=Tc;i++){
printf("Case %d: ",i);
init();
MCMF();       
}
}

[HDOJ3433 A Task Process]【二分答案】【dp验证】

【题目大意】

有n个人,X件A工作,Y件B工作。然后第i个人做一件A工作需要Ta[i],做一件B工作需要Tb[i]。最后输出,完成给定的工作至少需多少时间。

【算法分析】

就是二分答案然后dp。 F[i][j]表示决策完第i个人,A工作完成了j件,B工作最多能完成多少件?然后转移比较沙茶,那么略去。

【其他】

1Y。这几天被GDOI的作业弄得半死。。。该做的基本做了~不管了。现在开始做多校联赛的题。。。这题是送分的A题。。。

【CODE】

#include

#include

#include #define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)<(y)?(x):(y))
const int N=55;
const int INF=100000000;
int n,X,Y;
int Ta[N],Tb[N];
int F[N][205];

void init(){
     scanf("%d%d%d",&n,&X,&Y);
     for (int i=1;i<=n;i++)
       scanf("%d%d",&Ta[i],&Tb[i]);
}

bool Can(int Limit){
     int i,j,k;
     for (i=0;i<=n;i++)
       for (j=0;j<=X;j++)
         F[i][j]=-INF;
     F[0][0]=0;
     for (i=1;i<=n;i++)
       for (j=0;j<=X;j++)
         for (k=0;k<=j;k++)
           if ((j-k)*Ta[i]<=Limit)
           F[i][j]=max(F[i][j],F[i-1][k]+(Limit-(j-k)*Ta[i])/Tb[i]);
     return F[n][X]>=Y;
}

void solve(){
     int l=0,r=400000,mid;
     while (l           mid=l+r>>1;
           if (Can(mid)) r=mid;
                    else l=mid+1;
     }
     printf("%dn",l);
}

int main(){
    int Tc,i;
    scanf("%d",&Tc);
    for (i=1;i<=Tc;i++){
        printf("Case %d: ",i);
        init();
        solve();
    }
}

[POJ2187 Beauty Contest]【凸包+旋转卡壳】

【题目大意】
求平面距离最远的点对。
【算法分析】
凸包。
旋转卡壳求出对踵点。
就是弄两个向量,一个贴着凸包上某条边,另一个于该向量平行并与凸包相交,并使得这两个向量把整个图都夹在中间。
【其它】
以前n^2水过。。。于是重写。
另外我这种转法不是按最小角度转的,于是凸包上要注意去掉共线3点的情况。
去这种情况有点小技巧。。。。就是先判他们凸包连续的3点是否共线。然后在构造两个向量。
P:L[i-1]->L[i]
Q:L[i]->L[i+1]
然后用点积判他们方向是否相同,如果相同那就删,否则是不能删的。。。
(YY一下只有两个点的凸包)
【CODE】
#include #include #include #include #define dis(A,B) ((A.x-B.x)*(A.x-B.x)+(A.y-B.y)*(A.y-B.y))
using namespace std;
const int N=100005;
struct Point{int x,y;}a[N],L[N],Std;
int n,tot,v[N];

int Del_cmp(const void *A,const void *B){
Point P=*((Point*)A),Q=*((Point*)B);
if (P.x!=Q.x) return P.x-Q.x;
return P.y-Q.y;
}

void Delsame(){
qsort(a+1,n,sizeof(Point),Del_cmp);
int i,t=1;
for (i=2;i<=n;i++)
if (a[i].x!=a[i-1].x || a[i].y!=a[i-1].y)
a[++t]=a[i];
n=t;
}

int CJ(Point Z,Point A,Point B){
return (A.x-Z.x)*(B.y-Z.y)-(A.y-Z.y)*(B.x-Z.x);
}

int cmp(const void *A,const void *B){
int res=CJ(Std,*((Point*)A),*((Point*)B));
if (res) return res;
return (((Point*)A)->x+((Point*)A)->y-((Point*)B)->x-((Point*)B)->y);
}

void TB(){
Point P,Q,tmp,M;
int i,j;
M.x=M.y=0x7FFFFFFF;
for (i=1;i<=n;i++)
if (a[i].x M=a[i];
j=i;
}
tmp=a[1]; a[1]=a[j]; a[j]=tmp;
Std=a[1];
qsort(a+2,n-1,sizeof(Point),cmp);
L[1]=a[1];
a[n+1]=a[1];
for (tot=1,i=2;i<=n+1;i++){
while (tot>1 && CJ(L[tot-1],L[tot],a[i])>0) tot–;
L[++tot]=a[i];
}
tot–;
}

void Del_Useless_Point(){
if (tot<3) return;
Point P,Q;
int i,j;
for (i=1;i<=tot;i++) v[i]=0;
L[0]=L[tot]; L[tot+1]=L[1];
for (i=1;i<=tot;i++)
if (CJ(L[i-1],L[i],L[i+1])==0){
P.x=L[i].x-L[i-1].x;
P.y=L[i].y-L[i-1].y;
Q.x=L[i+1].x-L[i].x;
Q.y=L[i+1].y-L[i].y;
if (P.x*Q.x+P.y*Q.y>0) v[i]=1;
}
n=0;
for (i=1;i<=tot;i++)
if (!v[i])
a[++n]=L[i];
}

int max(int x,int y){return x>y?x:y;}
int min(int x,int y){return x int i,j,ans=0;
Point P,Q;
bool check;
Std.x=Std.y=0;
a[0]=a[n]; a[n+1]=a[1];
for (i=j=1;i<=n;i++){
for (;;j=j==n?1:j+1){
check=true;
P.x=a[i+1].x-a[i].x;
P.y=a[i+1].y-a[i].y;

Q.x=a[j+1].x-a[j].x;
Q.y=a[j+1].y-a[j].y;
if (CJ(Std,P,Q)<0) check=false; Q.x=a[j-1].x-a[j].x;
Q.y=a[j-1].y-a[j].y;
if (CJ(Std,P,Q)<0) check=false;
if (check) break;
}
ans=max(ans,dis(a[i],a[j-1]));
ans=max(ans,dis(a[i],a[j]));
ans=max(ans,dis(a[i],a[j+1]));
}
printf("%dn",ans);
}

int main(){
int i,j;
while (scanf("%d",&n)!=EOF){
for (i=1;i<=n;i++) scanf("%d%d",&a[i].x,&a[i].y);
Delsame();
TB();
Del_Useless_Point();
switch (n){
case 0:puts("0"); break;
case 1:puts("0"); break;
default:RC(); break;
}
}
return 0;
}

[HDOJ3425 Coverage]【求圆与线段覆盖部分的一个沙茶方法。。。】

【题目大意】
给定一个线段,和n个圆。
如果线段上某点被某一个圆覆盖,那么就算被覆盖。
最终问被覆盖点占线段总长的百分比。
【算法分析】
圆与线段交点有很多很快的方法,但是我介绍一种异常沙茶,巨慢,却不大容易错的方法。(实际上就是不用分类什么的而已。。。)
首先,对线段两点建立一个向量,然后就可以利用向量伸缩来很方便的表示该线段。
对于一个线段和一个圆。我们先用点到圆心距离为函数。由于他的单峰性质,二分得那个离圆最近的点。
(好像人们都比较喜欢三分,在此表示不解)
现在我们得到了圆覆盖掉的线段上的一个点。
于是我们现在就可以在这个点的基础上,向线段的两个端点爬山。
(就是设一个步长,如果爬出去就停止,并不停将步长除以2)
然后爬完了。得到那两个边缘点就是圆覆盖了线段的两个边缘点。
整个算法复杂度O(lg K) K为坐标距离除以eps。
【一些小技巧】
设向量为(x1,y1) -> (x2,y2)
那么我们就可以用一个数rate表示线段上的点。(其中0<=rate<=1)
具体就是( x1+rate*(x2-x1) , y1+rate*(y2-y1) )。
然后就比较方便了。
【对于本题】
本题的话,就是可以按每个圆覆盖点对的前端rate值排序。
然后就很容易贪心得到每部分极长被覆盖距离。
【其它】
程序有点货不对板。。。求圆与线段的一个交点时用的直接暴力枚举,不是二分。。。
【CODE】
#include #include #include #include #include #define sqr(x) ((x)*(x))
using namespace std;
const int N=125;
const int Times=3000;
const double Start_Step=100;
const double eps=1e-6;
struct Point{int x,y;}p[N];
int n,cnt;
int R[N];
struct Line{double St,Ed;}L[N];

void op(){
for (int i=1;i<=n+2;i++) swap(p[i].x,p[i].y);
}

inline bool Cover(double x,int i){
if (x double y=p[1].y+(x-p[1].x)/(p[2].x-p[1].x)*(p[2].y-p[1].y);
return sqr(R[i])>=sqr(x-p[i].x)+sqr(y-p[i].y);
}

void work(){
n+=2;
double sx=(double)(p[2].x-p[1].x)/Times,sy=(double)(p[2].y-p[1].y)/Times;
int i,j;
cnt=0;
for (i=3;i<=n;i++){
double x=p[1].x,y=p[1].y;
for (j=0;j<=Times;j++){
if (Cover(x,i)){
cnt++;
double tx=x,ty=y,step;
for (step=Start_Step;step>eps;step/=2)
while (Cover(x-step,i)) x-=step;
L[cnt].St=x;
x=tx;
for (step=Start_Step;step>eps;step/=2)
while (Cover(x+step,i)) x+=step;
L[cnt].Ed=x;
break;
}
x+=sx; y+=sy;
}
}
}

int cmp(const void *A,const void *B){
Line AA=*((Line*)A),BB=*((Line*)B);
if (AA.St if (AA.St>BB.St) return 1;
return 0;
}

void solve(){
qsort(L+1,cnt,sizeof(Line),cmp);
double now=0,Sum=0,last;
for (int i=1;i<=cnt;i++){
if (i==1 || last Sum+=now;
now=L[i].Ed-L[i].St;
last=L[i].Ed;
}
else{
if (L[i].Ed>last){
now+=L[i].Ed-last;
last=L[i].Ed;
}
}
}
Sum+=now;
printf("%.2lfn",Sum/(p[2].x-p[1].x)*100);
}

int main(){
while (1){
scanf("%d",&n); if (!n) return 0;
scanf("%d%d%d%d",&p[1].x,&p[1].y,&p[2].x,&p[2].y);
for (int i=3;i<=n+2;i++)
scanf("%d%d%d",&p[i].x,&p[i].y,&R[i]);
if (abs(p[1].x-p[2].x) if (p[1].x>p[2].x) swap(p[1],p[2]);
work();
solve();
}
}