练习赛13 A滑动窗口 B dp+树状数组维护前缀和 C博弈 G dfs序+线段树 H思维 Ifloyd J模拟

A
题意:给出数组,和一个窗口大小m,在每个区间[i , i + m - 1 ],求最大值maxrating和i的异或,A为所有区间这个值的和,还要求从这个区间开始maxrating为-1,找到大的count就加1,还要求count和i的异或,B为所有区间这个值的和。
A=∑( i=1 , n−m+1) (maxratingi⊕i)
B=∑(i=1 , n−m+1)(counti⊕i)
思路:双端队列实现的滑动窗口,倒着来,每个区间的最大值就是队头元素,比较次数就是队列里的元素个数。

Code;

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define LL long long
using namespace std;
const int AX = 2e7+66;
LL a[AX];
LL deq[AX];
//deque<int>deq;
int main(){
    int T;
    scanf("%d",&T);
    int n , m , k , p , q , r , mod ;
    while( T-- ){
        scanf("%d%d%d%d%d%d%d",&n,&m,&k,&p,&q,&r,&mod);
        for( int i = 1 ; i <= k ; i++ ){
            scanf("%d",&a[i]);
        }
        for( int i = k + 1 ; i <= n ; i++ ){
            a[i] = ( 1LL * p * a[i-1] + 1LL * q * i + r ) % mod;
        }
        int h = 1 , t = 0;
        LL A = 0LL , B = 0LL;
        for( int i = n ; i ; i -- ){
            while( h <= t && a[i] >= a[deq[t]] ) t --;
            deq[++t] = i;
            if( i + m - 1 <= n ){
                while( deq[h] >= i + m ) h++;
                A += i ^ ( a[deq[h]] );
                B += i ^ ( t - h + 1 );
            }
        }
        printf("%lld %lld\n",A,B);  
    }
    return 0 ;
}   

B

题意:给一个数组,求有多少个严格上升的子序列
思路:
dp[i]表示以i结尾的上升子序列个数,dp[i] = Σdp[j] + 1 a[i] > a[j] .
先离散化下,然后用树状数组维护前面比他小的 结果的和 , 每次加1然后更新dp[i],最后询问最大的数(离散化后最大为n)的结果。
Code:

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define LL long long
using namespace std;
const int AX = 1e5+66;
const LL MOD = 1e9+7;
LL a[AX];
LL b[AX];
LL c[AX];
map<int,int>mp;
LL n ;
int lowbit(int x){
    return x&(-x);
}
void update( LL value , int site){
    while( site <= n ){
        c[site] = ( c[site] + value ) % MOD;
        site += lowbit(site);
    }
}
LL getsum(int i){
    LL sum = 0;
    while( i >= 1 ){
        sum = ( sum + c[i] ) % MOD ;
        i -= lowbit(i);
    }
    return sum % MOD;
}
int tot ;
std::vector<int> v;
int get_id( int x ){
    //return lower_bound( v.begin() , v.end() , x ) - v.begin() + 1 ;
    if( !mp[x] ) mp[x] = ++tot;
    return mp[x];
} 
int main(){
    int T;
    scanf("%d",&T);
    while( T-- ){
        memset( c , 0 , sizeof(c) );
        mp.clear();
        tot = 0 ;
        //v.clear();
        scanf("%lld",&n);
        for( int i = 1 ; i <= n ; i ++ ){
            scanf("%lld",&a[i]); b[i] = a[i];
            //v.push_back(a[i]);
        }
        //sort( v.begin() , v.end() ) ; v.erase( unique(v.begin(),v.end()) , v.end() );
        sort( b + 1 , b + n + 1 );
        for( int i = 1 ; i <= n ; i++ ) b[i] = get_id(b[i]);
        for( int i = 1 ; i <= n ; i++ ) a[i] = get_id(a[i]);

        for( int i = 1 ; i <= n ; i++ ){
            LL ans = getsum( a[i] - 1 ) % MOD;
            update( ans + 1 , a[i] );
        }
        LL res = getsum( n ) % MOD;
        cout << res << endl;
    }
    return 0 ;
}

C
题意:地上有一个长和宽分别为a,b的矩形,然后两位玩家先后依次在这个矩形内画半径为r的圆,所画的圆可以相切但不能相交,直到某个玩家不能再往其中画圆了则判定此玩家失败,另一玩家获胜。
思路:先手想让后手输,只能找一个没有对称位置的地方画圆,否则后手一直找对称位置,先手就输了,所以矩形的中心就是制胜的地方。先手能放中间就赢。
Code:

#include <bits/stdc++.h>
using namespace std;
int main(){
    int T;
    scanf("%d",&T);
    while( T -- ){
        int a , b , r ;
        scanf("%d%d%d",&a,&b,&r);
        if( a >= 2 * r && b >= 2 * r ){
            printf("The first one is winner.\n");
        }
        else{
            printf("The second one is winner.\n");
        }
    }
    return 0;
}

G
题意:给出边的相连关系,以及每个节点的值,有两个操作,一个是将节点的值乘一个数,一个是查询节点及其子节点的乘积和乘积的因子个数。
另外题目说明,数据的素因子最大不超过13。
思路:dfs序+线段树维护区间乘积。
因为要查询每个节点及其子树的乘积,想到线段树可以维护区间的乘积以及约数个数。如果我能够将查询操作转换成区间,
有节点改变,就是单点更新,同时对 操作要乘上的数 求出素因子并计数更新(加)到节点。查询操作就是区间查询。。
那么怎么去构造这样一个区间呢,这就需要用到dfs序。
记录每个节点的dfs序的起始位置,查询就变成了区间查询。
比如:
这里写图片描述
dfs序:01367245
那么1节点的开始位置就是2,结束位置就是5.
L[1] = 2 ,R[1] = 5
0节点是到最后才遍历完子树,所以:
L[0] = 1 R[0] = 8
Code:

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define LL long long
using namespace std;
const int AX = 1e5+66;
const int MOD = 1e9+7;
const int prime[10] = { 2 , 3 , 5 , 7 , 11, 13 };
struct Edge{
    int to ;
    int next1;
}G[AX];
int cnt ;
int head[AX];
int tot ;
void addEdge( int u , int v ){
    G[++tot].to = v ; G[tot].next1 = head[u] ; head[u] = tot ;
}
int val[AX];
int L[AX];
int R[AX];
bool vis[AX];

struct Res{
    LL a ;
    int fac[10];
    Res(){
        a = 1LL;
        memset( fac , 0 , sizeof(fac) );
    }
    Res operator + ( const Res &rhs )const{
        Res ans ; ans.a = ( rhs.a * a ) % MOD;
        for( int i = 0 ; i <= 5 ; i++ ) ans.fac[i] = fac[i] + rhs.fac[i];
            return ans ;
    }
};

int n;
Res s[AX*20];

void pushUp( int rt ){
    s[rt] = s[rt<<1] + s[rt<<1|1];
}

void getFac( Res &p, int v ){
    for( int i = 0 ; i <= 5 ; i++ ){
        for( ; !(v % prime[i]) ; v /= prime[i] ) p.fac[i] ++;
    }
}

void update( int site , int val , int l , int r , int rt ){
    if( l == r ){
        s[rt].a = (LL)( s[rt].a * (LL)val ) % MOD;
        getFac( s[rt] , val );
        return ;
    }
    int mid = ( l + r ) >> 1 ;
    if( site <= mid ) update( site , val , l , mid , rt << 1 );
    else update( site , val , mid + 1 , r , rt << 1 | 1 );
    pushUp(rt);
}

Res query( int L , int R , int l , int r , int rt ){
    if( L <= l && R >= r ){
        return s[rt] ;
    }
    Res ans ;
    int mid = ( l + r ) >> 1 ;
    if( L <= mid ) ans = ans + query( L , R , l , mid , rt << 1 );
    if( mid < R ) ans = ans + query( L , R , mid + 1 , r , rt << 1 | 1 );
    return ans ;
}

void dfs( int x ){
    vis[x] = true;
    L[x] = ++cnt; 
    update(L[x],val[x],1,n,1);
    for( int i = head[x] ; ~i ; i = G[i].next1 ){
        if( !vis[G[i].to] )
            dfs( G[i].to );
    }
    R[x] = cnt ;
}

int main(){
    scanf("%d",&n);
    tot = 0;
    cnt = 0;
    memset( vis , false , sizeof(vis) );
    memset( head , -1 , sizeof(head) );
    int u , v ;
    for( int i = 1 ; i < n ; i++ ){
        scanf("%d%d",&u,&v);
        u ++ , v ++;
        addEdge( u , v ); 
    }
    for( int i = 1 ; i <= n ; i++ ){
        scanf("%d",&val[i]);
    }
    dfs(1);
    int Q;
    scanf("%d",&Q);
    char op[20];
    int x , y ; 
    while( Q-- ){
        scanf("%s%d",op,&x);
        x++;
        if( op[0] == 'R' ){
            Res res = query( L[x] , R[x] , 1 , n , 1 );
            LL num = 1LL;
            for( int i = 0 ; i <= 5 ; i++ ){
                num = num * (LL)( res.fac[i] + 1 ) % MOD ;
            }
            printf("%d %d\n",(int)res.a,(int)num);
        }else{
            scanf("%d",&y);
            update( L[x] , y , 1 , n , 1 );
        }
    }
    return 0 ;
}   

H

题意:初始0,有两种A : 2*x+1 或者B:2*x+2,求得到n的方式。
思路:n逆着来得到0即可。。
这题刚开始选择性失明看不到1e9的范围,上来没多想一顿bfs,ME 3次气急败坏就是没反应过来范围。。心态不好。。。
Code:

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
const int AX = 3e5+66;
char res[AX];
int main(){
    int n ;
    int tot = 0;
    cin >> n ;
    while( n ){
        if( n % 2 ){
            n = ( n - 1 ) / 2 ;
            res[tot ++] = 'A';
        }else{
            n = ( n - 2 ) / 2 ;
            res[tot ++] = 'B';
        }
    }
    for( int i = tot - 1 ; i >= 0 ; i-- ){
        cout << res[i] ;
    }cout << endl;
    return 0 ;
}

I
题意:给出几个星球,还有坐标,任意星球的距离是欧几里得距离(直线距离),有的星球间有黑洞,单向的,给出的询问两个星球,输出最短距离。
思路:floyd。黑洞的距离置为0即可。
Code:

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f3f3f3f3f
#define LL long long 
using namespace std;
int n;
map<string,int>mp;
map<int,string>mp1;
struct Node{
    LL x  , y , z;
};
map<int,Node>co;
int chong[105][105];
double cal( Node a , Node b ){
    return ( (double)sqrt((double)(a.x - b.x) * (a.x - b.x) + (double)(a.y-b.y)*(a.y-b.y) + (double)(a.z-b.z)*(a.z-b.z)) );
}
double dis[105][105];

void floyd( ){
    for( int k = 1 ; k <= n ; k++ ){
        for( int i = 1 ;  i <= n ; i++ ){
            for( int j = 1 ; j <= n ; j++ ){
                dis[i][j] = min( dis[i][j] , dis[i][k] + dis[k][j] );
            }
        }
    }
}

int main(){
    int T;
    scanf("%d",&T);
    int Case = 0 ;
    while( T-- ){
        co.clear();
        mp.clear();
        LL x , y , z;
        scanf("%d",&n);
        string s;
        for( int i = 1 ; i <= n ; i++ ){
            cin >> s >> x >> y >> z;
            mp[s] = i;
            co[i] = (Node){ x , y , z };
        }
        int q; 
        string s1, s2;

        for( int i = 1 ; i <= n ; i++ ){
            for( int j = 1 ; j <= n ; j++ ){
                dis[i][j] = cal( co[i] , co[j] );
            }
        }

        cin >> q ;
        for( int i = 0 ; i < q ; i ++ ){
            cin >> s1 >> s2;
            dis[mp[s1]][mp[s2]] = 0;
        }
        floyd();
        cin >> q ;
        cout << "Case " << ++Case << ':' << endl;
        for( int i = 0 ; i < q; i++ ){
            cin >> s1 >> s2;
            printf("The distance from %s to %s is %.0f parsecs.\n",s1.c_str(),s2.c_str(),dis[mp[s1]][mp[s2]]);
        } 
    }
    return 0 ;
}

J

题意:给两个表,要求输出相似的范围。就是哪一段的字符串相同位置可以不同。
思路:相同的话就直接是1,否则 因为每个字符串不同,给每个字符串一个值,就记录这一段的和,如果和相等说明这一段的字母相同。
Code:

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define LL long long
using namespace std;
const int AX = 1e6+66;
map<string,int>mp;
int a[AX];
int b[AX];
int res[AX];
int main(){
    int T;
    ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    cin >> T;
    int n ;
    while( T-- ){
        cin >> n ;
        string s ;
        for( int i = 1 ; i <= n ; i++ ){
            cin >> s ;
            mp[s] = i;
            a[i] = i ;
        }
        for( int i = 1 ; i <= n ; i++ ){
            cin >> s;
            b[i] = mp[s];
        }
        int f = 0 ;
        int tot = 0 ;
        LL suma = 0LL ;
        LL sumb = 0LL ;
        int l = 0 ; 
        for( int i = 1 ; i <= n ; i++ ){
            if( !f ){
                if( a[i] == b[i] ){
                    res[tot++] = 1 ;
                    suma = 0LL;
                    sumb = 0LL;
                }else{
                    suma += a[i] ;
                    sumb += b[i] ;
                    f = 1;
                    l = i ;
                }
            }else{
                suma += a[i];
                sumb += b[i];
                if( suma == sumb ){
                    res[tot++] = i - l + 1 ;
                    suma = 0LL;
                    sumb = 0LL;
                    f = 0 ;
                }
            }
        }
        for( int i = 0 ; i < tot ; i++ ){
            cout << res[i] ;
            if( i != tot - 1 ) cout << ' ';
        }
        cout << endl;
    }
    return 0 ;
}

猜你喜欢

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