《省赛选拔题解》

B:贪心考虑,从小的开始砍即可,排序一下。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int,int> pii;
const int N = 1e5 + 5;
const int M = 1e9 + 1;
const LL Mod = 2147483647;
#define pi acos(-1)
#define INF 1e9
#define dbg(ax) cout << "now this num is " << ax << endl;
namespace FASTIO{
    inline LL read(){
        LL x = 0,f = 1;char c = getchar();
        while(c < '0' || c > '9'){if(c == '-') f = -1;c = getchar();}
        while(c >= '0' && c <= '9'){x = (x<<1)+(x<<3)+(c^48);c = getchar();}
        return x*f;
    }
}
using namespace FASTIO;

int a[N];
int main()
{
    int n,m,t;n = read(),m = read(),t = read();
    for(int i = 1;i <= n;++i) a[i] = read();
    sort(a + 1,a + n + 1);
    for(int i = 1;i <= n;++i) {
        if(m == 0) break;
        if(t >= a[i]) {
            t -= a[i];
            m--;
        }
    }
    printf("%s\n",m == 0 ? "YES" : "NO");
    return 0;
}

F:搜索题。
首先我们考虑没有传送门和钟离。
那就是一个很简单的最小时间的bfs。
即:用小顶堆来维护bfs到的点用的时间,每次都取最小的时间来计算。
现在考虑加入钟离和传送门,那么就是多了一个状态。
我们知道这里的传送门如果走多次那么就会导致复杂度过高。
所以我们只需要去走一次让这个传送门就达到最优。
但是又可能存在没钟离走传送门和有钟离走传送门的情况。
所以要设置两个状态,f[i][j][0] - 表示到(i,j)位置之前没有得到过钟离的最短时间。f[i][j][1] - 表示到(i,j)位置之前没有得到钟离的最短时间。
然后在bfs中对情况进行分类即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int,int> pii;
const int N = 1e3 + 5;
const int M = 1e3 + 5;
const LL Mod = 1e9 + 7;
#define pi acos(-1)
#define INF 1e12
#define dbg(ax) cout << "now this num is " << ax << endl;
namespace FASTIO{
    inline int read(){
        int x = 0,f = 1;char c = getchar();
        while(c < '0' || c > '9'){if(c == '-') f = -1;c = getchar();}
        while(c >= '0' && c <= '9'){x = (x<<1)+(x<<3)+(c^48);c = getchar();}
        return x*f;
    }
}
using namespace FASTIO;

int n;
string a[N];
int dir[4][2] = {1,0,-1,0,0,1,0,-1},f[N][N][2];//1 - 有钟离,0 - 没有
int sx,sy,ex,ey,ans = 0;
vector<pii> vec;
struct Node{
    int x,y,step,zl;
    bool operator < (const Node a) const{
        return step > a.step;
    }
};
void bfs() {
    priority_queue<Node> Q;
    f[sx][sy][0] = 0;
    Q.push(Node{sx,sy,0,0});
    while(!Q.empty()) {
        Node q = Q.top();
        Q.pop();
        if(q.x == ex && q.y == ey) ans = 1;
        for(int i = 0;i < 4;++i) {
            int px = q.x + dir[i][0];
            int py = q.y + dir[i][1];
            int id = q.zl;
            if(px >= 0 && px < n && py >= 0 && py < n && a[px][py] != '1') {
                if(id == 1) {
                    if(q.step + 1 >= f[px][py][id]) continue;
                    f[px][py][id] = q.step + 1;
                    if(a[px][py] == 'X') {
                        for(auto v : vec) {
                            if(f[v.first][v.second][id] <= q.step + 1) continue;
                            f[v.first][v.second][id] = q.step + 1;
                            Q.push(Node{v.first,v.second,q.step + 1,id});
                        }    
                    }
                    else Q.push(Node{px,py,q.step + 1,id});
                }
                else {
                    if(a[px][py] == '3') {
                        f[px][py][id] = q.step + 1;
                        Q.push(Node{px,py,q.step + 1,1});
                    }
                    else {
                        int tim = 1;
                        if(a[px][py] == '2') tim += 3;
                        if(f[px][py][id] <= q.step + tim) continue;
                        f[px][py][id] = q.step + tim;
                        if(a[px][py] == 'X') {
                            for(auto v : vec) {
                            if(f[v.first][v.second][id] <= q.step + tim) continue;
                            f[v.first][v.second][id] = q.step + tim;
                            Q.push(Node{v.first,v.second,q.step + tim,id});
                        }    
                        }
                        else Q.push(Node{px,py,q.step + tim,id});
                    }
                }
            }
        }
    }
}
int main()
{
    n = read();
    for(int i = 0;i < n;++i) cin >> a[i];
    memset(f,0x3f3f3f3f,sizeof(f));
    for(int i = 0;i < n;++i) {
        for(int j = 0;j < n;++j) {
            if(a[i][j] == 'S') sx = i,sy = j;
            else if(a[i][j] == 'E') ex = i,ey = j;
            else if(a[i][j] == 'X') vec.push_back(pii(i,j));
        }
    }
    bfs();
    if(ans == 1) printf("%d\n",min(f[ex][ey][0],f[ex][ey][1]));
    else printf("mabu kule\n");
   // system("pause");
    return 0;
}

C题:这题数据好像有点问题,所以就先删了。
比赛的时候看了下大家的做法,过于暴力,是会TLE的。
这题的正解:建立一棵线段树,每个节点上位置犯罪值的最大位置和最小位置,然后去查询比他大的值里的位置的最大和最小值。
这里就是区间查询,复杂度就是nlogn。

G:树的重心求最远距离

// Author: levil
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int> pii;
const int N = 1e5 + 5;
const int M = 1e3 + 5;
const double eps = 1e-10;
const LL Mod = 998244353;
#define pi acos(-1)
#define INF 1e18
#define dbg(ax) cout << "now this num is " << ax << endl;
inline int read() {
    int f = 1;LL x = 0;char c = getchar();
    while(c < '0' || c > '9') {if(c == '-') f = -1;c = getchar();}
    while(c >= '0' && c <= '9'){x = (x<<1)+(x<<3)+(c^48);c = getchar();}
    return x*f;
}
inline long long ADD(long long x,long long y) {return (x + y) % Mod;}
inline long long DEC(long long x,long long y) {return (x - y + Mod) % Mod;}
inline long long MUL(long long x,long long y) {return x * y % Mod;}

int n,dp[N],dp2[N],a[N];
vector<int> G[N];
void dfs(int u,int fa) {
    dp[u] = dp[fa] + 1;
    for(auto v : G[u]) {
        if(v == fa) continue;
        dfs(v,u);
    }
}
void dfs2(int u,int fa) {
    dp2[u] = dp2[fa] + 1;
    for(auto v : G[u]) {
        if(v == fa) continue;
        dfs2(v,u);
    }
}
void solve() {
    n = read();
    for(int i = 1;i < n;++i) {
        int x,y;x = read(),y = read();
        G[x].push_back(y);
        G[y].push_back(x);
    }
    for(int i = 1;i <= n;++i) a[i] = read();
    dfs(1,0);
    int mx = -1,pos1 = 0,pos2 = 0;
    for(int i = 1;i <= n;++i) {
        if(dp[i] > mx) mx = dp[i],pos1 = i;
    }
    memset(dp,0,sizeof(dp));
    dp[0] = -1;
    dfs(pos1,0);
    mx = -1;
    for(int i = 1;i <= n;++i) {
        if(dp[i] > mx) mx = dp[i],pos2 = i;
    }
    dp2[0] = -1;
    dfs2(pos2,0);
    LL ans = 0;
    for(int i = 1;i <= n;++i) ans = max(ans,1LL * max(dp[i],dp2[i]) * a[i]);
    printf("%lld\n",ans);
}
int main() {    
    //freopen("data3.in","r",stdin);
    //freopen("data3.out","w",stdout);
    solve();
    //system("pause");
    return 0;
}

D:这题大家都对逆元的理解不是很够。
对概率对1e9 + 7取模其实就是一个逆元。
解法:我们需要进行拓扑排序,处理出精灵1进化到每个精灵的概率。
这里可能有一些精灵不能由1进化到,但是会进化到精灵1能进化到的某些精灵。如果把这些精灵都处理进去,那就是导致概率不正确。
所以我们需要把精灵1能进化到精灵都先dfs一次标记出来。
然后再重新构建一副拓扑图,再进行拓扑排序即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int,int> pii;
const int N = 1e5 + 5;
const int M = 1e3 + 5;
const LL Mod = 1e9 + 7;
#define pi acos(-1)
#define INF 1e12
#define dbg(ax) cout << "now this num is " << ax << endl;
namespace FASTIO{
    inline int read(){
        int x = 0,f = 1;char c = getchar();
        while(c < '0' || c > '9'){if(c == '-') f = -1;c = getchar();}
        while(c >= '0' && c <= '9'){x = (x<<1)+(x<<3)+(c^48);c = getchar();}
        return x*f;
    }
}
using namespace FASTIO;

int n,m,in[N];
vector<int> G[N],RG[N];
map<pii,int> mp;
bool vis[N];
LL ans[N];
LL quick_mi(LL a,LL b) {
    LL re = 1;
    while(b) {
        if(b & 1) re = re * a % Mod;
        a = a * a % Mod;
        b >>= 1;
    }
    return re;
}
void dfs(int x) {
    vis[x] = 1;
    if(x == n) return ;
    for(auto v : G[x]) {
        if(vis[v]) continue;
        dfs(v);
    }
}
int main()
{
    n = read(),m = read();
    while(m--) {
        int x,y;x = read(),y = read();
        if(mp[pii(x,y)]) continue;
        G[x].push_back(y);
        RG[y].push_back(x);
        mp[pii(x,y)] = 1;
    }
    dfs(1);
    for(int i = 1;i <= n;++i) {
        for(auto v : RG[i]) {
            if(!vis[v]) continue;
            in[i]++;
        }
    }
    queue<int> Q;
    Q.push(1);
    ans[1] = 1;
    while(!Q.empty()) {
        int u = Q.front();
        Q.pop();
        for(auto v : G[u]) {
            in[v]--;
            ans[v] = (ans[v] + ans[u] * quick_mi(G[u].size(),Mod - 2) % Mod) % Mod;
            if(in[v] == 0) Q.push(v);
        }
    }
    printf("%lld\n",ans[n]);
    return 0;
}

H:
设事件A为x1!=x2 ,事件B 为x2!=x3 ,事件C 为 x3!=x4 事件D 为 x4!=x1
问题要求:|A∩B∩C∩D|

可以转换成|U|-|A’∪B’∪C’∪D’|
即求 |A’∪B’∪C’∪D’| 就行,容斥定理。

#include <bits/stdc++.h>

using namespace std;

const int mod=1e9+7;
typedef  long long ll;
ll Query(ll l1,ll r1,ll l2,ll r2)
{
    ll l=max(l1,l2),r=min(r1,r2);
    return r-l+1>0?r-l+1:0;
}
ll Query2(ll l1,ll r1,ll l2,ll r2,ll l3,ll r3)
{
    ll l=max(l1,max(l2,l3)),r=min(r1,min(r2,r3));
    return r-l+1>0?r-l+1:0;
}
ll Query3(ll l1,ll r1,ll l2,ll r2,ll l3,ll r3,ll l4,ll r4)
{
    ll l=max(max(l1,l2),max(l3,l4)),r=min(min(r1,r2),min(r3,r4));
    return r-l+1>0?r-l+1:0;
}
int main()
{
    int t;
    ll l1,r1,l2,r2,l3,r3,l4,r4;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%lld%lld%lld%lld%lld%lld%lld%lld",&l1,&r1,&l2,&r2,&l3,&r3,&l4,&r4);
        ll sum=1;
        sum=sum*(r1-l1+1)%mod;
        sum=sum*(r2-l2+1)%mod;
        sum=sum*(r3-l3+1)%mod;
        sum=sum*(r4-l4+1)%mod;
        ll ant=0;
        ant=(ant+Query(l1,r1,l2,r2)%mod*(r3-l3+1)%mod*(r4-l4+1)%mod)%mod;
        ant=(ant+Query(l2,r2,l3,r3)%mod*(r1-l1+1)%mod*(r4-l4+1)%mod)%mod;
        ant=(ant+Query(l3,r3,l4,r4)%mod*(r1-l1+1)%mod*(r2-l2+1)%mod)%mod;
        ant=(ant+Query(l4,r4,l1,r1)%mod*(r3-l3+1)%mod*(r2-l2+1)%mod)%mod;

        ant=(ant-Query2(l1,r1,l2,r2,l3,r3)*(r4-l4+1)%mod+mod)%mod;
        ant=(ant-Query(l1,r1,l2,r2)*Query(l3,r3,l4,r4)%mod+mod)%mod;
        ant=(ant-Query2(l1,r1,l2,r2,l4,r4)*(r3-l3+1)%mod+mod)%mod;
        ant=(ant-Query2(l2,r2,l3,r3,l4,r4)*(r1-l1+1)%mod+mod)%mod;
        ant=(ant-Query(l2,r2,l3,r3)*Query(l1,r1,l4,r4)%mod+mod)%mod;
        ant=(ant-Query2(l1,r1,l3,r3,l4,r4)*(r2-l2+1)%mod+mod)%mod;

        ant=(ant+3*Query3(l1,r1,l2,r2,l3,r3,l4,r4)%mod+mod)%mod;
        printf("%lld\n",(sum-ant+mod)%mod);
    }
    return 0;
}

E:这题其实也不是特别难。
首先,我们去二分箱子的数量,这样去保证箱子尽可能少。
同时为了让最大的箱子尽可能小,我们再去二分最大的箱子的容量。
然后去判断是否能装下的时候,我们先排序,然后把大的装进去。
尽量把小的装到大的里面,扩大可以利用的空间,然后这样去判断能不能装下。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int,int> pii;
const int N = 1e5 + 5;
const int M = 1e3 + 5;
const LL Mod = 1e9 + 7;
#define pi acos(-1)
#define INF 1e12
#define dbg(ax) cout << "now this num is " << ax << endl;
namespace FASTIO{
    inline int read(){
        int x = 0,f = 1;char c = getchar();
        while(c < '0' || c > '9'){if(c == '-') f = -1;c = getchar();}
        while(c >= '0' && c <= '9'){x = (x<<1)+(x<<3)+(c^48);c = getchar();}
        return x*f;
    }
}
using namespace FASTIO;

int a[N],ans,n,k,num;
bool solve(int m,int x) {
    priority_queue<int,vector<int>,less<int> > Q;
    for(int i = n;i >= n - m + 1;--i) {
        Q.push(a[i]);
    }
    int num = 0;
    int L = 1,r = n - m;
    while(!Q.empty()) {
        if(L > r) break;
        int u = Q.top();
        Q.pop();
        if(a[L] + u > x) continue;
        Q.push(a[L++] + u);
    }
    return L > r;
}
bool check(int x) {
    int L = 1,r = k,ma = 0;
    while(L <= r) {
        int mid = (L + r) >> 1;
        if(solve(x,mid)) ma = mid,r = mid - 1;
        else L = mid + 1;
    }
    if(ma != 0) ans = ma;
    return ma != 0;
}
int main()
{
    n = read(),k = read();
    for(int i = 1;i <= n;++i) a[i] = read();
    sort(a + 1,a + n + 1);
    int L = 1,r = n;
    while(L <= r) {
        int mid = (L + r) >> 1;
        if(check(mid)) num = mid,r = mid - 1;
        else L = mid + 1;
    }
    printf("%d %d\n",num,ans);
    system("pause");
    return 0;
}

A:本场防AK题,出题的时候怕被AK了,就把这个题的难度上升了。
设dp[i][j][k] - 表示到树i,j,k这个状态下能砍到的最多的树。
j = 0 表示不砍倒,j = 1 表示向左砍倒,j = 2 表示向右砍到。
k = 0 表示从左边开始的dp,k = 1表示从右边开始的dp。
如果这题不能使用魔法,那么我们只需要从左边开始dp然后取最大值即为答案。
dp的时候只需要去判断会不会重叠的问题即可。
但是这题多了一个可以删去树的操作。
那么我们需要从右边开始再dp一次。
然后我们枚举删去哪棵树,然后去枚举它两边的树的砍的状态,取里面的最大值即可。
dp的转移:(从左边开始,右边同理):这里为了方便省去k那一维,因为是一样的dp思路。
dp[i][0] = max(dp[i - 1][0],dp[i - 1][1],dp[i - 1][2]) // 不砍的话就从前面的最大的状态转移。
如果可以向左砍:那么去判断i - 1的各个砍法会不会和i向左砍重叠,不会就可以从i - 1的那个砍法转移得到。
向右砍同理。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int,int> pii;
const int N = 1e5 + 5;
const int M = 1e3 + 5;
const LL Mod = 1000000;
#define pi acos(-1)
#define INF 1e12
#define dbg(ax) cout << "now this num is " << ax << endl;
namespace FASTIO{
    inline LL read(){
        LL x = 0,f = 1;char c = getchar();
        while(c < '0' || c > '9'){if(c == '-') f = -1;c = getchar();}
        while(c >= '0' && c <= '9'){x = (x<<1)+(x<<3)+(c^48);c = getchar();}
        return x*f;
    }
}
using namespace FASTIO;

struct TA{
    int x,h;
    bool operator < (const TA a) const {
        return x < a.x;
    }
}p[N];
int dp[N][3][2],n;// - 0 - 不倒,1 - 向左倒,2 - 向右倒,0 --- 从左开始砍,1 --- 从右开始砍
int main()
{
    n = read();
    for(int i = 1;i <= n;++i) p[i].x = read(),p[i].h = read();
    if(n == 1) printf("1\n");
    else if(n == 2) printf("2\n");
    else {
        sort(p + 1,p + n + 1);
        int ans = 0;
        dp[1][0][0] = 0,dp[1][1][0] = 1;
        if(p[2].x > p[1].x + p[1].h) dp[1][2][0] = 1;
        else dp[1][2][0] = 0;
        for(int i = 2;i <= n;++i) {
            dp[i][0][0] = max(dp[i - 1][0][0],max(dp[i - 1][2][0],dp[i - 1][1][0]));
            if(p[i - 1].x < p[i].x - p[i].h) {
                dp[i][1][0] = max(dp[i - 1][0][0],dp[i - 1][1][0]) + 1;
                if(p[i - 1].x + p[i - 1].h < p[i].x - p[i].h) dp[i][1][0] = max(dp[i][1][0],dp[i - 1][2][0] + 1);
            }
            else dp[i][1][0] = max(dp[i - 1][0][0],max(dp[i - 1][2][0],dp[i - 1][1][0]));
            if(p[i + 1].x > p[i].x + p[i].h || i == n) {
            dp[i][2][0] = max(dp[i - 1][0][0],max(dp[i - 1][2][0],dp[i - 1][1][0])) + 1;
            }
            else dp[i][2][0] = max(dp[i - 1][0][0],max(dp[i - 1][2][0],dp[i - 1][1][0]));
            ans = max(ans,max(dp[i][0][0],max(dp[i][1][0],dp[i][2][0])));
        }
        dp[n][0][1] = 0,dp[n][2][1] = 1;
        if(p[n - 1].x < p[n].x - p[n].h) dp[n][1][1] = 1;
        else dp[n][1][1] = 0;
        for(int i = n - 1;i >= 1;--i) {
            dp[i][0][1] = max(dp[i + 1][0][1],max(dp[i + 1][1][1],dp[i + 1][2][1]));
            if(p[i + 1].x > p[i].x + p[i].h) {
                dp[i][2][1] = max(dp[i + 1][0][1],dp[i + 1][2][1]) + 1;
                if(p[i + 1].x - p[i + 1].h > p[i].x + p[i].h) dp[i][2][1] = max(dp[i][2][1],dp[i + 1][1][1] + 1);
            } 
            else dp[i][2][1] = max(dp[i + 1][0][1],max(dp[i + 1][1][1],dp[i + 1][2][1]));
            if(p[i - 1].x < p[i].x - p[i].h || i == 1) {
                dp[i][1][1] = max(dp[i + 1][0][1],max(dp[i + 1][1][1],dp[i + 1][2][1])) + 1;
            }
            else dp[i][1][1] = max(dp[i + 1][0][1],max(dp[i + 1][1][1],dp[i + 1][2][1]));
        }
        for(int i = 2;i < n;++i) {
            int ma1 = p[i - 1].x + p[i - 1].h;
            int ma2 = p[i + 1].x - p[i + 1].h;
            //0 0
            ans = max(ans,dp[i - 1][0][0] + dp[i + 1][0][1]);
            //0 1
            int ma = (ma2 <= p[i - 1].x) ? 0 : (dp[i + 1][1][1] + (ma2 <= p[i].x));
            ans = max(ans,dp[i - 1][0][0] + ma);
            //0 2
            ans = max(ans,dp[i - 1][0][0] + dp[i + 1][2][1]);
            //1 0
            ans = max(ans,dp[i - 1][1][0] + dp[i + 1][0][1]);
            //1 1
            ma = (ma2 <= p[i - 1].x) ? 0 : (dp[i + 1][1][1] + (ma2 <= p[i].x));
            ans = max(ans,dp[i - 1][1][0] + ma);
            //1 2
            ans = max(ans,dp[i - 1][1][0] + dp[i + 1][2][1]);
            //2 0
            ma = (ma1 >= p[i + 1].x) ? 0 : (dp[i - 1][2][0] + (ma1 >= p[i].x));
            ans = max(ans,ma + dp[i + 1][0][1]);
            //2 1
            ma = (ma2 > ma1) ? (dp[i - 1][2][0] + dp[i + 1][1][1] + (ma2 <= p[i].x) + (ma1 >= p[i].x)): 0;
            ans = max(ans,ma);
            //2 2
            ma = (ma1 >= p[i + 1].x) ? 0 : (dp[i - 1][2][0] + (ma1 >= p[i].x));
            ans = max(ans,ma + dp[i + 1][2][1]);
        }
        printf("%d\n",ans);
    }
    //system("pause");
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值