Codeforces Beta Round #69 (Div. 1 Only) 【E题还是不会】

【A. Heroes】

就是一个DFS枚举,然后模拟。

最坏时间3^7 * 7^2

 

【B. Falling Anvils】

【题目大意】

给定一个方程。然后p的范围是[0,a],q的范围是[-b,b]。

p,q在各自区间上等概率分布。

问这个方程有解的概率。

【算法分析】

就是求p-4q>=0的概率。

然后把p看成x,q看成y。然后画出对应的平面图形分类讨论一下即可。高中数学课本的例题阿

代码就这样

        if (fabs(b)<1e-9) ret=1;

        else

        if (a

            ret=(2*b+a/4)/(4*b);

        }

        else{

            ret=1-b/a;

        }

注意b=0的情况。被坑了一次。

 

 

【C. Beavermuncher-0xFF】

【题目大意】

给定一棵有根树,每个点上有w[i]只鱼(其实题目是海狸= =),一开始你在根上。你每走一条边x->y,那么y上要少一条鱼。如果y上没鱼,那么不能走边x->y。现在要求你结束时依然需要站在根处,问你最多能干掉多少条鱼(即最多能走多少步)。

n<=10^5

【算法分析】

首先很容易发现我们可以把走出去的路线分解为很多圈圈,每个圈圈只包含两个点。

于是我们可通过点来划分路径。

考虑一棵以p为根的子树

我们可以以p为分界点把路径分隔开。

那么p下面怎么走和上面无关,只要剩下一条鱼在p上,就足够把p这个子树拼接到fa上。

我们考虑p上的鱼消失的两种方式。

A、从儿子走回来

B、从fa走下来

如果能选A,我们肯定尽量选A。因为如果选了B,那么必须要走回fa那儿去,那么就要同时消耗fa上鱼的数目,这就会影响以后的决策,使得后面取得的鱼变少。

于是这个贪心算法就很显然了。令F[i]表示以i为根,绕一圈回到i最多能吃到多少鱼。(如果i不为根,那么要求最后i上剩的鱼>=1,否则>=0)。

然后每次将自己的儿子按F[i]排个序。然后拼接就好了。

如果拼接完还有在i上的鱼多出来,那么就找个儿子卡能不能抵消。最后输出F[i]即可。

附代码

#include

#include

#include

#include

#include

using namespace std;

const int N=100005;

const int oo=0x80000000;

int n,S;

int w[N];

int fa[N];

int Q[N];

long long F[N];

vector

 

void bfs(){

    int head=0,tail=1;

    Q[1]=S;

    fa[S]=0;

    while (head!=tail){

        head++;

        int x=Q[head];

        int L=G[x].size();

        for (int i=0;i

            int y=G[x][i];

            if (fa[x]!=y){

                fa[y]=x;

                Q[++tail]=y;

            }

        }

    }

}

 

bool cmp(int i,int j){

    return F[i]>F[j];

}

 

void solve(){

    for (int i=1;i<=n;i++) F[i]=oo;

    for (int j=n;j>=1;j–){

        int low_limit= (j==1) ? 0 : 1;

        int x=Q[j];

        int L=G[x].size();

        F[x]=0;

        sort(G[x].begin(),G[x].end(),cmp);

        for (int i=0;i

            int y=G[x][i];

            if (fa[x]!=y){

                if (w[x]>low_limit && w[y]>0){

                    F[x]+=F[y]+2;

                    w[x]–;

                    w[y]–;

                }

            }

        }

        for (int i=0;i

            int y=G[x][i];

            if (fa[x]!=y){

                if (w[x]>low_limit && w[y]>0){

                    int delta=min(w[x]-low_limit,w[y]);

                    F[x]+=delta*2;

                    w[x]-=delta;

                    w[y]-=delta;

                }

            }

        }

    }

    cout << F[S] << endl;

}

 

int main(){

    freopen("input.txt","r",stdin);

    scanf("%d",&n);

    for (int i=1;i<=n;i++) scanf("%d",&w[i]);

    for (int x,y,i=1;i

        scanf("%d%d",&x,&y);

        G[x].push_back(y);

        G[y].push_back(x);

    }

    scanf("%d",&S);

    bfs();

    solve();

}

 

【D. Domino Carpet】

【题目大意】

经过一些人肉转换以后,题目变成了m*n个格子,这些格子有3种类型。

1、只允许被2*1的方块覆盖的。

2、只允许被1*2的方块覆盖的。

3、允许被1*2或2*1的方块覆盖的。

然后用2*1或者1*2的格子去完美覆盖这些格子。问有多少种方案。

n,m<=250

还有一个重要条件,假如放了一块1*2的方块占在(i,j)和(i,j+1),那么就不能存在其它的1*2方块放在(k,j-1)(k,j)或(k,j+1)(k,j+2)。

【算法分析】

由于那个重要条件,我们可以一列一列地dp转移。

要么直接整列竖的2*1方块。要么就是存在1*2方块的两列。

第一种很好算直接F[j+1]+=F[j]。

第二种可以在里面套一个dp。G[i][0]表示到达第i行,还没有放过1*2的方块,这时候的方案数。G[i][1]就是放过2*1的方块的方案数了。

然后F[j+2]+=G[m][1]*F[j]。

 

【E. Martian Food】

= =,算了10页草稿纸发现算不出来。唉,太弱小了。不会捉。

 

 

、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、

比赛时前3题都是迅速反应出算法,但是第3题写的时候,头脑有点混,悲剧写挂了。今天重打一遍不到10分钟就1Y了

然后就前两题那个沙茶分数排到了DIV I 150+。居然还涨rating了

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跌得叫一个惨啊……

哎,我太蒟蒻了。