2013-5-20 训练赛后总结

题目来源: 2012天津现场赛

A, 背景为麻将的模拟题,按照要求模拟就好。

B,sqrt(N)分解因子,然后暴力算即可

#include<cstdio>

#include<cstdlib>

#include<cstring>

#include<string>

#include<algorithm>

using namespace std;



int sum(int x,int b){

    int res = 0;

    while(x){

        res += (x%b)*(x%b);

        x /= b;

    }

    return res;

}

char mp[50] = "0123456789ABCDEF";



int main(){

    int n, m;

    while( scanf("%d%d", &n,&m) != EOF){

        int ans = 0;

        for(int i = 1; i*i <= n; i++){

            if( n%i == 0 ){    

            if( i*i == n ){

                ans += sum(i,m);        

            }

            else{

                ans += sum(i,m);

                ans += sum(n/i,m);

            }

            }

        }

        int a[50], n1 = 0;

        while(ans){

            a[n1++] = ans%m;

            ans /= m;

        }

            

        for(int i = n1-1; i >= 0; i--){    

            printf("%c", mp[a[i]]);    

        } puts("");

    }

    return 0;

}
View Code

 

C,dp( L, x, y ) 表示 前L-2位完全匹配,L-1,L分别为x,y的方案数, 然后转移有三类:

  L-1 / L / L+1,     三个都单独转,两个一起, 三个一起, 推一下就可以得到O(1)计算出来

  转移方程可以写成  dp( L+1, y1, z )  = Min{ dp(L, x, y ) + cost(三个变换)  }

#include <cstdlib>

#include <cstring>

#include <cstdio>

#include <iostream>

#include <algorithm>

using namespace std;

#define MAX(a,b) (a)>(b)?(a):(b)

#define MIN(a,b) (a)<(b)?(a):(b)

#define count Count

const int N = 1010;

int s[1005], e[1005];

char str[1005], str2[1005];

int n, dp[1005][11][11];

const int INF = 0x3f3f3f3f;

int c3[10][10][10][10][10][10];

int c2[10][10][10][10];

int c1[10][10];



int count(int x1, int x2, int up) {

    if (up) {

        if (x2 >= x1) return x2 - x1;

        else return 10-(x1 - x2);

    } else {

        if (x2 <= x1) return x1 - x2;

        else return 10-(x2 - x1);

    }

}

int change1(int x1,int x2){

    return min( count(x1,x2,1), count(x1,x2,0) );

}

int change2(int x1,int x2,int y1,int y2){

    int x_up = count(x1,x2,1), y_up = count(y1,y2,1);

    int x_down = count(x1,x2,0), y_down = count(y1,y2,0);

    return min( max(x_up,y_up), max(x_down,y_down) ); 

}

int change3(int x1,int x2,int y1,int y2,int z1,int z2){

    int x_up = count(x1,x2,1), y_up = count(y1,y2,1), z_up = count(z1,z2,1);

    int x_down = count(x1,x2,0), y_down = count(y1,y2,0), z_down = count(z1,z2,0);

    int res = INF;

    if( y_up<=x_up && y_up<=z_up ) res = min(res,min(10+y_up,x_up+z_up-y_up));

    else res = min( res, max(x_up,max(y_up,z_up)) );

    if( y_down<=x_down && y_down<=z_down ) res = min(res,min(10+y_down,x_down+z_down-y_down));

    else res = min( res, max(x_down,max(y_down,z_down)));

}



int comp(int x1,int x2,int y1,int y2,int z1,int z2){

    int t = INF;

    t = min( t, c1[x1][x2] + c1[y1][y2] + c1[z1][z2] );

    t = min( t, c1[x1][x2] + c2[y1][y2][z1][z2] );

    t = min( t, c2[x1][x2][y1][y2] + c1[z1][z2] );

    t = min( t, c3[x1][x2][y1][y2][z1][z2] );

    //t = min( t, change1(x1,x2)+change1(y1,y2)+change1(z1,z2) );

    //t = min( t, change1(x1,x2)+change2(y1,y2,z1,z2) );

    //t = min( t, change2(x1,x2,y1,y2)+change1(z1,z2) );

    //t = min( t, change3(x1,x2,y1,y2,z1,z2) );

    return t;

}

void init(){

    for(int x1 = 0; x1 < 10; x1++){

        for(int x2 = 0; x2 < 10; x2++){

            c1[x1][x2] = change1(x1,x2);

            for(int y1 = 0; y1 < 10; y1++){

                for(int y2 = 0; y2 < 10; y2++){

                    c2[x1][x2][y1][y2] = change2(x1,x2,y1,y2);

                    for(int z1 = 0; z1 < 10; z1++){

                        for(int z2 = 0; z2 < 10; z2++){

                            c3[x1][x2][y1][y2][z1][z2] = change3(x1,x2,y1,y2,z1,z2);    

                        }    

                    }

                }    

            }

        }    

    }

}

void solve() {

    memset(dp,0x3f,sizeof(dp));

    for(int y = 0; y < 10; y++){

        dp[1][0][y] = change1(s[1],y);        

    }

    for(int L = 1; L < n; L++)

    {    

        for(int x = 0; x < 10; x++)

            for(int y = 0; y < 10; y++)    {

                if( dp[L][x][y] == INF ) continue;

                for(int y1 = 0; y1 < 10; y1++)

                    for(int z = 0; z < 10; z++){

                        int t = comp(x,e[L-1],y,y1,s[L+1],z );    

                        dp[L+1][y1][z] = min( dp[L+1][y1][z], dp[L][x][y] + t );        

                    }

            }

    } 

    

    printf("%d\n", dp[n][e[n-1]][e[n]] );

}



int main() {

//    printf("(5,2,1) = %d, (2,2,1) = %d\n", change3(5,9,2,9,1,5), change3(2,9,2,9,1,5) );

    init();

    while (scanf("%s %s", str + 1, str2 + 1) != EOF) {

        n = strlen(str+1);

        s[0] = e[0] = 0;    

        for (int i = 1; i <= n; ++i) {

            s[i] = str[i] - '0';

            e[i] = str2[i] - '0';

        }

        solve();

    }

    return 0;

}
View Code

 

D, N个点围成一圈, 每个点有个权值,可以修改: i变为 -Ai,  i-1与i+1位置+Ai, 不会捉。。。

E. 第i个城市建加油站花费为 2^i, 比 (1,i-1)全部建加油站的和还大,可见,若i城市不建加油站而在 (1,i-1)这些位置多建一些这样更优。 那么我们可以最初令所有城市都建,然后从N到1开始尝试删除加油站,看是否合法。

  然后就是删除一个加油站判定问题, 因为未规定一个点只能走一次,要求长度最大为K, 那么当一个点从一个有加油站的城市出发,要么到有加油站的城市,或者到没有加油站的城市, 此时若全部点需要走到,并且能够回到1, 则必定在那些没有加油站的城市能够返回,那么此时从加油站的城市到非加油站的城市 路径长度必须要少于或等于 K/2,这样才能保证返回,另外 从有加油站的城市 到 有加油站的城市 则只要距离小于等于K即可。必定可以返回。 另外,我们假定这样一个结论: 所有非加油站城市 都能通过至少一个 加油站城市 到达。 因为若 从一个 加油站城市 经过 多个非加油站城市 再到 加油站城市, 其路径总长度必定小于等于K, 那么意味着 这两个加油站城市 直接距离必定小于等于K,意味着我们不需要通过 非加油站 城市到达 加油站城市。  所以我们能够通过 BFS,若是加油站城市则 入队,然后判定是否能够走到所有顶点至少一次。

#include<cstdlib>

#include<cstdio>

#include<cstring>

#include<algorithm>

#include<queue>

#include<cmath>

using namespace std;



const int N = 150;

const int inf = 0x3f3f3f3f;

struct node{

    int x, y;

}city[N];



int mp[N][N];

bool vis[N], sta[N];

int n, K;



int dist(int i,int j){

    return ceil( sqrt( (city[i].x-city[j].x)*(city[i].x-city[j].x)+(city[i].y-city[j].y)*(city[i].y-city[j].y) ) );

}

bool legal(){

    queue<int>Q; while( !Q.empty() ) Q.pop();

    memset(vis,0,sizeof(vis));

    Q.push(0);

    vis[0] = true;

    while( !Q.empty() ){

        int cur = Q.front(); Q.pop();

        for(int nxt = 0; nxt < n; nxt++){

            if( cur == nxt ) continue;

            if( !vis[nxt] ){

                if( sta[nxt] ){

                    if( mp[cur][nxt] <= K )

                        vis[nxt] = true, Q.push(nxt);

                }    

                else{

                    if( mp[cur][nxt] <= K/2 ) 

                        vis[nxt] = true;

                }

            }

        }

    }

    for(int i = 0; i < n; i++)

        if( !vis[i] ) return false;

    return true;

}

int main(){

    while( scanf("%d%d",&n,&K) != EOF){

        for(int i = 0; i < n; i++){

            scanf("%d%d", &city[i].x, &city[i].y );

        }    

        for(int i = 0; i < n; i++)

            for(int j = i; j < n; j++){

                if( i == j ) mp[i][j] = inf;

                else mp[i][j]=mp[j][i]=dist(i,j);

            }

        for(int i = 0; i < n; i++) sta[i] = true;

        if( legal() == false ){ puts("-1"); continue; }

        else{

            for(int i = n-1; i > 0; i-- ){

                sta[i] = false;

                if( !legal() ) sta[i] = true;

            }    

        }

        int k;    

        for(k = n-1; k > 0; k--) 

            if( sta[k] ) break;

        for( ; k >= 0; k-- )

            printf("%d",sta[k]);

        puts("");

    }    

    return 0;

}
View Code

 

F.  所有字串问题,听说后缀数组能做,用后缀自动机写会方便。果断去学习下。。。。

G. swap, 依据定义神码是 cup,  虽然矩阵很小,但是还是不知道如何计算。

H.  对于AB顺序,求出X获得 A,B,A+B分的期望, 与 BA顺序,获得A,B,A+B的期望,比较下即可。

#include<cstdio>

#include<cstring>

#include<cstdlib>

const double esp = 1e-8;

double sign(double x){

    return x<-esp?-1:(x>esp);

}

int main(){

    int T;

    scanf("%d", &T);

    while( T-- ){

        int A, B;

        double P, Q;

        scanf("%d%d%lf%lf", &A,&B,&P,&Q);

        double P1 = 1-P, Q1 = 1-Q;    

        double tiger = A*( Q1+Q*P*P1 )+B*(Q*P*P1)+(A+B)*(Q*P*P); 

        double wolf = A*(Q1*P1*P) + B*(Q+Q1*P*P1) + (A+B)*(Q1*P*P); 

        if( sign(tiger-wolf) >= 0 ){

            printf("tiger %.4lf\n", tiger );    

        }    

        else printf("wolf %.4lf\n", wolf );    

    }

    return 0;

}
View Code

I.  题目没看,看到别人题解似乎是一道数学题。。。

J. 给定的是多个连通块,然后连通块内部顶点有边,边上有权值, 然后不同连通块间只能坐地铁,问坐地铁不超过K次游览各个连通快最大权值。。。暂时无思路。。。

K.  使用数据结构 Splay Tree 来维护, 题目主要要解决 插入-x位置问题, 因为要满足 队列进出序列,意味着 +x之前的正整数对应的出队操作完成后-x才能出去,之间还可以有一些 数+y进队,但是-x一定要在这些+y出队前出去,所以可以得出 -x的位置,假定  +x左边的正整数的数量为 k,  则-x的位置应该为 左数第k+1个负数位置前一位, 若序列无k+1个负数,则在最后一位。 解决这个问题就好做了。 因为要序列中未出现的最小正整数,我们用个堆放进1-n。然后每次insert就删掉一个最小,若remove就将x加入进去。logN即可。

  insert i ,操作, 通过将 i-1旋转到根,再将i旋转到根下,然后插入的节点就是i的左儿子了。这很好做,-x问题就是上面说到的,先找到位置,找到了就与插入+x是一样的了。查找第K+1的负数,与整数的问题,我们可以通过添加一个节点信息来保存子树 负数个数,正数个数来解决。 多个标记即可。还有就是为了方便下面的remove 与 query操作,我们将 键值为 x的 +x,-x在 splay中的指针记录起来,这样就方便 对其进行删除与查询了。我的程序是用 loc[x][2] 分别记录 x, -x 对应的节点指针 p。

  remove x,  因为前面记录了loc[x][2],其+x,-x在splay中的节点指针, 则我们对于+x删除, 将其左边第一个与右边第一个分别移动到根与根下,然后删除左儿子即可。注意要往上push_up;

  query x, 其实和remove差不多, 如果不太理解可以看看关于 splay维护 数列区间的论文。

最后要注意的是,求和  (1+10^5)*10^5 极端情况可能溢出。。要用64 int 。

#include<cstdio>

#include<cstring>

#include<cstdlib>

#include<queue>

#include<string>

#include<algorithm>

using namespace std;

#define keytree ch[ ch[rt][1] ][0]

#define ft ch[rt][1]

const int N = (int)4e5+100;

typedef long long LL;

struct Splay{

    int ch[N][2],pre[N],sz[N];

    int num[N][2], key[N];

    LL sum[N];

    int rt, top;

    void rotate(int x,int d){

        int y = pre[x];

        ch[y][d^1] = ch[x][d]; pre[ ch[x][d] ] = y;

        if( pre[y] ) ch[pre[y]][ ch[pre[y]][1] == y ] = x;

        pre[x] = pre[y]; ch[x][d] = y; pre[y] = x;

        push_up(y);    

    }

    void splay(int x,int goal){

        while( pre[x] != goal ){

            int y = pre[x];

            if( pre[y] == goal ) rotate( x, ch[y][0]==x );

            else{

                int z = pre[y];

                int d1 = ch[z][0]==y, d2 = ch[y][0]==x;

                if( d1 == d2 ) rotate(y,d1), rotate(x,d2);

                else rotate(x,d2), rotate(x,d1);

            }

        }    

        if( goal == 0 ) rt = x;

        push_up(x);

    }

    void rotate_to(int k,int goal){ //左边有k个,不包含自身的情况下

        int x = rt;

        while( sz[ ch[x][0] ] != k ){

            if( sz[ ch[x][0] ] >= k ) x = ch[x][0];

            else k -= (sz[ch[x][0]]+1), x = ch[x][1];

        }

        //printf("x = %d\n", x );    

        splay( x, goal );    

        push_up(x);    

    }

    void NewNode(int &x, int c, int f){

        x = ++top;

        ch[x][0] = ch[x][1] = 0;

        pre[x] = f; sz[x] = 1;

        num[x][0] = (c>0); num[x][1] = (c<0);

        key[x] = sum[x] = c;

    }    

    void init(){

        // init the null point

        ch[0][0]=ch[0][1]=pre[0]=sz[0]=0;

        num[0][0]=num[0][1]=key[0]=sum[0]=0;

        top = rt = 0;    

        

        // Two Virtual Point

        NewNode( rt, 0, 0 );

        NewNode( ch[rt][1], 0, rt );

        push_up(rt);



    //    print(rt);    

    }    

    

    int Kth(){

        int k = num[ ch[rt][0] ][0] + 1; //左数第n+1个负数位置

        if( num[rt][1] < k ) return sz[rt]-1;

        else{

            int x = rt, s = 0;

            while(1){

                int d = num[ ch[x][0] ][1];

                if( d >= k ) x = ch[x][0];

                else if( d+(key[x]<0) == k ) 

                    return (s+sz[ ch[x][0] ]);

                else{

                    s += 1+sz[ ch[x][0] ];

                    k -= ( d + ( key[x] < 0 ) );

                    x = ch[x][1];

                }    

            }

        }

    }

    void push_up(int x){

        if(x){

            sum[x] = key[x] + sum[ ch[x][0] ] + sum[ ch[x][1] ];

            sz[x] = 1 + sz[ ch[x][0] ] + sz[ ch[x][1] ];

            num[x][0] = (key[x]>0) + num[ ch[x][0] ][0] + num[ ch[x][1] ][0];

            num[x][1] = (key[x]<0) + num[ ch[x][0] ][1] + num[ ch[x][1] ][1];    

        }    

    }

    int insert(int val,int pos){

        //printf("Insert: pos = %d\n", pos );    

        rotate_to( pos-1, 0 );

        rotate_to( pos, rt );

        NewNode( keytree, val, ft );

        push_up( ft ), push_up(rt);

        //printf("keytree =  %d\n", keytree );

        splay( keytree, 0 );



        return rt;

    }

    void remove(int x){

        splay(x, 0);

        int lch = ch[x][0], rch = ch[x][1];

        while( ch[lch][1] ) lch = ch[lch][1];

        while( ch[rch][0] ) rch = ch[rch][0];



        splay( lch, 0 ); splay( rch, rt );

        ch[ft][0] = 0;

        push_up(ft); push_up(rt);



    }

    LL query(int x, int y){

        splay(x,0); splay(y,rt);    

        return sum[keytree];

    }

    void print(int x){

        if( ch[x][0] ) print( ch[x][0] );

        printf("节点指针%d, 键值:%d, 父节点:%d, 左儿子:%d, 右儿子:%d,sum = %lld, sz = %d, num[0] = %d, num[1] = %d\n",

                x, key[x], pre[x], ch[x][0], ch[x][1], sum[x], sz[x],

                num[x][0], num[x][1] );

        if( ch[x][1] ) print( ch[x][1] );

    }

}spt;



int loc[N][2];



int main(){

    freopen("1.in","r",stdin);    

    int n, Case = 1;

    while( scanf("%d", &n) != EOF){

        printf("Case #%d:\n", Case++ );    

        // init    

        spt.init();    

        priority_queue<int,vector<int>,greater<int> > Q;

        for(int i = 1; i <= n; i++) Q.push(i);

        // operate    

        char op[10];

        int x, pos;

        for(int i = 0; i < n; i++){

            scanf("%s", op);

            if( op[0] == 'i' ){ //insert pos

                scanf("%d", &pos);    

                x = Q.top(); Q.pop();

                loc[x][0] = spt.insert( x, pos+1 ); // must splay(x,0) ,the rt is x    

            //    spt.print( spt.rt ); puts("*******");    

            

                pos = spt.Kth();

                loc[x][1] = spt.insert( -x, pos );    

            //    spt.print( spt.rt );    

            }

            else if( op[0] == 'r' ){ //remove x

                scanf("%d", &x);    

                spt.remove( loc[x][1] );

                spt.remove( loc[x][0] );

                Q.push(x); 

            }    

            else{ // query x

                scanf("%d", &x);    

                LL res = spt.query( loc[x][0], loc[x][1] );    

                printf("%lld\n", res );    

            }    

        }

    }

    return 0;

}
View Code

 

 

你可能感兴趣的:(总结)