ACM-ICPC 2018 徐州赛区网络预赛 A 组合数学,递推 F map模拟 H树状数组 J LCA+最大生成树

版权声明:本文为博主原创文章,未经博主允许也可以转载。 https://blog.csdn.net/FrankAx/article/details/82561862

A
题意:n给人围成一圈,有2^k 个面具,编号0-2^k -1 ,问分配方法,满足相邻两个人分配的面具编号:~(i^j) (异或且非)为正数,可以重复分配面具
思路:题目可以转化成: 每个数x都只有一个不能相邻的数2^k - x, 求出所有满足条件的组合。
只有一个人肯定是2^k 个面具随便放;
两个人时就是2^k * (2 ^ k - 1 )种;
那么n >= 3 时,我们考虑:第一个与n-1 个位置的关系。
①当1位置的数 != n - 1 位置的数
a( 1 )!= a(n-1)
那么 第n位限制条件有两个数不能放,所以 dp[n] = dp[n-1] * ( 2 ^ k - 2 )
但是这样考虑会多算一种情况,即 dp[n-1] 内已经包含了 a(1) == a(n-1)的情况。(即使首尾相等,仍然能够相接)。
因此,a(1) != a(n-1) && a(1) 与 a(n-1) 能相接的情况则是dp[n-1] - dp[n-2] * 1 (不能相接就是n-2个人添加第n-1个人时,a(n-1) == 2 ^k - a(1) ),
假如a(1) 与a(n-1) 不能相接(a(n-1) == 2^k - a(1)),那么我们就要继续考虑a[n-2],a(n-2)与a(n-1) 不相同的话,就是dp[n-2] - dp[n-3] ,相同就继续往下考虑,直到最后dp[n-1] - dp[n-2] + dp[n-2] - dp[n-3] +…- dp[1].
得到准确的情况数是:dp[n-1] - dp[1]
所以:( dp[n-1] - 2 ^ k ) * ( 2 ^ k - 2 )
②a(1) == a(n-1)
结果是:dp[n-2] * ( 2 ^ k - 1)
因此,总共的情况是:dp[n]= (dp[n-2](2^k-1) + (dp[n-1] - 2^k) (2^k-2))
Code:

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define ll long long 
using namespace std;
const ll MOD = 1e9 + 7 ;
const int MAXN = 1000076;
ll dp[MAXN];
ll quick( ll a , ll b ){
    ll ans = 1ll ;  
    while( b ){
        if( b & 1 ){ 
            ans = ( ans * a ) % MOD ;
        } 
        b >>= 1 ;
        a = ( a * a ) % MOD ;
    }  return ans ; 
}
int main(){
    int T ; 
    ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0) ;
    cin >> T;
    ll n , k ;
    while( T-- ){
        cin >> n >> k ; 
        dp[1] = quick(2,k)%MOD; 
        dp[2] = ( quick(2,k) * ( ( quick(2,k)+MOD-1 ) % MOD ) % MOD ) % MOD ;
        for( int i = 3 ; i <= n ; i++ ){
            dp[i] = ( dp[i-2] * (quick(2,k)+MOD-1)%MOD + (( dp[i-1] - quick(2,k) + MOD ) % MOD ) * ( quick(2,k) - 2 + MOD ) % MOD  ) % MOD ;
        }
        cout << dp[n] << endl;
    }
    return 0 ;
}

F
题意:给n个框架,每个给k个坐标,问最长的连续出现的坐标值长度。
思路:map直接记录就行了,只不过需要注意的是同一个框架会出现相同坐标。
Code:

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define LL long long 
using namespace std;
typedef pair<int,int>P;
map<P, int> mp ;
map<P, int> num ; 
int main(){
    int T;
    scanf("%d",&T);
    while( T-- ){
        int k ; 
        mp.clear();
        num.clear();
        int n ; 
        scanf("%d",&n);
        int x , y ;
        int res = 0 ; 
        for( int i = 1 ; i <= n ; i++ ){
            scanf("%d",&k);
            for( int j = 0 ; j < k ; j++ ){
                scanf("%d%d",&x,&y);
                if( mp[(P(x,y))] == i ) continue;
                if( mp[P(x,y)] == i - 1 ){
                    num[P(x,y)] ++ ;
                    res = max( res , num[P(x,y)] ) ;
                }else{
                    num[P(x,y)] = 1 ;
                }
                mp[P(x,y)] = i ; 
            }
        }
        printf("%d\n",res);
    }
    return 0 ;
}

H
题意:阅读L,R区间内的书可以获得知识量:
a[L] * ( R - L + 1 ) + a[L+1] * ( R - L ) ….. + a[R] * 1
给两个操作1 , L ,R ,查询看L,R页的书能够获得多少知识
2 , L , R : 将L位置的值更改为R

思路:两个树状数组,一个维护长度为n-i+1,以i为左边界,n右边界的知识和。
一个维护前缀和。
当查询L,R内的知识量时,首先用第一个树状数组求出的是a[L]* ( n - L + 1 ) + …+a[R] * ( n - R + 1 ) , 第二个树状数组求出的是 a[L] + ….+ a[R] ,
但是我们需要查询a[L] * ( R - L + 1 ) + a[L+1] * ( R - L ) ….. + a[R] * 1
可以看出中间相差 n - R
那么结果就是:第一个树状数组的结果 - ( n - r ) * 第二个树状数组的结果。
更新时,需要查询l位置原来的值tmp,然后加上r-tmp更新之。
Code:

#include <bits/stdc++.h>
#define LL unsigned long long 
using namespace std ;
const int AX = 1e5+66;
LL c[AX];
LL c1[AX];

int lowbit( int x ) {
    return x & (-x) ; 
}

void update( int site , LL val ){
    while( site < AX ){
        c[site] += val ;
        site += lowbit(site) ;
    }
}

LL query( int site ){
    LL ans = 0ULL ;
    while( site > 0 ){
        ans += c[site];
        site -= lowbit(site);
    }
    return ans ; 
}

void update1( int site , LL val ){
    while( site < AX ){
        c1[site] += val ;
        site += lowbit(site) ;
    }
}

LL query1( int site ){
    LL ans = 0ULL ;
    while( site > 0 ){
        ans += c1[site];
        site -= lowbit(site);
    }
    return ans ; 
}

int main(){
    int n , q ; 
    LL x ;
    scanf("%d%d",&n,&q); 
    for( int i = 1 ; i <= n ; i++ ){
        scanf("%lld",&x);
        update( i , 1ULL * ( n - i + 1 ) * x );
        update1( i , x ) ;
    }
    int op ; 
    int l , r ; 
    while( q-- ){
        scanf("%d%d%d",&op,&l,&r);
        if( op == 1 ){
            LL ans = query( r ) - query( l - 1 ) ;
            LL ans1 = 1ULL * ( n - r ) * ( query1( r ) - query1( l - 1 ) ) ;
            printf("%lld\n",ans - ans1);
        }else{
            LL tmp = query1( l ) - query1( l - 1 ) ;
            update( l , 1ULL * ( r - tmp ) * ( n - l + 1 ) ) ;
            update1( l , 1ULL * ( r - tmp ) ) ;
        }
    }
    return 0 ; 
}

J

思路:既然迷宫花费最小,那么任意两点可到达,就是最大生成树。然后转化为树上任意两点之间的距离,用LCA处理,这里用的是离线的Tanjan算法。
Code:

#include <bits/stdc++.h>
#define LL long long 
using namespace std;
const int AX = 1e6+666;
const int MAXN = 1e6;
struct Node{
    int u ;
    int v ;
    int w ; 
    bool operator < ( const Node &a ) const{
        return a.w < w; 
    }
}e[AX];
int n , m ;
int tot ;
int pre[AX];
int fa[AX];
int vis[AX];
int ans[AX];
int dis[AX];
struct node{
    int to , w; 
    node( int a , int b ){
        to = a; w = b ; 
    }
};
std::vector<int> vec[AX];
std::vector<node> Q[AX];
void addedge( int u , int v , int w ) {
    e[tot].u = u ; e[tot].v = v ; e[tot++].w = w;
}   
void init(){
    memset( ans , 0, sizeof(ans) ) ;
    memset( dis , 0 , sizeof(dis) ) ;
    memset( vis , 0 , sizeof(vis) ) ;
    for( int i = 1 ; i <= MAXN ; i++ ){
        pre[i] = i ; 
        fa[i] = i ; 
        vec[i].clear();
        Q[i].clear();
    }
} 

int find( int x ){
    return x == pre[x] ? pre[x] : pre[x] = find(pre[x]);
}
void mix( int x , int y ){
    int xx = find(x) ;
    int yy = find(y) ;
    if( xx != yy ){
        pre[yy] = xx ;
    }
}
void Kruscal(){
    sort( e , e + tot );
    for( int i = 0 ; i < tot ; i++ ){
        if( find(e[i].u) != find(e[i].v) ){
            mix( e[i].u , e[i].v ) ;

            vec[e[i].u].push_back(e[i].v);
            vec[e[i].v].push_back(e[i].u);
        }
    }
}

int find_fa( int x ){
    return x == fa[x] ? fa[x] : fa[x] = find_fa( fa[x] ) ;
}

void Tarjan( int x , int f ){
    fa[x] = x ;
    vis[x] = 1 ;
    for( int i = 0 ; i < vec[x].size() ; i++ ){
        if( vec[x][i] == f ) continue;
        int v = vec[x][i] ; 
        dis[v] = dis[x] + 1 ;
        Tarjan( v , x );
    }
    for( int i = 0 ; i < Q[x].size() ; i++ ){
        int v = Q[x][i].to;
        if(vis[v]){
            ans[Q[x][i].w]= dis[x] + dis[v] - 2 * dis[find_fa(v)] ;
        }
    }
    fa[x] = f ;
}

int main(){
    scanf("%d%d",&n,&m);
    char op[5];
    int d , r ; 
    tot = 0 ;
    init();
    for( int i = 0 ; i < n ; i++ ){
        for( int j = 0 ; j < m ; j++ ){
            scanf("%s%d%s%d",op,&d,op,&r);
            int id = i * m + j + 1 ;
            if( i < n ){
                addedge( id , ( i + 1 ) * m + j + 1 , d );
            }
            if( j < m ){
                addedge( id , id + 1 , r );
            }
        }
    }
    Kruscal();
    int q;
    scanf("%d",&q);
    int x1 , x2 , y1 , y2 ;
    for( int i = 1 ; i <= q ; i++ ){
        scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
        x1-- ; x2--;
        int id1 = x1 * m + y1  ;
        int id2 = x2 * m + y2  ;
        Q[id1].push_back(node(id2,i));
        Q[id2].push_back(node(id1,i));
    }
    dis[1] = 0 ;
    vis[1] = 1 ;
    Tarjan(1,0);
    for( int i = 1 ; i <= q ; i++ ){
        printf("%d\n",ans[i]);
    }
    return 0 ; 
}

猜你喜欢

转载自blog.csdn.net/FrankAx/article/details/82561862