前言:

A - 超级闪光牛可乐

思路:

  • 输出一个字符串,它所代表的数之和大于x即可

以下是代码部分, 代码参考来源——牛客288141082号

#include<bits/stdc++.h>
using namespace std;
int n, m;
string s;
int main() {
	cin>>n>>m>>s;
	cout<<string(1000,s[0]);
    return 0;
}

B - 人列计算机

思路:

  • 模拟判断即可

以下是代码部分

#include <bits/stdc++.h>
using namespace std;

#define endl '\n'
using ll = long long;
const int N = 1e5 + 10;

void solve()
{
    string s[5];
    int a, b;
    for(int i = 0; i < 5; i ++)
        getline(cin, s[i]);
    if(isdigit(s[1][0]) && isdigit(s[3][0]))
    {
        a = s[1][0] - '0', b = s[3][0] - '0';
        if(s[2][5] == '&')
            cout << (a&b) << endl;
        else
            cout << (a|b) << endl;
    }
    else
    {
        a = s[2][0] - '0';
        cout << (!a) << endl;
    }
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);cout.tie(nullptr);
    int t = 1;
    //cin >> t;
    while(t --)
        solve();

    return 0;
}

C - 时间管理大师

思路:

  • 先把单位统一,分别存入-1, -3, -5的状态,后统一输出即可

以下是代码部分,代码参考来源——牛客288141082号

#include<bits/stdc++.h>
using namespace std;

int i,j,k,n,m,t;
set<int> s;

int main(){
	cin>>t;
	while(t--){
		cin>>i>>j;
		k=i*60+j;
		s.insert(k-1);
		s.insert(k-3);
		s.insert(k-5);
	}
	cout<<s.size()<<endl;
	for(auto i:s){
		j = i/60; k=i%60;
		cout<<j<<' '<<k<<endl;
	}
}

D - 我不是大富翁

思路:

  • 动态规划题
    • i 代表回合数
    • j 代表地块的序号
    • dp[i][j]代表是否可以被走到
  • 可得状态转移方程
  • (记得取模)
    • 轮的第 地块的状态由第 轮的第 状态转移过来
  • 因为取模的时候会有 , 所以题目设定的, 更改为 ~
    • 此操作对结果无影响

以下是代码部分, 代码参考来源——出题人题解

#include<bits/stdc++.h>
using namespace std;


int main() {
    int n, m;
    cin >> n >> m;

    vector dp(m + 1, vector(n, false));
    dp[0][0] = true;
    for (int i = 1; i <= m; i++) {
        int x;
        cin >> x;
      //取模,防止数组越界段错误
        x %= n;
      //状态转移
        for (int j = 0; j < n; j++)
            dp[i][j] = dp[i - 1][(j + x) % n] | dp[i - 1][(j - x + n) % n];
    }
    cout << (dp[m][0] ? "YES" : "NO") << "\n";
}

E - 多重映射

思路1:

  • 逆向思考, 后面的操作受前面的操作影响
    • 也就是说,后面的操作会覆盖前面的操作,比如:操作为 and
    • 所以可以逆向存储,防止后面的被前面的覆盖掉
  • 存储之后,遍历 输出即可。
  • 注意重新更改 to[i] 的值,防止影响到下一轮的数据。
    • 需要注意的是:
    • 重新更改to[]中的值的时候不可以直接:
for(int i = 0; i < N; i ++) to[i] = i;

此方式会

  • 所以逆转回to[]的值,可以使用参考代码里的方法,只初始化已经被改变的值
    • 此方法可有效减少循环次数

以下是代码部分,代码参考来源——ReservoirDog

  • 写法1:
#include<bits/stdc++.h>
using namespace std;

const int N = 1e6 + 10, M = 1e5 + 10;
//to[]处理映射关系, a[]储存最初的数据, u[], v[]储存操作
int to[N], a[M], u[M], v[M];

void solve()
{
    int n, m;
    cin >> n >> m;
    //输入
    for(int i = 0; i < n; i ++) cin >> a[i];
    //储存操作
    for(int i = 0; i < m; i ++)
        cin >> u[i] >> v[i];
    //逆向存储
    for(int i = m - 1; i >= 0; i --)
        to[u[i]] = to[v[i]];
    //正向对映
    for(int i = 0; i < n; i ++)
        cout << to[a[i]] << " \n"[i == n - 1];
    //处理to[]防止影响到下一组数据
    for(int i = m - 1; i >= 0; i --)
    {
        to[u[i]] = u[i];
        to[v[i]] = v[i];
    }
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);cout.tie(nullptr);
    // 初始化对应关系
    for(int i = 0; i < N; i ++) to[i] = i;

    int t;
    cin >> t;
    while(t --)
        solve();

    return 0;
}

以下是代码部分,代码参考来源——出题人题解

  • 写法2:
#include<bits/stdc++.h>
using namespace std;

void solve()
{
    int n, m;
    cin >> n >> m;
    //创建数组
    vector<int> a(n), u(m), v(m);
    //相当于写法1中的to[]
    map<int, int> mp;
    for(int i = 0; i < n; i ++) cin >> a[i];
    for(int i = 0; i < m; i ++)
        cin >> u[i] >> v[i];
    for(int i = m - 1; i >= 0; i --)
    {
        //如果mp中的元素中有v[i]
        //映射串联起来
        if(mp.count(v[i])) mp[u[i]] = mp[v[i]];
        //否则,直接赋值
        else mp[u[i]] = v[i];
    }
    //如果mp[]中没有对映的映射,则为它本身
    // 否则输出对映的映射
    for(int i = 0; i < n; i ++)
        cout << (mp[a[i]] ? mp[a[i]] : a[i]) << " \n"[i == n - 1];
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);cout.tie(nullptr);
    int t = 1;
    cin >> t;
    while(t --)
        solve();

    return 0;
}

思路2:

  • 启发式合并

以下是代码部分, 代码参考来源——出题人题解

#include<bits/stdc++.h>
using namespace std;

//二维数组,第一维代表数值大小,第二维代表坐标
vector<vector<int>> dic(1E6 + 5);

void solve()
{
    int n, m;
    cin >> n >> m;
    //创建储存输入的数组
    vector<int> in(n), ans;
    for(int i = 0; i < n; i ++)
    {
        cin >> in[i];
        //输入的值存入ans中
        ans.push_back(in[i]);
        //把坐标存入二维数组dic中
        dic[in[i]].push_back(i);
    }

    for(int i = 0; i < m; i++)
    {
        int l, r;
        //输入操作
        cin >> l >> r;
        if(l == r) continue;
        //if,以及else语句用来减少程序运行时间
        //      insert()时间复杂度为O(n),n与后面的长度有关
        if(dic[l].size() < dic[r].size())
        {
            //l的坐标全部接入到r的尾部
            dic[r].insert(dic[r].end(), dic[l].begin(), dic[l].end());
            //删除l中的坐标
            dic[l].clear();
        }
        else
        {
            //r的坐标全部接入到l的尾部
            dic[l].insert(dic[l].end(), dic[r].begin(), dic[r].end());
            //删除r中的坐标
            dic[r].clear();
            //交换
            swap(dic[l], dic[r]);
        }
        ans.push_back(r);
    }
    for(int i : ans)
    {
        for(int j : dic[i])
            in[j] = i;
        dic[i].clear();
    }
    for(int i = 0; i < n; i ++)
        cout << in[i] << " n"[i == n - 1];
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);cout.tie(nullptr);
    int t = 1;
    cin >> t;
    while(t --)
        solve();

    return 0;
}

F - 现在是消消乐时间

建议:

思路:

  1. 容易发现,一个方块在被穿透时,仅仅只有两种穿透方法
  2. 只需要枚举其中一个方块能否被两种方式的其中一种穿透即可

以下是代码部分,代码参考来源——出题人题解

  • 1.过程优化的暴力搜索
#include<bits/stdc++.h>
using namespace std;

const int N = 2e3 + 10;
bool vis[N][N][4];
//储存方向
vector<int> mx = {1, 1, -1, -1};
vector<int> my = {1, -1, 1, -1};
//储存输出结果
vector<string> str = {"UR", "UL", "DR", "DL"};
int n, m, d;

bool clac(int x, int y, int k)
{
    int sum = 0;
    //当这个状态被判断过时,直接退出,返回false
    while(!vis[x][y][k])
    {
        //光线走过
        vis[x][y][k] = true;
        //沿着方向模拟前进
        int dx = x + mx[k], dy = y + my[k];
        //判断时候到达矩形的顶角
        if ((dx < 0) + (n < dx) + (dy < 0) + (m < dy) == 2) break;
        //向下变向上
        if (dx < 0) k -= 2;
        //向上变向下
        else if (n < dx) k += 2;
        //向左变向右
        else if (dy < 0) k -= 1;
        //向右变向左
        else if (m < dy) k += 1;
        //只有当行序号大于d的时候,才开始真正消除方块
        sum += max(x, x + mx[k]) > d;
        //模拟光线的移动
        x += mx[k], y += my[k];
    }
    //如果相等,则代表全都遍历到了。
    return sum == (n - d) * m;
}

void solve()
{
    cin >> n >> m >> d;
    //i代表初始选择的行位置
    for(int i = 0; i <= n; i ++)
        //j代表初始选择的列位置
        for(int j = 0; j <= m; j ++)
            //k代表方向
            for(int k = 0; k <= 3; k ++)
            {
                //当返回false, 则跳过。
                if(!clac(i, j, k)) continue;
                cout << "YES\n";
                cout << i << ' ' << j << '\n';
                cout << str[k] << '\n';
                return ;
            }
    cout << "NO\n";
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);cout.tie(nullptr);
    int t = 1;
    //cin >> t;
    while(t --)
        solve();

    return 0;
}

以下是代码部分,代码参考来源——出题人题解

  • 起点优化的暴力搜索
#include<bits/stdc++.h>
using namespace std;

int n, m, d;
//判断是否合法
bool clac(int sx, int sy)
{
    vector vis(n + 1, vector(m + 1, false));
    int x = sx, y = sy;
    int dx = 1, dy = 1, cnt = 0;
    while(cnt++ <= n * m)
    {
        vis[x][y] = true;
        int bounce = 0;
        //没有越界
        if(x + dx < 0 || n < x + dx)
        {
            dx *= -1;
            bounce ++;
        }
        //没有越界
        if(y + dy < 0 || m < y + dy)
        {
            dy *= -1;
            bounce ++;
        }
        //代表到达了矩形的角落处
        if(bounce == 2) break;
        x += dx, y += dy;
    }
    bool flag = true;
    for (int i = d; i < n; i++)
        for (int j = 0; j < m; j++)
            //判断是否都遍历过了, 并且排除了越界的情况
            if (vis[i][j] + vis[i + 1][j] + vis[i][j + 1] + vis[i + 1][j + 1] < 2)
                flag = false;
    return flag;
}

void solve()
{
    cin >> n >> m >> d;
    //从左下角发射
    if (clac(0, 0)) cout << "YES\n0 0\nUR\n";
    //从右下角发射
    else if (clac(0, 1)) cout << "YES\n0 1\nUL\n";

    else cout << "NO\n";
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);cout.tie(nullptr);
    int t = 1;
    //cin >> t;
    while(t --)
        solve();

    return 0;
}

G - 三点不共线

思路1:

图: alt

以下是代码部分,代码参考来源——出题人题解

  • 平面几何 + 三角函数
#include<bits/stdc++.h>
using namespace std;

const double PI = acos(-1);
//角度转弧度
double toArc(double x)
{
    return PI * x / 180;
}
//两点之间距离公式
double dis(double x1, double y1, double x2, double y2)
{
    double val = (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2);
    return sqrt(val);
}

void solve()
{
    double x1, y1, x2, y2, alpha;
    //保留小数点后10位
    cout << fixed << setprecision(10);
    //输入
    cin >> x1 >> y1 >> x2 >> y2 >> alpha;
    //求 α/2 并转化为弧度
    alpha = toArc(alpha / 2);
    //判断H和G的位置关系
    double k = 0;
    if(x1 != x2)
        k = (y1 - y2) / (x1 - x2);
    //求AB线段的中垂线的垂点
    double xM = (x1 + x2) / 2;
    double yM = (y1 + y2) / 2;

    double angle = atan(abs(yM - y1) / abs(xM - x1));
    double line = dis(x1, y1, xM, yM);
    //此时line 代表MC线段
    line /= tan(alpha);
    if(k < 0)
        cout << xM + line * sin(angle) << ' ' << yM + line * cos(angle) << '\n';
    else
        cout << xM - line * sin(angle) << ' ' << yM + line * cos(angle) << "\n";
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);cout.tie(nullptr);
    int t = 1;
    cin >> t;
    while(t --)
        solve();

    return 0;
}

思路2:

  • 向量做法

以下是代码部分