2022年“5校联盟”蓝桥杯软件大赛训练赛8(2022.01.27)

题目地址:http://182.92.175.181/contest.php?cid=2353

问题 A: 电路维修

题意具体是通过旋转方块找到一条联通左上角和右下角的线路。一个方块有四个顶点,如果线路是’\'可以在左上角到右下角的点建一条权值为0的边,否则在左下角到右上角的点建一条权值为1的边,另一种情况同理。此时可以将问题转化成:带权图中求一条左上角到右下角的最短路径,朴素的方法如可能会超时,需要一些优化。可以使用双端队列,将不需要旋转就能达到的点从队头加入,反之加入队尾可以加速搜索的过程。也可以使用双向BFS来求解,下面给出deque实现的代码

#include 
using namespace std;
#define reset(x) memset(x, 0, sizeof(x))
#define Q_in_out                 \
    ios::sync_with_stdio(false); \
    cin.tie(0);                  \
    cout.tie(0);
typedef long long int ll;
typedef long double ld;
typedef pair<int, int> P;
#define sc(x) scanf("%d", &x);
#define scl(x) scanf("%lld", &x);
#define prt(x) printf("%d", x);
#define prtl(x) printf("%lld", x);
#define forl(l, r) for (int i = l; i < r; i++)
#define forr(r, l) for (int i = r; i >= l; i--)
const int N = 1e5 + 5;
// const int modp = 998244353;
const ll modp = 1e9 + 7;
// const ll modp = 1e9+7;
const int inf = 0x3f3f3f3f;
const double eps = 1e-6;
const double pi = acos(-1.0);
const double e = 2.718281828459045;
int dist[510][510];
char mp[510][510];
char tb[]={'/','\\','/','\\'};
int bx[4]={-1,0,0,-1},by[4]={0,0,-1,-1};
int dir[4][2]={
    {-1,1},
    {1,1},
    {1,-1},
    {-1,-1}
};
int r,c;
bool valid(int x,int y){ 
    return x >= 0 && x <= r && y >= 0 && y <= c;
}
int solve()
{
    scanf("%d%d",&r,&c);
    for (int i=0;i<r;i++) scanf("%s",mp[i]);
    if ((r+c)&1)    //行列和为奇数无解
    {
        printf("NO SOLUTION");
        return 0;
    }
    memset(dist,-1,sizeof dist);
    deque<P>q;
    dist[0][0]=0;
    q.push_back({0,0}); //初始点
    while (q.size())
    {
        P tp=q.front();q.pop_front();
        if(tp.first==r&&tp.second==c) break;
        for(int i=0;i<4;i++){
            int tx=tp.first+dir[i][0];
            int ty=tp.second+dir[i][1];
            if(valid(tx,ty)&&(dist[tx][ty]<0||dist[tp.first][tp.second]+1<=dist[tx][ty])){
                //判断加入队尾还是队头
                int jx=bx[i]+tp.first;
                int jy=by[i]+tp.second;
                if(mp[jx][jy]==tb[i]){      //优先遍历不需要旋转的
                    dist[tx][ty]=dist[tp.first][tp.second];
                    q.push_front({tx,ty});
                }
                else{
                    dist[tx][ty]=dist[tp.first][tp.second]+1;
                    q.push_back({tx,ty});
                }
                   
            }
        }
    }
    printf("%d",dist[r][c]);
    return 0;
}
int main()
{
    // init();
    // Q_in_out 
    int t;
    t = 1;
    cin>>t;
    while (t--)
    {
        solve();
        // cout << endl;
        puts("");
    }
    return 0;
}
问题 B: 魔板

仍然是BFS问题,从初始状态不断进行三个操作后得到目标状态,其中判重可以使用STL哈希表Map,也可以利用康托展开判重(可以提高效率)

#include 
using namespace std;
#define reset(x) memset(x, 0, sizeof(x))
#define Q_in_out                 \
    ios::sync_with_stdio(false); \
    cin.tie(0);                  \
    cout.tie(0);
typedef long long int ll;
typedef long double ld;
typedef pair<int, int> P;
#define sc(x) scanf("%d", &x);
#define scl(x) scanf("%lld", &x);
#define prt(x) printf("%d", x);
#define prtl(x) printf("%lld", x);
#define forl(l, r) for (int i = l; i < r; i++)
#define forr(r, l) for (int i = r; i >= l; i--)
const int N = 1e5 + 5;
// const int modp = 998244353;
const ll modp = 1e9 + 7;
// const ll modp = 1e9+7;
const int inf = 0x3f3f3f3f;
const double eps = 1e-6;
const double pi = acos(-1.0);
const double e = 2.718281828459045;
struct nd
{
    string sta;
    string step;
};
string A(string s){
    return s.substr(4)+s.substr(0,4);
}
string B(string s){
    string s1=s.substr(0,4);
    char c=s1.back();s1.pop_back();
    s1.insert(s1.begin(),c);
    string s2=s.substr(4);
    c=s2.back();s2.pop_back();
    s2.insert(s2.begin(),c);
    return s1+s2;
}
string C(string s){
    char c=s[1];
    s[1]=s[5];s[5]=s[6];
    s[6]=s[2];s[2]=c;
    return s;
}
int main() {
    string src="12348765",des="12345678";
    char x;
    int l=7;
    for(int i=0;i<8;i++){
        cin>>x;
        if(i<4) des[i]=x;
        else des[l--]=x;
    }
    unordered_map<string,bool>vis;
    vis[src]=1;
    queue<nd>q;
    nd tp;
    tp.sta=src;tp.step="";
    q.push(tp);
    while (q.size())
    {
        tp=q.front();
        q.pop();
        if(tp.sta==des){
            printf("%d\n%s",tp.step.size(),tp.step.c_str());
            break;
        }
        string a,b,c;
        a=A(tp.sta);
        b=B(tp.sta);
        c=C(tp.sta);
        nd nt;
        if(!vis.count(a)){
            vis[a]=1;
            nt.sta=a;nt.step=tp.step;nt.step.push_back('A');
            q.push(nt);
        }
        if(!vis.count(b)){
            vis[b]=1;
            nt.sta=b;nt.step=tp.step;nt.step.push_back('B');
            q.push(nt);
        }
        if(!vis.count(c)){
            vis[c]=1;
            nt.sta=c;nt.step=tp.step;nt.step.push_back('C');
            q.push(nt);
        }
    }
    return 0;
}
问题 C: Knight Moves

比较常见的BFS题,vis标记数组采用x*305+y的简单哈希处理

#include 
using namespace std;
#define reset(x) memset(x, 0, sizeof(x))
#define Q_in_out                 \
    ios::sync_with_stdio(false); \
    cin.tie(0);                  \
    cout.tie(0);
typedef long long int ll;
typedef long double ld;
typedef pair<int, int> P;
#define sc(x) scanf("%d", &x);
#define scl(x) scanf("%lld", &x);
#define prt(x) printf("%d", x);
#define prtl(x) printf("%lld", x);
#define forl(l, r) for (int i = l; i < r; i++)
#define forr(r, l) for (int i = r; i >= l; i--)
const int N = 1e5 + 5;
// const int modp = 998244353;
const ll modp = 1e9 + 7;
// const ll modp = 1e9+7;
const int inf = 0x3f3f3f3f;
const double eps = 1e-6;
const double pi = acos(-1.0);
const double e = 2.718281828459045;
struct nd
{
    int x,y,step;
};
int dir[8][2]={
    {-1,-2},{1,-2},{-2,-1},{2,-1},
    {-1,2},{1,2},{-2,1},{2,1}
};
bool vis[3*N];
int solve()
{
    reset(vis);
    int l; 
    cin>>l;
    int sx,sy,dx,dy;
    cin>>sx>>sy>>dx>>dy;
    nd tp;
    tp.x=sx;tp.y=sy;tp.step=0;
    queue<nd>q;
    q.push(tp);
    vis[sx*305+sy]=1;
    while (q.size())
    {
        tp=q.front();
        q.pop();
        if(tp.x==dx&&tp.y==dy){
            cout<<tp.step;
            return 0;
        }
        for(int i=0;i<8;i++){
            int tx,ty;
            tx=tp.x+dir[i][0];
            ty=tp.y+dir[i][1];
            if(tx>=0&&tx<l&&ty>=0&&ty<l&&!vis[tx*305+ty]){
                vis[tx*305+ty]=1;
                nd nt;
                nt.x=tx;nt.y=ty;nt.step=tp.step+1;
                q.push(nt);
            }
        }
    }
    return 0;
}
int main()
{
    // init();
    Q_in_out 
    int t;
    t = 1;
    cin>>t;
    while (t--)
    {
        solve();
        cout << endl;
    }
    return 0;
}
问题 D: 棋盘游戏

棋盘可以看成是16位01串,因此只需一个int值就可以表达出棋盘的状态,并且利于判重以及交换动作

#include 
using namespace std;
#define reset(x) memset(x, 0, sizeof(x))
#define Q_in_out                 \
    ios::sync_with_stdio(false); \
    cin.tie(0);                  \
    cout.tie(0);
typedef long long int ll;
typedef long double ld;
typedef pair<int, int> P;
#define sc(x) scanf("%d", &x);
#define scl(x) scanf("%lld", &x);
#define prt(x) printf("%d", x);
#define prtl(x) printf("%lld", x);
#define forl(l, r) for (int i = l; i < r; i++)
#define forr(r, l) for (int i = r; i >= l; i--)
const int N = 1e5 + 5;
// const int modp = 998244353;
const ll modp = 1e9 + 7;
// const ll modp = 1e9+7;
const int inf = 0x3f3f3f3f;
const double eps = 1e-6;
const double pi = acos(-1.0);
const double e = 2.718281828459045;
bool vis[N];
int main() {
    int src=0,des=0;
    char x;
    for(int i=0;i<16;i++){
        cin>>x;
        if(x=='1') src+=1<<(15-i);
    }
    for(int i=0;i<16;i++){
        cin>>x;
        if(x=='1') des+=1<<(15-i);
    }
    queue<P>q;
    q.push({src,0});
    vis[src]=1;
    while (q.size())
    {
        P tp=q.front();q.pop();
        if(tp.first==des){
            cout<<tp.second;
            break;
        }
        for(int i=15;i>=0;i--){  //枚举每一种棋子交换方式(分别和右、下交换)
            int x=(15-i)/4,y=(15-i)%4;
            if(x<3&&(tp.first&(1<<i))!=(tp.first&(1<<i-4)))   //下边还有数并且下边数和当前位不同
            {
                int nt=tp.first;
                nt^=(1<<i)^(1<<i-4);    //交换等价于取反
                if(!vis[nt]){
                    vis[nt]=1;
                    q.push({nt,tp.second+1});
                }
            }
            if(y<3&&(tp.first&(1<<i))!=(tp.first&(1<<i-1)))   
            {
                int nt=tp.first;
                nt^=(1<<i)^(1<<i-1);   
                if(!vis[nt]){
                    vis[nt]=1;
                    q.push({nt,tp.second+1});
                }
            }
        }
    }
    
    return 0;
}
问题 E: Keyboarding

似乎是2015 WF上的原题,本人菜做不了,给其他人的博客参考一下:
博客1、博客2
代码如下:代码原文

#include 
using namespace std;
char s[55][55];
int n, m;
char t[100005];
int vis[55][55];
int dp[55][55];
int dis[55][55];
struct node {
   int x, y, now, d;
};
struct nd {
   int x, y;
};
nd ycl[9][55][55];
int zfx[4] = { 0, 0, -1, 1 };
int zfy[4] = { 1, -1, 0, 0 };
int fk = 0;
int main() {
   scanf("%d %d", &n, &m);
   for (int i = 1; i <= n; i++) {
       for (int j = 1; j <= m; j++) {
           cin >> s[i][j];
       }
   }
   for (int i = 1; i <= n; i++) {
       for (int j = 1; j <= m; j++) {
           for (int k = 0; k < 4; k++) {
               int nxx = i, nxy = j;
               while (s[nxx + zfx[k]][nxy + zfy[k]] == s[nxx][nxy]) {
                   nxx += zfx[k];
                   nxy += zfy[k];
               }
               nd now;
               now.x = nxx;
               now.y = nxy;
               ycl[k][i][j] = now;
           }
       }
   }
   scanf("%s", t + 1);
   fk = strlen(t + 1);
   t[++fk] = '*';
   int kk = 1;
   while (t[kk] == s[1][1] && kk <= fk) {
       kk++;
   }
   node nog;
   nog.x = 1;
   nog.y = 1;
   nog.now = kk;
   nog.d = kk - 1;
   vis[1][1] = kk;
   queue<node> q;
   q.push(nog);
   while (q.size()) {
       node temp = q.front();
       q.pop();
       //	printf("%d %d %d %d\n",temp.x,temp.y,temp.now,temp.d);
       if (s[temp.x][temp.y] == t[temp.now]) {
           if (temp.now == fk) {
               printf("%d", temp.d + 1);
               break;
           }
           vis[temp.x][temp.y] = temp.now + 1;
           node nog;
           nog.d = temp.d + 1;
           nog.x = temp.x;
           nog.y = temp.y;
           nog.now = temp.now + 1;
           q.push(nog);
           continue;
       }
       for (int i = 0; i < 4; i++) {
           nd xf = ycl[i][temp.x][temp.y];
           int nx = xf.x + zfx[i];
           int ny = xf.y + zfy[i];
           if (nx >= 1 && ny >= 1 && nx <= n && ny <= m) {
               if (vis[nx][ny] >= temp.now) {
                   continue;
               }
               vis[nx][ny] = temp.now;
               node nog;
               nog.d = temp.d + 1;
               nog.x = nx;
               nog.y = ny;
               nog.now = temp.now;
               q.push(nog);
           }
       }
   }
   return 0;
}
问题 F: 移动玩具

做法、代码同D

问题 G: 山峰和山谷

由于需要对某一高度的地形周围进行搜索,故采用BFS方法。对每个地形高度逐一搜索,在八个方向上扩展时,遇到相同高度加入队列,并做好判重标记,否则对高于/低于的地形进行计数。检查计数情况,若不存在比当前地形高的山则山峰数加一,不存在比其低的山则山谷加一,否则不对结果计数。

#include 
using namespace std;
#define reset(x) memset(x, 0, sizeof(x))
#define Q_in_out                 \
    ios::sync_with_stdio(false); \
    cin.tie(0);                  \
    cout.tie(0);
typedef long long int ll;
typedef long double ld;
typedef pair<int, int> P;
#define sc(x) scanf("%d", &x);
#define scl(x) scanf("%lld", &x);
#define prt(x) printf("%d", x);
#define prtl(x) printf("%lld", x);
#define forl(l, r) for (int i = l; i < r; i++)
#define forr(r, l) for (int i = r; i >= l; i--)
const int N = 2e6 + 5;
// const int modp = 998244353;
const ll modp = 1e9 + 7;
// const ll modp = 1e9+7;
const int inf = 0x3f3f3f3f;
const double eps = 1e-6;
const double pi = acos(-1.0);
const double e = 2.718281828459045;
int res1,res2,n;
int mp[1005][1005];
bool vis[1005][1005];
int dir[8][2]={
    {-1,-1},
    {0,-1},
    {1,-1},
    {1,0},
    {1,1},
    {0,1},
    {-1,1},
    {-1,0}
};
bool valid(int x,int y){
    return x>=0&&x<n&&y>=0&&y<n;
}
void bfs(int x,int y){
    queue<P>q;
    q.push({x,y});
    vis[x][y]=1;
    int cnt1=0,cnt2=0;
    while (q.size())
    {
        P tp=q.front();q.pop();
        for(int i=0;i<8;i++){
            int tx=dir[i][0]+tp.first;
            int ty=dir[i][1]+tp.second;
            if(valid(tx,ty)){
                if(mp[tx][ty]==mp[tp.first][tp.second]){
                    if(!vis[tx][ty]){
                        q.push({tx,ty});
                        vis[tx][ty]=1;
                    }
                }
                else if(mp[tx][ty]>mp[tp.first][tp.second]) cnt2++;
                else cnt1++;
            }
        }
    }
    if(cnt1==0) res2++;
    if(cnt2==0) res1++;
}
int main() {
    
    cin>>n;
    for(int i=0;i<n;i++){
        for(int j=0;j<n;j++)
        scanf("%d",&mp[i][j]);
    }
    for(int i=0;i<n;i++){
        for(int j=0;j<n;j++){
            if(!vis[i][j]) bfs(i,j);
        }
    }
    cout<<res1<<' '<<res2;
    return 0;
}
问题 H: 宾馆房间开门问题
#include 
using namespace std;
#define reset(x) memset(x, 0, sizeof(x))
#define Q_in_out                 \
    ios::sync_with_stdio(false); \
    cin.tie(0);                  \
    cout.tie(0);
typedef long long int ll;
typedef long double ld;
typedef pair<int, int> P;
#define sc(x) scanf("%d", &x);
#define scl(x) scanf("%lld", &x);
#define prt(x) printf("%d", x);
#define prtl(x) printf("%lld", x);
#define forl(l, r) for (int i = l; i < r; i++)
#define forr(r, l) for (int i = r; i >= l; i--)
const int N = 1e5 + 5;
// const int modp = 998244353;
const ll modp = 1e9 + 7;
// const ll modp = 1e9+7;
const int inf = 0x3f3f3f3f;
const double eps = 1e-6;
const double pi = acos(-1.0);
const double e = 2.718281828459045;
int solve()
{
    vector<bool>a(101,1);
    for(int i=2;i<=100;i++){
        for(int j=i;j<=100;j+=i){
            a[j]=a[j]^1;
        }
    }
    for(int i=1;i<=100;i++) if(a[i]) cout<<i<<' ';
    return 0;
}
int main()
{
    // init();
    Q_in_out 
    int t;
    t = 1;
    // cin>>t;
    while (t--)
    {
        solve();
        cout << endl;
    }
    return 0;
}
问题 I: 输出最大数所在位置
#include 
using namespace std;
#define reset(x) memset(x, 0, sizeof(x))
#define Q_in_out                 \
    ios::sync_with_stdio(false); \
    cin.tie(0);                  \
    cout.tie(0);
typedef long long int ll;
typedef long double ld;
typedef pair<int, int> P;
#define sc(x) scanf("%d", &x);
#define scl(x) scanf("%lld", &x);
#define prt(x) printf("%d", x);
#define prtl(x) printf("%lld", x);
#define forl(l, r) for (int i = l; i < r; i++)
#define forr(r, l) for (int i = r; i >= l; i--)
const int N = 1e5 + 5;
// const int modp = 998244353;
const ll modp = 1e9 + 7;
// const ll modp = 1e9+7;
const int inf = 0x3f3f3f3f;
const double eps = 1e-6;
const double pi = acos(-1.0);
const double e = 2.718281828459045;
int solve()
{
    int n;
    cin>>n;
    vector<int>a(n);
    int tp=INT_MIN,k;
    for(int i=0;i<n;i++){
        cin>>a[i];
        if(a[i]>tp){
            tp=a[i];
            k=i;
        }
    }
    cout<<k+1;
    return 0;
}
int main()
{
    // init();
    Q_in_out 
    int t;
    t = 1;
    // cin>>t;
    while (t--)
    {
        solve();
        cout << endl;
    }
    return 0;
}
问题 J: 打印杨辉三角形前十行
#include 
using namespace std;
#define reset(x) memset(x, 0, sizeof(x))
#define Q_in_out                 \
    ios::sync_with_stdio(false); \
    cin.tie(0);                  \
    cout.tie(0);
typedef long long int ll;
typedef long double ld;
typedef pair<int, int> P;
#define sc(x) scanf("%d", &x);
#define scl(x) scanf("%lld", &x);
#define prt(x) printf("%d", x);
#define prtl(x) printf("%lld", x);
#define forl(l, r) for (int i = l; i < r; i++)
#define forr(r, l) for (int i = r; i >= l; i--)
const int N = 1e5 + 5;
// const int modp = 998244353;
const ll modp = 1e9 + 7;
// const ll modp = 1e9+7;
const int inf = 0x3f3f3f3f;
const double eps = 1e-6;
const double pi = acos(-1.0);
const double e = 2.718281828459045;
int solve()
{
    int a[10][10];
    a[0][0]=a[1][0]=a[1][1]=1;
    for(int i=2;i<10;i++){
        for(int j=0;j<=i;j++){
            if(j==0||j==i) a[i][j]=1;
            else a[i][j]=a[i-1][j]+a[i-1][j-1];
        }
    }
    for(int i=0;i<10;i++)
    { 
        if(i!=10)
        for(int j=0;j<27-3*i;j++) cout<<" ";
        for(int j=0;j<=i;j++)
            cout<<setw(6)<<a[i][j];
        cout<<endl;
    }
    return 0;
}
int main()
{
    // init();
    Q_in_out 
    int t;
    t = 1;
    // cin>>t;
    while (t--)
    {
        solve();
        cout << endl;
    }
    return 0;
}
问题 K: 数组元素位置平移

问题 L: 十个数从大到小排序

问题 M: 最大的节点

在建图时我们可以反向建图,再从最大的节点n往下遍历(n~1),在对i进行每一次深度遍历时将途中经过的节点所能达到的最大节点值设为i,同时设置访问标志,当后面再次遍历到同一节点时无需更新答案,因为我们是从n往下开始搜索的,因此对于每个节点来说第一次访问到时的起点i即为题目所求
代码:

#include 
using namespace std;
#define reset(x) memset(x, 0, sizeof(x))
#define Q_in_out                 \
    ios::sync_with_stdio(false); \
    cin.tie(0);                  \
    cout.tie(0);
typedef long long int ll;
typedef long double ld;
typedef pair<int, int> P;
#define sc(x) scanf("%d", &x);
#define scl(x) scanf("%lld", &x);
#define prt(x) printf("%d", x);
#define prtl(x) printf("%lld", x);
#define forl(l, r) for (int i = l; i < r; i++)
#define forr(r, l) for (int i = r; i >= l; i--)
const int N = 1e5 + 5;
// const int modp = 998244353;
const ll modp = 1e9 + 7;
// const ll modp = 1e9+7;
const int inf = 0x3f3f3f3f;
const double eps = 1e-6;
const double pi = acos(-1.0);
const double e = 2.718281828459045;
vector<int>mp[N];
bool vis[N];
int res[N];
void dfs(int x,int mx){
    vis[x]=1;
    res[x]=mx;
    for(int t:mp[x]){
        if(!vis[t])
            dfs(t,mx);
    }
}
int main()
{
    int n,m;
    cin>>n>>m;
    int u,v;
    for(int i=0;i<m;i++){
        scanf("%d%d",&u,&v);
        mp[v].push_back(u);
    }
    for(int i=n;i>=1;i--){
        if(!vis[i]) dfs(i,i);
    }
    for(int i=1;i<=n;i++) printf("%d ",res[i]);
    return 0;
}
问题 N:有向图的D和E

规则:对于E图来说,若某节点i与某节点j有公共终点,则i能到达的顶点j也能到达,根据上述规则可利用邻接矩阵存图,为简化操作,可使用STL中bitset

#include 
using namespace std;
#define reset(x) memset(x, 0, sizeof(x))
#define Q_in_out                 \
    ios::sync_with_stdio(false); \
    cin.tie(0);                  \
    cout.tie(0);
typedef long long int ll;
typedef long double ld;
typedef pair<int, int> P;
#define sc(x) scanf("%d", &x);
#define scl(x) scanf("%lld", &x);
#define prt(x) printf("%d", x);
#define prtl(x) printf("%lld", x);
#define forl(l, r) for (int i = l; i < r; i++)
#define forr(r, l) for (int i = r; i >= l; i--)
const int N = 1e5 + 5;
// const int modp = 998244353;
const ll modp = 1e9 + 7;
// const ll modp = 1e9+7;
const int inf = 0x3f3f3f3f;
const double eps = 1e-6;
const double pi = acos(-1.0);
const double e = 2.718281828459045;
int solve(int t)
{
    int m,k;
    cin>>m>>k;
    bitset<305>mp[305];
    int u,v;
    for(int i=0;i<k;i++){
        cin>>u>>v;
        mp[u][v]=1;
    }
    bool f=1;
    for(int i=0;i<m;i++){
        for(int j=i+1;j<m;j++){
            if(mp[i]!=mp[j]&&(mp[i]&mp[j]).count())
            {
                f=0;
                break;
            }
        }
    }	
    cout<<"Case #"<<t<<": ";
    if(f) cout<<"Yes";
    else cout<<"No";
    return 0;
}
int main()
{
    Q_in_out 
    int t;
    t = 1;
    cin>>t;
    int l=1;
    while (t--)
    {
        solve(l++);
        cout << endl;
    }
    return 0;
}
问题 O:奶牛排序

首先,完全图肯定能确定顺序,并且完全图边的数目为:n*(n-1)/2 (n个节点),因此我们可以利用题设已知关系,求出所有的关系(包括直接关系和传递的关系),再用完全图关系数减掉已知关系数即可
代码:

#include 
using namespace std;
#define reset(x) memset(x, 0, sizeof(x))
#define Q_in_out                 \
    ios::sync_with_stdio(false); \
    cin.tie(0);                  \
    cout.tie(0);
typedef long long int ll;
typedef long double ld;
typedef pair<int, int> P;
#define sc(x) scanf("%d", &x);
#define scl(x) scanf("%lld", &x);
#define prt(x) printf("%d", x);
#define prtl(x) printf("%lld", x);
#define forl(l, r) for (int i = l; i < r; i++)
#define forr(r, l) for (int i = r; i >= l; i--)
const int N = 2e6 + 5;
// const int modp = 998244353;
const ll modp = 1e9 + 7;
// const ll modp = 1e9+7;
const int inf = 0x3f3f3f3f;
const double eps = 1e-6;
// const double pi = acos(-1.0);
const double e = 2.718281828459045;
bitset<1005>mp[1005];
int main() {
    int n,m;
    cin>>n>>m;
    int u,v;
    for(int i=0;i<m;i++){
        cin>>u>>v;
        mp[u][v]=1;
    }
    for(int j=1;j<=n;j++){
        for(int i=1;i<=n;i++)
            if(mp[i][j]){       //i->j
                mp[i]|=mp[j];   //i->j以及j->x得出i->x的关系
            }
    }
    int cnt=0;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++)
            if(mp[i][j]) cnt++;
    }
    cout<<n*(n-1)/2-cnt;
    return 0;
}
问题 P: 子树查询

考察搜索、树状数组,人太菜写不动了,贴别人的代码思路:
通过自己来编号所有苹果,每个节点保存两个值,左值为本身,右值为其包含的所有后代中最大的编号
我们可以通过搜索来进行编号,在编好号之后,我们可以知道,对于某一点而言,我们是先通过这个点搜完所有他的后代编号才结束的,所以这个点的右值,包含了当前点所有的后代与祖先,后代必然是所有编号大于本节点的点,那么祖先呢,那必然是编号小于这个节点的点了
所以我们通过sum(rig[x])-sum(lef[x]-1)就能得到查询的答案。至于更新,只需要更新当前点即可,这样就转化为树状数组了
代码:(代码原文)

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
#define lson 2*i
#define rson 2*i+1
#define LS l,mid,lson
#define RS mid+1,r,rson
#define UP(i,x,y) for(i=x;i<=y;i++)
#define DOWN(i,x,y) for(i=x;i>=y;i--)
#define MEM(a,x) memset(a,x,sizeof(a))
#define W(a) while(a)
#define gcd(a,b) __gcd(a,b)
#define LL long long
#define N 100005
#define INF 0x3f3f3f3f
#define EXP 1e-8
#define lowbit(x) (x&-x)
const int mod = 1e9+7;
vector<vector<int> > a(N);
int n,m,lef[N],rig[N],c[N],tot,s[N];
int sum(int x)
{
    int ret = 0;
    while(x>0)
    {
        ret+=c[x];
        x-=lowbit(x);
    }
    return ret;
}
void add(int x,int d)
{
    while(x<=n)
    {
        c[x]+=d;
        x+=lowbit(x);
    }
}
void dfs(int x)
{
    lef[x] = tot;
    for(int i = 0; i<a[x].size(); i++)
    {
        tot++;
        dfs(a[x][i]);
    }
    rig[x] = tot;
}
int main()
{
    int i,j,k,x,y;
    char op[5];
    while(~scanf("%d",&n))
    {
        MEM(lef,0);
        MEM(rig,0);
        MEM(s,0);
        MEM(c,0);
        for(i=0; i<N; i++)
            a[i].clear();
        for(i = 1; i<n; i++)
        {
            scanf("%d%d",&x,&y);
            a[x].push_back(y);
        }
        tot = 1;
        dfs(1);
        for(i = 1; i<=n; i++)
        {
            s[i] = 1;
            add(i,1);
        }
        scanf("%d",&m);
        while(m--)
        {
            scanf("%s%d",op,&x);
            if(op[0]=='Q')
            {
                printf("%d\n",sum(rig[x])-sum(lef[x]-1));
            }
            else
            {
                if(s[x])
                    add(lef[x],-1);
                else
                    add(lef[x],1);
                s[x]=!s[x];
            }
        }
    }
    return 0;
}
问题 Q:矩形区域查询

二维的树状数组可以求解:
代码:(代码来源)

#include 
#include 
using namespace std;
int tr[1500][1500], n;
int lowbit(int x){
	return x&(-x);
}
void updata(int x, int y, int a){
	for(int i=x; i<=n; i+=lowbit(i)){
		for(int j=y; j<=n; j+=lowbit(j)){
			tr[i][j]+=a;
		}
	}
}
int getsum(int x, int y){
	int s=0;
	for(int i=x; i>0; i-=lowbit(i)){
		for(int j=y; j>0; j-=lowbit(j)){
			s+=tr[i][j];
		}
	}
	return s;
}
int main(){
	int op, x, y, a, l, b, r, t;
	memset(tr, 0, sizeof(tr));
	scanf("%d%d", &op, &n);
	while(scanf("%d", &op)){
		if(op==3) break;
		if(op==1){
			scanf("%d%d%d", &x, &y, &a);
			updata(x+1, y+1, a);
		}
		if(op==2){
			scanf("%d%d%d%d", &l, &b, &r, &t);
			printf("%d\n", getsum(r+1, t+1)-getsum(l, t+1)-getsum(r+1, b)+getsum(l, b));
		}
	}
	return 0;
}

你可能感兴趣的:(菜鸡成长之路,蓝桥杯,算法,c++)