SRM 503

唉,跌rating了。

250是个很骗人的题目。。。我一开始很迅速地反应到结果只能是2,1,-1。。。然后构造的方法也YY了一下。写完过掉样例以后,突然觉得好像不太靠谱,有点忐忑。然后又跑去换了个dp。最后折腾到只有130+分了。其实很显然本身就是对的啊。。。

500这种求期望的题见到就挂啊。

总是喜欢求和然后除以总方案数这种YY方式。太弱了。我YY的时候甚至觉得每个排列不是等可能的(由于第二个条件)。

然而排列和后面的选取是分步进行的。所以每个排列是等可能的。

于是可以通过枚举边,然后算对应概率,最后加起来的方式求期望值。

Codeforces Beta Round #64 【Solution】

纪念第一套自己YY切完的CF、(其实同时也是第一套切完的CF)

T_T,题意略的都是没什么技术含量纯粹为完整性的题目

 

 

A题

题意略

比赛被hack了一把。F[0]=1啊T_T。

其实就是个dp,F[i]=F[j]*2^(i-1-j) 1<=j

然后输出F[i]即可

 

 

B题

题意略

把每个sentence看成一个块,然后可以说是dp,也可以说是贪心。

F[i]表示用i段最长能覆盖到哪里,显然F[i]=F[i-1]+尽量接……

于是看什么时候能覆盖到尾巴就行了。

 

 

C题

【题意】

给定3个数Max_x Max_y w,让你求一个数对(x,y)满足这样 1<=Max_x,Max_y<=10^5 w<=10^7

for (i=1;i<=x;i++)

  for (j=1;j<=y;j++)

     if (i*j==rev(i)*rev(j)) cnt++;

最后cnt>=w,并且满足x*y最小。

输出x y或者无解输出-1

其中rev函数表示翻转。比如说rev(123217)=712321

【算法分析】

首先把式子换一下变成

 i/rev(i)=rev(j)/j

从小到大枚举i,令g=gcd(i,rev(i)),然后对i/rev(i)约分一下,再围观另外一边,显然

((i/g)*k)/((rev[i]/g)*k) = rev(j) / j……其实也就算是说j是rev(i)/g的倍数……然后就发现其实答案最多是Max_x lg Max_y级别的……然后其实可以暴力。

我们枚举x,考虑找出每个x对应的最小的y,满足1<=i<=x && 1<=j<=y内包含满足条件的数对的数目>=w。

然后x从小到大枚举,那么y必然递减,这里可以不必二分。

最终时间复杂度O(n lg n) (n=max(Max_x,Max_y))

于是有了这种代码

    int i,j,Tmp=Max_y,r;

    for (i=1;i<=Max_x;i++){

        r=rev[i]/g[i];                                                          // g[i]=gcd(i,rev[i])

        for (j=r;j<=Max_y;j+=r)

          if ((lld)(i)*j==(lld)(rev[i])*rev[j]) add(j);                 //add(i)表示将Tr[i]+1

        while (Tmp>1 && Get(Tmp-1)>=w) Tmp–;           //Get(i)表示Tr[1]+Tr[2]+Tr[3]+…+Tr[i] (于是使用树状数组维护之)

        if (Get(Tmp)>=w && (lld)(i)*Tmp

            ans=(lld)(i)*Tmp;

            ret_x=i;

            ret_y=Tmp;

        }

    }

    if (ans==INF) cout << "-1n";

    else cout << ret_x << " " << ret_y << endl;

 

 

D题

【题目大意】

一开始点集S为空,然后有Q<=10^5个询问,询问有两种类型,第一种是在点集S中插入一个点,第二种是在问,一个点是否在由点集S所构成的凸包中。

【算法分析】

其实问题就在于动态维护凸包,对于第二种询问,我们只要尝试用这个点去更新凸包,如果需要更新,那么该点在凸包外,否则在凸包内。

我们考虑把凸包上的边逆时针定向:


于是他们都变成了向量,我们以atan2(x,y)给这些向量排序(其实就是极角排序),然后再定第一条的向量的起始点为原点。然后以该原点对每个点连一条向量。

这样向量划分出了很多个区域,注意因为是凸包,所以这些向量所夹的角都为(0,pi)之间。然后我们就可以通过类似二分的方法找出该删的一个向量在哪里位置,然后暴力枚举前后的向量判断能否删除(该删的向量必然是连续的)

特别地,如果新的那个点在那个>pi的区间内,那么我们只需要检测与原点相连的边即可得到该删的向量。

对于凸包中的一个向量(x1,y1)->(x2,y2),我们判断该不该删,就是判断(x2-x1,y2-y1)^(New.x-x1,New.y-y1)是否<0, 其中^表示叉乘……

特别地叉乘等于0时要特判这个点是否在线段(x1,y1)-(x2,y2)上。

考虑动态维护一个有序序列,我们可以使用平衡树来解决。这样每个点只能被插入以及删除一次,于是是平摊lg n的。(这里不一定需要Splay)

当然几何题免不了各种恶心情况……大家可以慢慢发掘……

该题算法不难,但是实现有点恶心……属于体力题……WA了N久……

 

E题

【题目大意】

给定一棵n<=180个结点的树(树上的边长度都为1),然后让你指定一些点作为关键点(指定一个关键点需要付出k的代价),设点i到离自己最近的关键点的距离为dis(i),那么这个点需要花费d[dis(i)]的代价。(d[]在数据中给出)

【算法分析】

这个题我觉得很不错,实现不麻烦,也挺考思维的。一开始看成是一般图了,后来发现是树囧、YY良久,终于切掉。

我的方法是……dp

F[i,j]表示处理到i这个点,并且离i最近的一个关键是j,这时候最小花费的费用是多少。然后从树的叶子往根推。

我们考虑处理第i个结点,那么离它最近的关键点j有3种情况

1、i要通过自己的父亲到达j

2、i通过其中一个儿子到达j

3、i=j(这种情况处理起来和第一种其实是一样的)

先设near[i][j]表示从i跑到j,走第一步跑到哪里。比如说从1->2->3->4,那么near[1][4]=2,near[4][1]=3。

再设mat[i][j]表示从i跑到j的路径长度。

注意上面提到的3种情况中有一些状态是不能通过儿子转移上来的。

Case 1:

设当前状态为F[p][j],儿子状态是F[i][k],那么如果near[i][k]=p,但是j!=k,那么显然没有j=k时优的……

理由如下:

i->p->……->k————————这是i到离自己最近关键点的路径

    p->……->j————————这是p到离自己最近关键点的路径

……既然p->……->j是最短的,那么显然i->p->……->j也是最短的T_T。

 

Case 2:

设当前状态为F[p][j],儿子状态时F[i][k],那么如果near[p][j]=i,但是j!=k,那么显然没有j=k时优的……

理由类似上面:

p->i->……->j

     i->……->k

然后你们懂的T_T

 

于是就可以转移了。你们可能觉得这不太正确……那么继续来YY。

考虑F[i,j]所包含的信息,在i为根的子树里,如果不需要通过i跑上来的点,显然在下面已经指派完成了的不用管。

如果要通过i跑上来的点,那么它们所需要的点都是j,于是,F[i,j]所包含的信息对我们推导后面的状态是一样的,于是同样是达到(i,j)这种状态,我们只需要取最优的一个就可以了,所以这个状态定义是正确的……

由于一棵树有n条边,每次转移需要n^2,所以一共要n^3

下面附上代码。

 #include 

#include #include #include #include using namespace std;
typedef long long lld;
const lld INF=(lld)(1)<<50;
int n,price;
int w[205];
lld mat[205][205];
int near[205][205];
lld F[205][205];
int belong[205];

void Floyed(){
  int i,j,k;
  for (k=1;k<=n;k++)
    for (i=1;i<=n;i++)
      for (j=1;j<=n;j++)
      if (mat[i][k]+mat[k][j]        mat[i][j]=mat[i][k]+mat[k][j];
}

void Make_Near(){
  int i,j,k;
  for (i=1;i<=n;i++)
    for (j=1;j<=n;j++)
    if (i!=j){
      for (k=1;k<=n;k++)
        if (mat[k][j]==mat[i][j]-1 && mat[i][k]==1) near[i][j]=k;
    }
}

void dfs(int p,int fa){//dp的转移
  int i,j,k;
  for (i=1;i<=n;i++)
    if (mat[p][i]==1 && i!=fa)
      dfs(i,p);
  for (j=1;j<=n;j++)
    if (p==j)
    F[p][j]=price;                    //price是题目中提到的k,表示建一个关键点需要的代价
    else
    F[p][j]=w[mat[p][j]];             //w是题目中的d[]数组
  for (i=1;i<=n;i++)                    //F[p][j] Get message from  F[i][k]
    if (mat[p][i]==1 && i!=fa)          //i是p的儿子
      for (j=1;j<=n;j++){
      lld Min=INF;
      for (k=1;k<=n;k++){
      if (near[i][k]==p && k!=j) continue;      //文中提到的Case 1
      if (near[p][j]==i && k!=j) continue;      //文中提到的Case 2
      if (F[i][k]        Min=F[i][k];
      }
      F[p][j]+=Min;
    }
}

void Out(int p,int j,int fa){
  belong[p]=j;
  for (int i=1;i<=n;i++)
    if (mat[p][i]==1 && i!=fa){
    lld Min=INF;
    int Mini=0;
    for (int k=1;k<=n;k++){
      if (near[i][k]==p && k!=j) continue;
      if (near[p][j]==i && k!=j) continue;
      if (F[i][k]        Min=F[i][k];
        Mini=k;
      }
    }
    Out(i,Mini,p);
    }
}

void output(){
  lld Min=INF,Mini=0;
  for (int i=1;i<=n;i++)
    if (F[1][i]      Min=F[1][i];
      Mini=i;
    }
  cout << Min << endl;
  memset(belong,0,sizeof(belong));
  Out(1,Mini,0);
  for (int i=1;i<=n;i++) cout << belong[i] << " ";
  cout << endl;
}

int main(){
  scanf("%d%d",&n,&price);
  for (int i=1;i<=n;i++)
    for (int j=1;j<=n;j++)
      if (i==j)
      mat[i][j]=0;
      else
      mat[i][j]=INF;
  w[0]=0;
  for (int i=1;i  for (int x,y,i=1;i    scanf("%d%d",&x,&y);
    mat[x][y]=mat[y][x]=1;
  }
  Floyed();
  Make_Near();
  dfs(1,0);
  output();
}

[ZOJ1021 The Willy Memorial Program]【狗狗40题】【特别特别坑爹】

规模十分之小,但是特别特别坑T_T

一定要看清楚这一段话:

As an example, if we set the target point to level 4 in the left pipe in the figure above, the elapsed time for water to reach that target is assumed to be 5 (not 2), Also note that if the water reaches to the top of a pipe (say in level x), it won’t pour out outside the pipe until empty spaces in connected pipes below level x are filled (if can be filled, i.e. the level of water reaches the connecting links). (Note that there may be some links at level x, to which water is entered). After all such spaces are filled; the water level would not go up further.

然后别的怎么暴力都0MS……

搜狗V5啊,在学校上不了ZOJ,用了搜狗的全网加速就上得了啦……其实就是个代理T_T

const int N=55;
const int INF=0x7FFFFFFF;
int n,m,Target_i,Target_level;
struct Pipe{int x,y,h;}P[N];
struct Link{int p1,p2,y;}L[N];
int level[N];

void gao(){
    int i,j,k,ans=0;
    fill(level,level+n+1,INF);
    level[1]=P[1].y+P[1].h;
    while (1){ //退出条件:1、满了 2、达到target
        for (i=1;i<=n;i++)
          for (j=1;j<=n;j++)
            for (k=1;k<=m;k++)if (L[k].p1==j || L[k].p2==j){
              int x,y;
              if (L[k].p1==j) { x=L[k].p2; y=j; }
                         else { x=L[k].p1; y=j; }
              if (level[x]==INF && level[y]==L[k].y) level[x]=P[x].y+P[x].h;
            }
        int cnt=0,Max=-INF;
        bool check=true;
        for (i=1;i<=n;i++)
          if (level[i]!=INF && level[i]>Max)
              Max=level[i];
        for (i=1;i<=n;i++)
          if (level[i]!=INF && level[i]==Max)
            if (level[i]>P[i].y){
              cnt++;
              level[i]–;
            }
            elseif (level[i]==P[i].y)
              check=false;
        if (!check) cnt=0;
        if (cnt==0){
            puts("No Solution");
            return;
        }
        if (level[Target_i]            cout << ans << endl;
            return;
        }
        ans+=cnt;
    }
}

void add(int k,int x,int y,int l){
    int p1=0,p2=0;
    for (int i=1;i<=n;i++){
      if (y>=P[i].y && y<=P[i].y+P[i].h && P[i].x+1==x) p1=i;
      if (y>=P[i].y && y<=P[i].y+P[i].h && P[i].x==x+l) p2=i;
    }
    L[k].p1=p1;
    L[k].p2=p2;
    L[k].y=y;
}

int main(){
    int Tc,x,y,l;
    cin >> Tc;
    while (Tc–){
        cin >> n;
        for (int i=1;i<=n;i++) cin >> P[i].x >> P[i].y >> P[i].h;
        cin >> m;
        for (int i=1;i<=m;i++){
            cin >> x >> y >> l;
            add(i,x,y,l);
        }
        cin >> Target_i >> Target_level;
        if (Target_level<=P[Target_i].y || Target_level>P[Target_i].y+P[Target_i].h)
            puts("No Solution");
        else
            gao();
    }
}

一些数据:

3

7
10 4 6
7 0 13
12 0 13
3 2 8
0 7 6
1 0 6
5 5 7
13
2 1 5
2 3 1
1 9 2
1 12 4
4 8 1
11 10 1
8 13 4
8 2 4
4 4 3
8 5 2
4 6 1
6 7 1
6 11 1
7 12

3
0 0 1
2 1 1
4 1 2
2
1 1 1
3 2 1
2 2

2
0 0 3
2 3 1
1
1 3 1
1 3

 

Topcoder SRM 500 【Experience】

各路大神齐集,题目难到奇葩。

ACRush滑铁卢500和1000都挂了……

CLJ神犇爆-25……

还有两target是0的,红色爆0一堆……

然后我一进去我的房间,就看到一个熟悉的ID:felix-halim,然后还有个人拼命YM它、YY着到底是谁,突然想起是建这个网站的人:

http://felix-halim.net/uva/hunting.php?id=29277

UVA帝啊T_T(上面是AekdyCoin的号)

不过后来他爆0了。我们房子唯一一个红的也爆0了……

最后本房一共7个人有分,其中有一个是叉人得的分。

其余都是只pass了250pt……

由于我巨慢的手速和思考速度,T_T,我的250只有122.66分了,最后在DIV1里刚刚好200名。

于是rating就涨回去了,终于从Blue Boy变回Yellow Boy了!!!上次由于插件事件爆0那rating跌得叫一个惨啊……

哎,我太蒟蒻了。

Topcoder Member SRM485 DIV 1 1000pt

给定一个凸多边形包围住另一个凸多边形,外面的凸多边形上的每个点都向离自己的最远的在外凸多边形上的点连一条线。然后问,随意取一个在外凸多边形上的点,有多少的概率能取到对应线段与内多边形相交的点。

然后规模是TC的老规矩:50

然后算法就是暴力T_T。但是我暴力好像都写得很长。

第一步:

枚举每条外多边形上的边Point[i]->Point[i+1],然后再枚举Point[j]和Point[k],然后搞Point[j]->Point[k]的中垂线(这个有模板T_T),这个中垂线跑回来和Point[i]->Point[i+1]求交点(没交点就算了)。然后将这些交点按到Point[i]的距离排个序,去重。然后对于这个交点序列,每个点到下一个点形成了线段,然后就找一个到线段离两个端点最远的Point[t],然后这个线段对应的最远点就是Point[t]了。

第二步:

枚举外多边形上的点Point[i],然后枚举内多边形上的点,找到一左一右两个交点L和R(可以加多一层循环判是不是所有点都在它左边或者右边),然后向量Point[i]->L和向量Point[i]->R所夹的位置,就是能够穿过内多边形所对应的位置。然后枚举第一步求的交点序列,如果相邻交点间线段对应的最远点是Point[i],那么就用Point[i]->L和Point[i]->R这两个直线向它求交点,然后分情况讨论一下(我这里好像显然写长了)。

 

T_T,幸好调对第一个样例以后就Y了,不然得Debug个半死。

【CODE】

const double eps=1e-9;

    int sign(double x){
        if (x<-eps) return -1;
        if (x> eps) return1;
        return0;
    }

    int m,n;
    struct TPoint{
        double x,y;
        TPoint operator + (TPoint oth){
            TPoint ret;
            ret.x=x+oth.x;
            ret.y=y+oth.y;
            return ret;
        }
        TPoint operator – (TPoint oth){
            TPoint ret;
            ret.x=x-oth.x;
            ret.y=y-oth.y;
            return ret;
        }
        TPoint operator * (double oth){
            TPoint ret;
            ret.x=x*oth;
            ret.y=y*oth;
            return ret;
        }
        booloperator != (TPoint oth){
            return (sign(x-oth.x)!=0 || sign(y-oth.y)!=0);
        }
        booloperator == (TPoint oth){
            return sign(x-oth.x)==0 && sign(y-oth.y)==0;
        }
        doubleoperator ^ (TPoint oth){
            return x*oth.y-y*oth.x;
        }
    }Site[55],Dep[55];
   
struct TLine{
    double a,b,c;
};

TPoint Rot90(TPoint A){
    TPoint ret;
    ret.x=-A.y;
    ret.y=A.x;
    return ret;
}
   
TLine Get_Line(TPoint A,TPoint B){
    TLine ret;
    ret.a=A.y-B.y;
    ret.b=B.x-A.x;
    ret.c=B.y*A.x-B.x*A.y;
    return ret;
}

TPoint jiao(TLine A,TLine B){
    TPoint ret;
    ret.x=(A.b*B.c-A.c*B.b)/(A.a*B.b-B.a*A.b);
    ret.y=(A.c*B.a-A.a*B.c)/(A.a*B.b-B.a*A.b);
    return ret;
}

double dis(TPoint A,TPoint B){
    return sqrt((A.x-B.x)*(A.x-B.x) + (A.y-B.y)*(A.y-B.y));
}

    pair        pair        ret.second=false;
        TLine L2=Get_Line(A,B);
        if (sign(L.a*L2.b-L2.a*L.b)==0) return ret;
        else{
            ret.first=jiao(L,L2);
            if (sign(min(A.x,B.x)-ret.first.x)<=0 && sign(ret.first.x-max(A.x,B.x))<=0
            &&  sign(min(A.y,B.y)-ret.first.y)<=0 && sign(ret.first.y-max(A.y,B.y))<=0){
                if (ret.first==A || ret.first==B) return ret;
                ret.second=true;
                return ret;
            }
        }
        return ret;
    }
   
    vector     vector     vector     int tttt;
   
    bool cmp(TPoint A,TPoint B){
        return dis(A,Site[tttt])    }
   
    void Div_part(){
        int i,j,k,p;
        for (i=0;i            Cut[i].clear();
            Cut[i].push_back(Site[i]);
            Cut[i].push_back(Site[(i+1)%m]);
            for (j=0;j              for (k=j+1;k                  TPoint mid=(Site[j]+Site[k])*0.5;
                  TPoint dir=Rot90(Site[j]-Site[k]);
                  pair                  if (cross.second) Cut[i].push_back(cross.first);
              }
            tttt=i;
            sort(Cut[i].begin(),Cut[i].end(),cmp);
            TV.clear();
            for (j=0;j              if (j==0 || Cut[i][j]!=Cut[i][j-1]) TV.push_back(Cut[i][j]);
            Cut[i]=TV;
            best[i].clear();
            for (p=0;p                int now=0;
                for (j=1;j                  if ( sign( dis(Site[j],TV[p]) – dis(Site[now],TV[p]) )>=0 && sign( dis(Site[j],TV[p+1]) – dis(Site[now],TV[p+1]) )>=0 )
                    now=j;
                best[i].push_back(now);
            }
        }
    }
   
    double ans;
   
    void Rush(){
        int i,j,k,L,R;
        for (i=0;i            for (j=0;j                bool check=true;
                for (k=0;k                  if ( sign( (Dep[j]-Site[i])^(Dep[k]-Site[i]) ) > 0 )  check=false;
                if (check) { L=j; break; }
            }
            for (j=0;j                bool check=true;
                for (k=0;k                  if ( sign( (Dep[j]-Site[i])^(Dep[k]-Site[i]) ) < 0 ) check=false;
                if (check) {R=j; break;}
            }
            for (j=0;j              for (k=0;k                if (best[j][k]==i){
                    pair                    pair                    TPoint Lp=Cut[j][k];
                    TPoint Rp=Cut[j][k+1];
                    if (crossL.second){
                      if (crossR.second)
                          ans+=dis(crossL.first,crossR.first);
                      else{
                          if (sign( (Dep[L]-Site[i])^(Lp-Site[i]) )<=0 && sign( (Dep[R]-Site[i])^(Lp-Site[i]) )>=0 )
                              ans+=dis(Lp,crossL.first);
                          else
                              ans+=dis(Rp,crossL.first);
                      }
                    }
                    else{
                      if (crossR.second){
                          if (sign( (Dep[L]-Site[i])^(Lp-Site[i]) )<=0 && sign( (Dep[R]-Site[i])^(Lp-Site[i]) )>=0 )
                              ans+=dis(Lp,crossR.first);
                          else
                              ans+=dis(Rp,crossR.first);
                      }
                      else{
                          if (sign( (Dep[L]-Site[i])^(Lp-Site[i]) )<=0 && sign( (Dep[R]-Site[i])^(Lp-Site[i]) )>=0
                          &&  sign( (Dep[L]-Site[i])^(Rp-Site[i]) )<=0 && sign( (Dep[R]-Site[i])^(Rp-Site[i]) )>=0)
                              ans+=dis(Lp,Rp);
                      }
                    }
                }
        }
    }
class Deposit {
public:
    double successProbability(vector         m=siteX.size();
        n=depositX.size();
        for (int i=0;i            Site[i].x=siteX[i];
            Site[i].y=siteY[i];
        }
        for (int i=0;i            Dep[i].x=depositX[i];
            Dep[i].y=depositY[i];
        }
        double Sum=0;
        for (int i=0;i        ans=0;
        Div_part();
        Rush();
        return ans/Sum;
    }
};