The 2025 Hunan University Programming Contest

The 2025 Hunan University Programming Contest

  • 一、c 题
  • 二、j题
  • 三、F题
  • 四、H题
  • 五、D题

地址:

一、c 题

签到题,思路就是找到两个不递增的位置,交换两个位置的值,然后再遍历一边看看满不满足整体递增。

#include 
using namespace std;
int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n;
    cin >> n;
    int a[1005] = { 0 };
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }
    int i, vi = -1;
    for (i = 1; i < n; i++) {
        if (a[i] > a[i + 1]) {
            vi = i;
            break;
        }
    }
    if (vi == -1) {
        cout << "Sorted" << '\n';
        return 0;
    }
    int minh = INT_MAX, v2 = ++i;
    for (i; i <= n; i++) {
        if (minh > a[i]) {
            minh = a[i];
            v2 = i;
        }
    }
    swap(a[vi], a[v2]);
    for (i = 1; i < n; i++) {
        if (a[i] > a[i + 1]) {
            cout << "Failed" << '\n';
            return 0;
        }
    }
    cout << "Sorted" << '\n';
    return 0;
}

二、j题

数学题。计算每个点的次数期望可以从之前的位置的点推迟来。
公式: E [ n ] = 1 + 1 n ∑ i = 0 n − 1 E [ i ] E[n] = 1 + \frac{1} {n} \sum_{i=0}^ {n-1} E[i] E[n]=1+n1i=0n1E[i]

#include
using namespace std;
const int M = 1e5 + 10;
// const int mod =
double E[M],preE[M];
inline void solve() {
    int N = 1e5 + 1;
 
    E[0]=0,E[1]=1.0;
    // if (N == 1) {
    //     printf("%.15lf\n",E[1]);
    //     return;
    // }
    for (int n=2;n<=N;n++) {
        preE[n-1]=E[n-1];
        preE[n-1] += preE[n-2];
        E[n]=1 + 1.0 / n * preE[n-1];
    }
 
}
int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int T;
    cin >> T;
    solve();
    while (T--) {
        int n;
        cin >> n;
        printf("%.15lf\n",E[n]);
    }
    return 0;
}

三、F题

挺好想的,首先如果第一的数是偶数,那么那么门与门之间就会死循环,不会跳到下一个,之后如果有第一个数是奇数那么之后到第n-1个房间一定是偶数,这样才能配合一个第一个留下来的门去变成奇数个门。
结论:第一个是奇数,且2到n-1是偶数才为yes,否则是false;

#include 
using namespace std;
int a[100005];
int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int T;
    cin >> T;
    while (T--) {
        int n;
        cin >> n;
        for (int i = 0; i < n; i++) {
            cin >> a[i];
        }
        if (n == 1) {
            cout << "Yes\n";
        } else if (n == 2) {
            if (a[0] % 2 == 0) {
                cout << "No\n";
            } else {
                cout << "Yes\n";
            }
        } else {
            bool odd = false;
            for (int i = 1; i < n-1; i++) {
                if (a[i] % 2 == 1) {
                    odd = true;
                    break;
                }
            }
            if (odd) {
                cout << "No\n";
            } else {
                if (a[0] % 2 == 0 ) {
                    cout << "No\n";
                } else {
                    cout << "Yes\n";
                }
            }
        }
    }
    return 0;
}

四、H题

模拟,没什么好说的。

#include 
const double pai = 3.141592654;
using namespace std;
const int MAXN = 100005;
int a[MAXN];
signed main() {
    ios_base::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    int T=1;
    cin>>T;
    
    while(T--) {
        string a;
        cin>>a;
        a='&'+a;
        if (a[2]=='-') {
            int tep = abs(a[3]-a[1]);
            double ans = 50.0*sin(tep * 1.0 / 8.0 * pai);
            
            printf("%.10lf\n",2 * ans);
        }else if (a[2]=='>') {
            int tep=0;
            if (a[1]<a[3]) {
                tep = a[3]-a[1];
            }else {
                tep = a[3] + 8 - a[1];
            }
            double ans = 2 * pai * 50 * (tep * 1.0 / 8);
            printf("%.10lf\n",ans);
        }else if (a[2]=='<') {
            int tep=0;
            if (a[1]<a[3]) {
                tep = 8 + a[1] - a[3];
            }else {
                tep = a[1] - a[3];
            }
            double ans = 2 * pai * 50 * (tep * 1.0 / 8);
            printf("%.10lf\n",ans);
        }else if (a[2]=='v') {
            double ans = 2 * 50.0;
            printf("%.10lf\n",ans);
        }

    }
    return 0;
}

五、D题

滑动窗口+单调队列
这道题的根本在于知道一个窗口怎么判断是不是好数组呢,我们可以从这个窗口中找到最小前缀和的值,然后和这个窗口的前一个值(这里者的是前面所有值的前缀和)比较,如果最小的值都大于前面的值,那么把前面的值去掉之后窗口中仍然前缀和是正的即合法。
为了避免循环取余问题,我们用2倍原数组来模拟循环移位。
以示例举例

Q[0] = 0
Q[1] = 2       // 0+2
Q[2] = 5       // 0+2+3
Q[3] = 1       // 0+2+3-4
Q[4] = 2       // 0+2+3-4+1
Q[5] = 1       // 0+2+3-4+1-1
Q[6] = 3       // 0+2+3-4+1-1+2
Q[7] = 6       // 0+...+2+3
Q[8] = 2       // 0+...+2+3-4
Q[9] = 3       // 0+...+2+3-4+1
Q[10]= 2       // 0+...+2+3-4+1-1

滑动窗口最小值计算
窗口大小 n=5,计算每个窗口的最小值:
窗口 [1,5](下标1~5):min(2,5,1,2,1)=1
窗口 [2,6](下标2~6):min(5,1,2,1,3)=1
窗口 [3,7](下标3~7):min(1,2,1,3,6)=1
窗口 [4,8](下标4~8):min(2,1,3,6,2)=1
窗口 [5,9](下标5~9):min(1,3,6,2,3)=1
存储结果:minInWindow = [?, 1, 1, 1, 1, 1](下标1~5)
检查移位条件
遍历每个移位 k(0~4),检查 Q[k] <= minInWindow[k+1]:
k=0:Q[0]=0 <= minInWindow[1]=1 → 满足
移位后数组:[2,3,-4,1,-1]
前缀和:[2,5,1,2,1](全部≥0)
k=1:Q[1]=2 > minInWindow[2]=1 → 不满足
移位后数组:[3,-4,1,-1,2]
前缀和:[3,-1,…](-1<0,无效)
k=2:Q[2]=5 > minInWindow[3]=1 → 不满足
移位后数组:[-4,1,-1,2,3]
前缀和:[-4,…](-4<0,无效)
k=3:Q[3]=1 <= minInWindow[4]=1 → 满足
移位后数组:[1,-1,2,3,-4]
前缀和:[1,0,2,5,1](全部≥0)
k=4:Q[4]=2 > minInWindow[5]=1 → 不满足
移位后数组:[-1,2,3,-4,1]
前缀和:[-1,…](-1<0,无效)

具体解释看代码!

#include
using namespace std;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n;
    cin >> n;
    vector<int> a(n);
    long long total = 0;
    for (int i = 0; i < n; i++) {
        cin >> a[i];
        total += a[i];
    }//先判读一下所有数字的和,如果所有数字和都小于0,那么就没有合法的数组了。

    if (total < 0) {
        cout << 0 << endl;
        return 0;
    }

    vector<int> A(2 * n);//数组扩充,避免循环取余问题,简化操作
    for (int i = 0; i < n; i++) {
        A[i] = a[i];
        A[i + n] = a[i];
    }

    vector<long long> Q(2 * n + 1, 0);//前缀和数组用来存放每个坐标的前缀和
    for (int i = 1; i <= 2 * n; i++) {
        Q[i] = Q[i - 1] + A[i - 1];
    }

    vector<long long> minInWindow(n + 1, 0);//记录窗口中的最小值
    deque<int> dq;//双端队列实现单调递增,注意dq中存放的是坐标

    for (int j = 0; j < 2 * n; j++) {
        while (!dq.empty() && Q[dq.back()] >= Q[j]) {
            dq.pop_back();
        }
        dq.push_back(j);

        if (dq.front() <= j - n) {//要保证求的最小前缀和在窗口内
            dq.pop_front();
        }

        if (j >= n) {
            int i = j - n + 1;
            if (i >= 1 && i <= n) {
                minInWindow[i] = Q[dq.front()];//窗口的长度到达数组长度后就将最小值赋给miInWindown容器
            }
        }
    }

    int count = 0;
    //将每一个值都和后面窗口的最小值比较,窗口最小值大于等于当前值才合法
    //因为证明窗口减去前面一大堆后仍然有能力保证前缀和大于等于0
    for (int k = 0; k < n; k++) {
        if (Q[k] <= minInWindow[k + 1]) {
            count++;
        }
    }

    cout << count << endl;
    return 0;
}

你可能感兴趣的:(c++,算法)