练习赛3 B组合 C multiset D set G 计算几何矩形面积交/并 H模拟 I 组合数公式+逆元 J搜索 K添加限制条件的最短路

43 篇文章 1 订阅
34 篇文章 0 订阅

B.
题目链接:https://hihocoder.com/problemset/problem/1509
题意:给定一个长度为 n 的非负整数序列 a[1..n]
你需要求有多少个非负整数 S 满足以下两个条件:
(1).0 ≤ S < 260
(2).对于所有 1 ≤ i < n ,有 (a[i] xor S) ≤ (a[i+1] xor S)

思路:遍历每一对,找每一对第一个不相同的位,想要让前面的大于后面的则需要这样考虑:
1.如果a[i] 这位是1 , a[i+1] 这位是0,那么s这位必须为1
2.如果a[i] 这位是0 , a[i+1]这位是1,那么s这位必须是0
不满足条件就输出0,总共有60位,除了限定的位外,其他的都有2种情况,快速幂计算即可。
Code:

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#define LL long long
using namespace std;
const int AX = 1e5+66;
LL s[AX];
LL a[AX];
LL quick( LL a , LL b ){
    LL ans = 1LL;
    while( b ){
        if( b & 1 ){
            ans *= a;
        }
        b >>= 1 ;
        a *= a ;
    }
    return ans ;
}
int main(){
    int n;
    LL res = 1LL ;
    scanf("%d",&n);
    for( int i = 0 ; i < n ; i++ ){
        scanf("%lld",&a[i]);
    }
    int bit;
    for( int i = 0 ; i < 60 ; i++ ) s[i] = -1;
    for( int i = 0 ; i < n - 1 ; i++ ){
        for( int j = 59 ; j >= 0 ; j-- ){
            if( (1LL<<j) & ( a[i] ^ a[i+1] ) ){
                if( a[i] & (1LL<<j) ){
                    if( s[j] == -1 ) s[j] = 1;
                    if( !s[j] ){ return 0*printf("0\n"); }
                }else{
                    if( s[j] == -1 ) s[j] = 0;
                    if( s[j] ){ return 0*printf("0\n"); }
                }
                break;
            }
        }
    }
    int ans = 0 ;
    for( int i = 0 ; i < 60 ; i++ ){
        if( s[i] == -1 ) ans ++;
    }
    printf("%lld\n",quick(2,ans));
    return 0 ;
}

C
题意:给出事件,要求实时输出1队的名次。
思路:如果输入的不是1队的事件,需要判断原来名次是否高于1队,如果高于1队,就删除原来的更新。如果小于1队并且现在的大于1队,直接插入。
如果是1队,就清除set里面名次低于1队的。最后容器的尺寸就是比1队名次高的
Code:

#include <bits/stdc++.h>
using namespace std;
const int AX = 1e5+66;
int n , m ; 
struct Node{
    int num ;
    int p  ;
    Node( ){
        num = 0 ;
        p = 0 ; 
    }
}a[AX];

struct cmp{
    bool operator()( const Node &a , const Node &b ) const{
        return ( ( a.num == b.num && a.p > b.p ) || a.num < b.num );
    }
};

multiset<Node,cmp>s ;

bool operator < ( Node x , Node y ){ return ( x.num == y.num && x.p > y.p ) || ( x.num < y.num ) ;}
bool operator > ( Node x , Node y ){ return ( x.num == y.num && x.p < y.p ) || ( x.num > y.num ) ;}
bool operator <= ( Node x , Node y ){ return !( y < x ) ;}

int main(){
    int x , y ; 
    scanf("%d%d",&n,&m); 
    while( m -- ){
        scanf("%d%d",&x,&y);
        Node tmp = a[x];
        a[x].num ++ ;
        a[x].p += y ;
        if( x == 1 ){
            while( s.size() && (*s.begin()) <= a[1] ){
                s.erase(s.begin()); 
            }
        }else{
            if( a[1] < tmp ){
                s.erase(s.find(tmp));
                s.insert(a[x]);
            }else if( a[1] < a[x] ) s.insert(a[x]);
        }
        printf("%d\n",s.size()+1);
    }
    return 0 ;
}

D
题意:给一个序列,要求构造一棵树,每次去掉最小的叶子和所连的边,序列是跟叶子相连的点。
思路:set维护下叶子序列,如果去掉一个叶子节点使得序列中的节点为叶子,就加入。
Code:

#include <iostream>
#include <cstdio>
#include <string>
#include <set>
#include <map>
#include <sstream>
using namespace std;
const int AX = 7500+66;
int a[AX];
map<int,int>mp;
set<int>s[AX];
set<int> leaf ;
int main(){
    int n = 0 ;
    string tmp ;
    //while( ~scanf("%d",&a[n++]) );
    getline(cin,tmp);
    stringstream ss(tmp);
    int nx = 0 ;
    while( ss ){
        ss >> a[n++];
        if( nx < a[n-1] ) nx = a[n-1];
    }
    n -- ;
    for( int i = 0 ; i < n ; i++ ){
        mp[a[i]]++ ;
    }
    for( int i = 1 ; i <= nx ; i++ ){
        if( !mp[i] ){
            leaf.insert(i);
        }
    }
    for( int i = 0 ; i < n ; i++ ){
        mp[a[i]] -- ;
        if( leaf.size() ){
            s[a[i]].insert(*(leaf.begin()));
            s[*(leaf.begin())].insert(a[i]);
            leaf.erase(leaf.begin());
        }
        if( !mp[a[i]] ) leaf.insert(a[i]);
    }
    for( int i = 1 ; i <= nx ; i++ ){
        printf("%d:",i);
        int m = s[i].size();
        set<int>::iterator it = s[i].begin();
        for( ; it != s[i].end(); it++ ){
            printf(" %d",(*it));
        }printf("\n");
    }
    return 0 ;
}

G
题目链接:https://cn.vjudge.net/problem/FZU-2300
题意:
分别给定2个矩形的一个坐标以及长和宽,计算交的面积/并的面积。
思路:计算机和求交的面积,然后长乘宽算出总的面积-相交的就是并的。
那么只需要计算交的面积,首先确定两个矩形对角的点,通过交换构成下面的局面:
这里写图片描述
那么面积显而易见

Code:

#include <bits/stdc++.h>
#include <cstdio>
#include <algorithm>
const int AX = 1e5+66;
using namespace std;
#define swap(a,b) {double t;t=a;a=b;b=t;}
int main(){
    double x , y , w , h;
    double x1 , x2 ,x3 ,x4 ,x5 ,x6 ,x7 ,x8;
    double y1 ,y2 ,y3 , y4 , y5 , y6 , y7 , y8;
    int T;
    scanf("%d",&T);
    while( T-- ){
        double xx[5], yy[5];
        scanf("%lf%lf%lf%lf",&x,&y,&w,&h);
        double re1 = w * h ;
        x1 = x; y1 = y;
        x2 = x; y2 = y + h ;
        x3 = x + w ; y3 = y + h;
        x4 = x + w ; y4 = y;
        xx[0] = x2; yy[0] = y2;
        xx[1] = x4; yy[1] = y4;

        scanf("%lf%lf%lf%lf",&x,&y,&w,&h);  
        double re2 = w * h ;
        x5 = x; y5 = y ;
        x6 = x; y6 = y + h ;
        x7 = x + w ; y7 = y + h ;
        x8 = x + w ; y8 = y ;

        xx[2] = x6 ; yy[2] = y6;
        xx[3] = x8 ; yy[3] = y8;

        if (xx[0] > xx[1])
            swap(xx[0], xx[1]);
        if (xx[2] > xx[3])
            swap(xx[2], xx[3]);
        if (yy[0] > yy[1])
            swap(yy[0], yy[1]);
        if (yy[2] > yy[3])
            swap(yy[2], yy[3]);
        if (xx[1] <= xx[2] || xx[0] >= xx[3] || yy[0] >= yy[3] || yy[1] <= yy[2]) {
            printf("0.00\n");
            continue;
        }
        else {
            sort(xx, xx + 4);
            sort(yy, yy + 4);
            double sum = re1 + re2 ;
            double jiao = (xx[2] - xx[1]) * (yy[2] - yy[1]);
            double uni = sum - jiao; 
            printf("%.2lf\n", jiao / uni );
        }


    }
    return 0 ;
}

H. 题目链接:https://cn.vjudge.net/problem/FZU-2294
思路:模拟题,注意开unsigned long long
Code:

#include <iostream>
#include <cstdio>
#include <map>
#include <string.h>
using namespace std;
const int AX = 1e5+66;
const unsigned long long INF = 140737488355328ULL;
map<int,string>mp1;
map<string,int>mp;
map<int,unsigned long long>val;
int tot ;
int main(){
    ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    char op[50];
    string a,b;
    unsigned long long v;
    tot = 0 ;
    while( cin >> op ){
        cin >> a;
        if( !strcmp(op,"def") ){
            cin >> v;
            if( !mp[a] ){
                mp[a] = ++tot;
                mp1[tot] = a;
            }
            val[tot] = v; 
            cout << a << " = " << val[tot] << endl;
        }else if( !strcmp(op,"add") ){
            cin >> b;
            val[mp[a]] += val[mp[b]];
            val[mp[a]] %= INF;
            cout << mp1[mp[a]] << " = " << val[mp[a]] << endl;
        }else if( !strcmp(op,"sub") ){
            cin >> b;
            val[mp[a]] -= val[mp[b]];
            val[mp[a]] = ( val[mp[a]] + INF ) % INF;
            cout << mp1[mp[a]] << " = " << val[mp[a]] << endl;
        }else if( !strcmp(op,"div") ){
            cin >> b ;
            val[mp[a]] /= val[mp[b]];
            cout << mp1[mp[a]] << " = " << val[mp[a]] << endl;
        }else if( !strcmp(op,"mul") ) {
            cin >> b ;
            val[mp[a]] = ( val[mp[a]] * val[mp[b]] ) % INF;
            cout << mp1[mp[a]] << " = " << val[mp[a]] << endl;
        }else{
            cin >> b ;
            val[mp[a]] %= val[mp[b]];
            cout << mp1[mp[a]] << " = " << val[mp[a]] << endl;
        }
    }

    return 0 ;
}

I
这里写图片描述
Code:

#include <iostream>
#include <cstdio>
#define LL long long 
using namespace std;
const LL MOD = 1e9 + 7 ;
const int AX = 1e6+2;
LL inv[AX];
void init(){
    inv[1] = 1;
    for( int i = 2 ; i < AX ; i++ ){
        inv[i] = (LL) ( MOD - MOD / i ) * inv[MOD%i] % MOD;
    }
}

int main(){
    ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    int T;
    cin >> T;
    LL n , m ;
    init();
    while( T-- ){
        cin >> n >> m ; 
        if( n < m ){
            cout << n << endl;
        }else{
            //printf("%lld\n",((LL)((n+1)*m)%MOD*quick(m+1,MOD-2)%MOD)%MOD);
            cout << (n+1)*m%MOD*inv[m+1]%MOD << endl;
        }
    }
    return 0 ; 
}

J
题意:给m个数,剩下的是important数,如果unimportant数是两个不同important数的最近公共祖先,那么这也是important数,求important数的数量。
思路:对于给定的数搜其子节点,1标记为unimp数,0标记为imp数,2标记为有一个imp节点为其祖先。
Code:

#include <bits/stdc++.h>
using namespace std;
const int AX = 1e5+66;
int tot ;
int head[AX<<1];
bool vis[AX];
int mark[AX];
int a[AX];
int f[AX];
struct Node{
    int v , next;
}G[AX<<1];
void init(){
    tot = 0 ; 
    memset( G , 0 , sizeof(G) ) ;
    memset( head , -1 , sizeof(head) );
    memset( f , -1 , sizeof(f) );
    memset( vis , false , sizeof(vis) );
    memset( mark , 0 , sizeof(mark));
}
void addEdege( int u , int v ){
    G[tot].v = v ;
    G[tot].next = head[u]; head[u] = tot++;
}

void build( int x , int pre ){
    for( int i = head[x] ; ~i ; i = G[i].next ){
        int v = G[i].v ; 
        if( v == pre ) continue;
        f[v] = x;
        build( v , x );
    }
}

void dfs( int x ){
    vis[x] = true ;
    for( int i = head[x] ; ~i ; i = G[i].next ){
        int v = G[i].v ; 
        if( v == f[x] ) continue;
        if( mark[v] == 1 && !vis[v] ) dfs(v);
        if( mark[v] == 2 || !mark[v] ){
            if( mark[x] == 1 ) mark[x] = 2 ;
            else {
                mark[x] = 0 ; break ; 
            }
        }
    } 
}

int main(){
    int T;
    scanf("%d",&T);
    int n , q , m ;  
    int Case = 0 ; 
    int x , y ; 
    while( T-- ){
        init();
        scanf("%d%d",&n,&q);
        for( int i = 1 ; i < n ; i++ ){
            scanf("%d%d",&x,&y);
            addEdege(x,y);
            addEdege(y,x);
        }
        build( 1 , -1 );
        int res ;
        printf("Case #%d:\n",++Case);
        while( q -- ) {
            scanf("%d",&m);
            res = n - m ; 

            for( int i = 0 ; i < m ; i++ ) {
                scanf("%d",&a[i]);
                mark[a[i]] = 1 ;
            }
            for( int i = 0 ; i < m ; i++ ){
                if( !vis[a[i]] ){
                    dfs( a[i] );
                }
            }
            for( int i = 0 ; i < m ; i++ ){
                if( !mark[a[i]] ) res ++ ; 
                mark[a[i]] = 0 ; 
                vis[a[i]] = 0 ;
            }
            printf("%d\n",res);
        }
    }
    return 0 ; 
}

K 题目链接:https://cn.vjudge.net/problem/FZU-2298
题意:求s到t最短路,中间会等红绿灯。。。
思路:spfa求最短路,松弛操作的时候加个等红绿灯的时间就行了。多组输入记得清空。。因为这个好多题都wa1.
Code:

#include <iostream>
#include <cstdio>
#include <queue>
#include <vector>
#include <cstring>
#define INF 0x3f3f3f3f
using namespace std;
const int AX = 1e3+6;
int n , m ;
int a[AX];
std::vector<int> G[AX];
std::vector<int> W[AX];
int vis[AX];
long long dis[AX];

int cal( int x , int to ){
    if( !x || !a[to] || x < a[to] ) return 0 ;
    if( x % a[to] == 0 && ( x / a[to] ) % 2 == 0 ) return 0 ;
    if( x % a[to] == 0 && ( x / a[to] ) % 2 == 1 ) return a[to] ;

    if( ( x / a[to] ) % 2 == 0 ){
        return 0 ;
    }else{
        int tmp = a[to];
        while( tmp < x ){
            tmp *= 2 ;
        }
        return tmp - x ;
    }
}

void SPFA( int v0 ){
    for( int i = 1 ; i <= n ; i++ ){
        dis[i] = INF;
    }
    memset( vis , 0 , sizeof(vis) );
    dis[v0] = 0 ;
    queue<int>q;
    q.push(v0);

    vis[v0] = 1 ;
    while( !q.empty() ){
        int u = q.front();
        q.pop();
        vis[u] = 0;
        for( int i = 0 ; i < G[u].size() ; i++ ){
            int time = cal(dis[u],u);
            if( dis[u] + W[u][i] + time < dis[G[u][i]] ){
                dis[G[u][i]] = dis[u] + W[u][i] + time;
                if( !vis[G[u][i]] ){
                    q.push(G[u][i]);
                    vis[G[u][i]] = 1;
                }
            }
        }
    }
}

int main(){
    int T;
    scanf("%d",&T);
    while( T-- ){
        scanf("%d%d",&n,&m);
        for( int i = 1 ; i <= n ; i++ ){
            scanf("%d",&a[i]);
        }
        for(int i = 0 ; i <= n ; i++ ) G[i].clear();
        for(int i = 0 ; i <= n ; i++ ) W[i].clear();
        int x , y , w;
        while( m-- ){
            scanf("%d%d%d",&x,&y,&w);
            G[x].push_back(y);
            G[y].push_back(x);
            W[x].push_back(w);
            W[y].push_back(w);
        }
        int s , t ;
        scanf("%d%d",&s,&t);
        SPFA(s);
        printf("%d\n",dis[t]);

    }
    return 0 ; 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值