3月校赛(NWPU)

版权声明:欢迎评论与转载,转载时请注明出处! https://blog.csdn.net/wjl_zyl_1314/article/details/88736548

#A Chino with Geometry:
–>题目传送门<–
题解:
一道数学几何题,由于题目已经说必然相割,所以不用考虑相离的情况,计算|BD|* |BE|,分解来看,
设DE的中垂线DE的交点为O,BD=BO-DO,BE=BO+EO;所以BDBE=(BO-DO)(BO+EO),由于DO=EO,所以右式=BO平方-EO平方,又因为BO平方=AB平方-AO平方,EO平方=R平方-AO平方。所以答案为AB平方减去R平方。答案必定为整数,只需注意变量范围开至long long即可。
AC代码:

#include <iostream>
using namespace std;
#define LL long long
int main()
{
    LL x0,y0,r,x1,y1,y2;
    cin>>x0>>y0>>r>>x1>>y1>>y2;
    LL ab=(x0-x1)*(x0-x1)+(y0-y1)*(y0-y1);
    cout<<ab-r*r<<endl;
    return 0;
}

#B:Chino with Segment Fault:
–>题目传送门<–
题解:
由于代码块都是相连的,即最低一行是没有空格的,所以由于xi不起作用,所以可以把每一个代码块都当作宽度为1高度为yi的长条形代码块。
现在简化后,就可以看到代码块整体就类似于金字塔形。使一个矩形尽可能多的覆盖就能使矩阵块用的最少。这样我们以囊括高度相同的代码部分用矩形覆盖即可保证使用的最小(类似题目样例)。
具体操作,遍历时利用一个数组来记录当前的最高高度,如果下一个比当前高度高,则加入数组,并更新最高高度,如果和当前相同,则不用管,如果比当前小,则表示金字塔要走下坡了,此时就需要用矩形覆盖上一个最高高度,因为后面不可能与其相连了。直至结束或者下一个比当前最高高度高。
重复上面操作,直至结束即可。
AC代码(内有详细解释):

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

int n, W;
int x[N], y[N];
int tp, st[N];

int main() {
    scanf("%d%d", &n, &W);
    for (int i = 1; i <= n; ++i)
        scanf("%d%d", x + i, y + i);
    y[++n] = 0;//最后一个高度为0作为结束,因为每一个yi都大于0
    int ret = 0;
    for (int i = 1; i <= n; ++i) {//由于每一个内存块都是连在一起的所以xi并不重要,可以看作是每一个都站一个格子的yi高度的内存条
        while (tp && st[tp] > y[i]) --tp, ++ret;//如果当前的最高高度比下一个要高说明我要用掉一行来覆盖最高的那一条,因为他不会与其他内存条再连接了
        while (tp && st[tp] == y[i]) --tp;//如果相同高度,则只用把当前高度向前移一位,因为这一块可以与前一个用同一行覆盖,tp--免得当st【tp】高于yi时多计算结果

        st[++tp] = y[i];//总是记录当前最高高度
        //cout<<i<<" "<<y[i]<<" "<<tp<<" "<<st[tp]<<" "<<ret<<endl;
    }
    printf("%d\n", ret);
    return 0;
}

#C:
–>题目传送门<–
签到题,2^x大于等于n即为所需复读次数。
AC代码:

#include <iostream>
#include <bits/stdc++.h>
using namespace std;
#define LL long long
int main()
{
    LL x;
    cin>>x;
    for(int i=1;;i++)
    {
        if(pow(2,i)>=x)
        {
            cout<<i<<endl;
            return 0;
        }
    }
    return 0;
}

#D:Chino with Queue
–>题目传送门<–
题解:
第一次写这种题目,也是见识到了状压dp,何为状压,就是状态压缩,即将当前状态利用二进制压缩到数组中。这道题目里的f【i】【n】即为当前状态为n的二进制例如“111”(代表第1,2,3个人都被安排好了),此时以i为队尾的最大舒适度。更多细节见代码。
AC代码:

#include<bits/stdc++.h>
#define N 20
using namespace std;
//状压dp
int n, f[N][1 << N], W[N][N];//f[i][j]表示以i为队尾的当前队列的状态为j的二进制时候的最大舒适度
int max(int a, int b){
	return a>b?a:b;
}
int solve() {
    for (int i = 0; i < n; ++i) f[i][1 << i] = W[i][i];//初始化
    for (int i = 1; i < (1 << n); ++i)
        for (int j = 0; j < n; ++j)
            for (int k = 0; k < n; ++k)
                if (((i & (1 << j)) == 0) && (i & (1 << k)))//如果第j个人没有被排并且第k个人被排了(如果不懂为啥可以去搜索一下状压dp)
                    f[j][i | (1 << j)] = max(f[j][i|(1<<j)], f[k][i] + W[j][k]);//当前的状态和将第j个人插在第k个人后面
    int ret = 0;
    for (int i = 0; i < n; ++i) ret = max(ret, f[i][(1<<n)-1]);//找出最大值,f【i】【(1<<n)-1】表示所有人都被安排了,并且第i个人为队尾的状态下的最大舒适度
    return ret;
}

int main() {
    cin >> n;
    for (int i = 0; i < n; ++i)
	for (int j = 0; j < n; ++j)
	    cin >> W[i][j];
    cout << solve() << endl;
    return 0;
}

#E Chino with Equation:
–>题目传送门<–
题解:
这道题目可以看作是一个排列组合的简单题。
如果求正整数解,可以看作是将n分成m份,插空法,总共n-1个空,要插m-1个。
答案为c(n-1,m-1)(组合数)
求非负整数解,相当于将每一个解都加1,类似于上面一样,只不过一共n+m-1个空。
答案为c(n+m-1,m-1)
不过在这里要用一个费马小定理求逆元:a^-1=pow(a,mod-2)
这样将除法转换成乘法,方面求余。
AC代码:

#include <bits/stdc++.h>
#define N 3000100
#define bmod 1000000007
using namespace std;

long long m, n;
long long fact[N];

long long pow_mod(long long a, long long x, long long p) {//快速幂
    long long ret = 1; a %= p;
    for (; x; x >>= 1) {
        if (x & 1) ret = (ret * a) % p;
        a = (a * a) % p;
    }
    return ret;
}

int main() {
    cin >> m >> n;
    fact[0] = 1LL;
    for (long long i = 1LL; i <= (n << 1); ++i) fact[i] = (fact[i - 1] * i) % bmod;//阶乘
    long long ret1 = (((fact[n - 1] * pow_mod(fact[m - 1], bmod - 2, bmod)) % bmod) * pow_mod(fact[n - m], bmod - 2, bmod)) % bmod; // (n - 1, m - 1)组合数 插空法
    long long ret2 = (((fact[n + m - 1] * pow_mod(fact[m - 1], bmod - 2, bmod)) % bmod) * pow_mod(fact[n], bmod - 2, bmod)) % bmod; // (n + m - 1, m - 1)//相当于将m个数每一个都加上1,计算正整数的解就似乎,其非负整数解
    cout << ret1 << " " << ret2 << endl;
    return 0;
}

#G Chino with Expectation:
–>题目传送门<–
题解:
题意理解了很好做,利用前缀和,计算区间和,每次询问时,只需要加上r-l+1个x 再加上n个区间和,求期望即可。
AC代码:

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

int n, q, a, d, l, r;
double sum[N], ave1, ave2, ans;

int main() {
	scanf("%d%d", &n, &q);
	for (int i = 1; i <= n; ++i) scanf("%d", &a), sum[i] = sum[i - 1] + 1.0 * a;
	while (q--) {
		scanf("%d%d%d", &d, &l, &r);
		double p = 1.0 * (r - l + 1) / n, _sum = sum[r] - sum[l - 1];
		ave1 = 1.0 * _sum / (r - l + 1);
		ave2 = 1.0 * (_sum + d) / (r - l + 1);
		ans = ave1 * (1 - p) + ave2 * p;
		printf("%.12f\n", ans);
	}
	return 0;
}

#H Chino with Train to the Rabbit Town:
–>题目传送门<–
又是一道动态规划题。
题解:
f【i】数组表示排到第i个人时能够满足意愿值为k的最大车箱数。
pre【i】表示异或前缀和为i的下标。
细节见代码
AC代码:

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

int n, k, ret, f[N], pre[N], sum[N];

int main() {
    memset(pre, -1, sizeof(pre));
    ret = pre[0] = sum[0] = 0;
    scanf("%d%d", &n, &k);
    for (int i = 1, a; i <= n; ++i) {
        scanf("%d", &a);
        sum[i] = sum[i - 1] ^ a;//异或前缀和
        f[i] = f[i - 1];//当前长度为i时,能满足的最多车厢数
        if (pre[sum[i] ^ k] != -1)//因为sum^k如果存在那么在哪个节点之后的人的异或值一定为k,因为当前所有人异或和 为sumi=sumi^k^k
            f[i] = max(f[i], f[pre[sum[i] ^ k]] + 1);//选出较大的
        ret = max(ret, f[i]);
        pre[sum[i]] = i;//前缀和为sum的下表
    }
    printf("%d\n", ret);
    return 0;
}

欢迎评论!

猜你喜欢

转载自blog.csdn.net/wjl_zyl_1314/article/details/88736548
今日推荐