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

 

 

posted @ 2013-05-22 12:13  yefeng1627  阅读(194)  评论(0编辑  收藏  举报

Launch CodeCogs Equation Editor