2021NUC_ACM新生赛

总之就是痛心疾

A.中北折磨王

题目描述

输入描述

输出描述

样例输入

98 63

样例输出

7

思路

不知道各位在做的时候有没有一种感觉, 和熟知的辗转相除法即最大公约数很像, 实际上的确是, 名字为更相减损法, 考验细节的时候到了
最大公约数求解还有一点就是gcd(a, b) == gcd(b, a % b), 其名为欧几里得算法

代码

#include <iostream>

using namespace std;

int gcd(int a, int b) {
    return b ? gcd(b, a % b) : a;
}

int main() {
    int x, y;
    cin >> x >> y;
    cout << gcd(x, y) << endl;
    return 0;
}

B.买橘子(未完成)

题目描述

输入描述

输出描述

样例输入

5 6
1 2 3 4 6
1
2
3
4
5
1

样例输出

0
1
3
5
6
2

思路

据说是莫比乌斯反演

代码

C.数论小王子

题目描述

输入描述

输出描述

样例输入

5 4
1 1 1 1 1
1 2 3 1
3 2 5
2 2 4 3
3 2 5

样例输出

0
5

提示

思路

裸的线段树了, 但是还是有很多实现细节

代码

#include <iostream>

using namespace std;
typedef long long LL;

const int N = 1e5 + 10;

int n, m;
LL w[N];
LL q1,q2;

struct Node {
    int l, r;
    LL sum1, sum2;
    LL add, mul;
}tr[N * 4];

void build(int i, int l, int r);

void query(int i, int l, int r);

void update(int u, int l, int r, LL c, int type);

void pushup(int i);
void pushup(Node& u, Node& l, Node& r);

void pushdown(int u, int len);
void pushdown(Node& u, Node& l, Node& r, int len);

int main() {
    cin >> n >> m;
    for (int i = 1; i <= n; i ++) cin >> w[i];

    build(1, 1, n);

    while (m --) {
        int op, l, r;
        LL c;
        cin >> op;
        if (op == 3) {
            cin >> l >> r;
            q1 = q2 = 0;
            query(1, l ,r);
            int cnt = r - l + 1;
            double sumx = q1 * 1.0 / cnt;
            double t = (q2 - 2 * sumx * q1 + sumx * sumx * cnt) / cnt;
            //printf("%d %d %.3lf %d ", q2, q1, sumx, cnt);
            printf("%.0lf\n", t);
        }
        else {
            cin >> l >> r >> c;
            if (op == 1) update(1, l, r, c, 1);
            else update(1, l, r, c, 2);
        }
    }

    return 0;
}

void update(int u, int l, int r, LL c, int type) {
    if(tr[u].l >= l && tr[u].r <= r){
        if(type == 2){
            tr[u].mul *= c;
            tr[u].add *= c;
            tr[u].sum1 *= c;
            tr[u].sum2 *= c * c;
        }else{
            tr[u].add += c;
            tr[u].sum2 += 2 * c * tr[u].sum1 + c * c * (tr[u].r - tr[u].l + 1);
            tr[u].sum1 += c * (tr[u].r - tr[u].l + 1);
        }
        return ;
    }
    pushdown(u, tr[u].r - tr[u].l + 1);
    int mid = (tr[u].l + tr[u].r) >> 1;
    if(l <= mid) update(u << 1, l, r, c, type);
    if(r > mid) update(u << 1 | 1, l, r, c, type);
    pushup(u);
}

void query(int u, int l, int r) {
    if(tr[u].l >= l && tr[u].r <= r){
        q1 += tr[u].sum1;
        q2 += tr[u].sum2;
        return ;
    }
    int mid = (tr[u].l + tr[u].r) >> 1;
    pushdown(u, tr[u].r - tr[u].l + 1);
    if(l <= mid) query(u << 1, l, r);
    if(r > mid) query(u << 1 | 1, l, r);
    //pushup(rt);
}

void build(int i, int l, int r) {
    tr[i] = { l, r, 0, 0, 0, 1 };
    if(l == r){
        tr[i].sum1 = w[l];
        tr[i].sum2 = w[l] * w[l];
        return ;
    }
    int mid = (l + r) >> 1;
    build(i << 1, l, mid);
    build(i << 1 | 1, mid + 1, r);
    pushup(i);
}

void pushup(int i) {
    pushup(tr[i], tr[i << 1], tr[i << 1 | 1]);
}
void pushup(Node& u, Node& l, Node& r) {
    u.sum1 = l.sum1 + r.sum1;
    u.sum2 = l.sum2 + r.sum2;
}

void pushdown(int u, int len) {
    pushdown(tr[u], tr[u << 1], tr[u << 1 | 1], len);
}
void pushdown(Node& u, Node& l, Node& r, int len) {
    r.mul *= u.mul;
    l.mul *= u.mul;

    l.add = u.add + l.add * u.mul;
    r.add = u.add + r.add * u.mul;

    l.sum2 = l.sum2 * u.mul * u.mul + 2 * u.add * l.sum1 + (len - (len >> 1)) * u.add * u.add;
    r.sum2 = r.sum2 * u.mul * u.mul + 2 * u.add * r.sum1 + (len >> 1) * u.add * u.add;

    l.sum1 = u.add * (len - (len >> 1)) + l.sum1 * u.mul;
    r.sum1 = u.add * (len >> 1) + r.sum1 * u.mul;

    u.mul = 1;
    u.add = 0;
}

D.wsp的机器人(新加)

题目描述

输入描述

输出描述

样例输入

7 2
1 2 3 4 5 6 7
1 1
1 2

样例输出

1
3

思路

经典区间和, 利用前缀和求解即可
注意会卡掉cin

代码

#include <iostream>

using namespace std;
typedef long long LL;

const int N = 1e5 + 10;

int n, m;
LL f[N];

int main() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i ++) scanf("%lld", f + i), f[i] += f[i - 1];

    while (m --) {
        int l, r;
        scanf("%d%d", &l, &r);
        printf("%lld\n", f[r] - f[l - 1]);
    }
    return 0;
}

E.找最短

题目描述

输入描述

输出描述

样例输入

14
bacbacbacbacba

样例输出

3

思路

和字符串有关, 那必然是KMP了, 结论是n - next[n]

代码

#include <iostream>
#include <cstring>

using namespace std;

const int N = 1e6 + 10;

int n;
string str;
int ne[N];

int main() {
    cin >> n;
    cin >> str;
    str = " " + str;
    for (int i = 2, j = 0; i <= n; i ++) {
        while (j && str[i] != str[j + 1]) j = ne[j];
        if (str[i] == str[j + 1]) j ++;
        ne[i] = j;
    }

    cout << n - ne[n] << endl;
    return 0;
}

F.我要进ACM

题目描述

输入描述

输出描述

样例输入

3

样例输出

我要进ACM
我要进ACM
我要进ACM

思路

代码

#include <iostream>

using namespace std;

int main() {
    int T;
    cin >> T;
    while (T --) {
        puts("我要进ACM");
    }
    return 0;
}

G.小智的难题(感谢张舟学长的分享)

题目描述

输入描述

输出描述

样例输入

3 6 3
2 4
4 2
0 0

样例输出

28

思路

第一部分 n列,每一列大于等于1小于等于k,总数为m。转换为容斥模型,1<=x<=k, 总数为m,替换为 0<=y<=k-1,总数为m-n,
容斥枚举有多少个违反<=k-1的条件,具体可以参考博客:https://blog.csdn.net/codeswarrior/article/details/81906367
复杂度O(m)级别,因为某些客观条件将数据范围减小后,可能认为某些dp方法也可以通过(大概率过不去),这里给出未经测试的dp思路
dp[0] = 1;
for(int i=1;i<=n;i++)//枚举列
for(int j=m;j>=i;j--)//枚举当前用过的总座位数
for(int p=1;p<=min(k,i);p++)//枚举当前列用的座位数
dp[i] = (dp[i-p]+dp[i])%mod;
第二部分最直接的思路 dp(i,j)表示当前列红色权值和为i,蓝色权值和为j的方案数,时间复杂度O((5n)^2 * n)
进一步优化 dp( i )表示到当前列红色权值和减去蓝色权值和为i的方案数 时间复杂度O(10 * n^2)

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
char in[] = "0.in";
char out[] = "0.out";
const int N = 1e3+10,M = 11,mod = 1e9+7;
typedef long long ll;
int a[N],b[N],n,m,k;
int kmi(int a,int b){
	int ans = 1;
	while(b){
		if(b&1) ans = 1ll*ans*a%mod;
		b>>=1;
		a = 1ll*a*a%mod;
	}
	return ans;
}
const int maxn = 1e6+10;
int fac[maxn],inv[maxn];
void init(){
	fac[0] = inv[0] = 1;
	
	for(int i=1;i<maxn;i++) fac[i]=1ll*fac[i-1]*i%mod;
	for(int i=1;i<maxn;i++) inv[i]=1ll*inv[i-1]*kmi(i,mod-2)%mod;
}
int C(int n,int m){
	if(n<0||m<0||n<m) return 0;
	return 1ll*fac[n]*inv[m]%mod*inv[n-m]%mod;
}
const int maxm = 4e4+10,midm = 1.5e4;
int dp[N][maxm];
void cal()
{
	memset(dp,0,sizeof dp);
	cin>>n>>m>>k;
	for(int i=1;i<=n;i++) scanf("%d%d",&a[i],&b[i]);
	if(m<n){
		cout<<0<<endl;
		return;
	}
	ll ans = 0;
	for(int i=0,op=1;i<=n&&i*k<=m-n;i++,op*=-1){
		ans = ((ans + 1ll*op*C(n,i)*C(m-1-i*k,n-1)%mod)%mod + mod)%mod;
		//cout<<"--"<<1ll*C(n,i)*C(m-1-i*k,n-1)%mod<<endl;
	}
	dp[0][midm] = 1;
	for(int i=1;i<=n;i++){
		for(int j=-1e4;j<=1e4;j++){
			dp[i][j+midm] = (1ll*dp[i][j+midm]+dp[i-1][j-a[i]+midm])%mod;
			dp[i][j+midm] = (1ll*dp[i][j+midm]+dp[i-1][j+b[i]+midm])%mod; 
		}
	}
	cout<<1ll*ans*dp[n][midm]%mod<<endl;
}

int main()
{
	init();
	for(int i = 1; i < 10; i ++)
	{
		in[0] = char(i+'0');
		out[0] = char(i+'0');
		freopen(in, "r", stdin);
		freopen(out, "w", stdout);
		cal();
	}
	return 0;
}

H.clq找队友

题目描述

输入描述

输出描述

样例输入

5 4 1 3 5
1 2 1
2 3 1
3 4 1
4 5 1

样例输出

8

提示

思路

最短需要走的距离, 最短路无疑了, 那么该如何走呢需要走去两个点并且走回原地

  • 从起点走去A点, 然后去B点, 最后回起点
  • 从起点走去A点并且回原点, 然后去B点并且回原点
  • 还有就是只能去到一个点的时候, 这个情况下只需要去到一个点然后返回即可
    取上面两种情况的最小值, 注意long long问题

代码

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>

#define x first
#define y second

using namespace std;
typedef long long LL;
typedef pair<LL, int> PII;

const int N = 1e5 + 10, M = 2e6 + 10;

int n, m, S, F1, F2;
int h[N], e[M], ne[M], idx;
LL w[M];
LL d[N];
bool st[N];

void dijkstra(int u) {
    memset(st, false, sizeof st);
    for (int i = 1; i <= n; i ++) d[i] = 1e18;
    d[u] = 0;
    priority_queue<PII, vector<PII>, greater<PII> > q;
    q.push({ 0, u });

    while (q.size()) {
        PII t = q.top();
        q.pop();

        if (st[t.y]) continue;
        st[t.y] = true;

        for (int i = h[t.y]; i != -1; i = ne[i]) {
            int j = e[i];
            if (d[j] > t.x + w[i]) {
                d[j] = t.x + w[i];
                q.push({ d[j], j });
            }
        }
    }
}

void add(int a, int b, LL c) {
    e[idx] = b;
    ne[idx] = h[a];
    w[idx] = c;
    h[a] = idx ++;
}

int main() {
    memset(h, -1, sizeof h);

    cin >> n >> m >> S >> F1 >> F2;
    for (int i = 0; i < m; i ++) {
        int a, b;
        LL c;
        cin >> a >> b >> c;
        add(a, b, c);
        add(b, a, c);
    }

    dijkstra(S);
    LL a = d[F1], b = d[F2];

    dijkstra(F1);
    LL c = d[F2];
    
    if (a >= 1e18 && b >= 1e18) puts("clq meng le");
    else if (a >= 1e18) cout << b * 2 << endl;
    else if (b >= 1e18) cout << a * 2 << endl;
    else cout << min((a + b) * 2, a + b + c) << endl;
    return 0;
}

I.暖男zsz

题目描述

输入描述

输出描述

样例输入

5
11011

样例输出

2

思路

一个人想要吃饭就必须拿到两只筷子, 即左右都必须有一只筷子, 即有两个连续的1
那么只需要统计连续的1的数量, 然后除以2就是答案, 因为是环形的, 不如将最开始的1算在最后一个1上面,

代码

#include <iostream>
#include <vector>

using namespace std;

int n;
string str;
vector<int> a;

int main() {
    cin >> n;
    cin >> str;

    int i = 0;
    while (i < n && str[i] == '1') i ++;
    for (int j = i; j < n; j ++)
        if (str[j] == '0') a.push_back(0);
        else a.back() ++;
    a.back() += i;

    int ans = 0;
    for (int i = 0; i < a.size(); i ++)
        ans += a[i] / 2;
    cout << ans << endl;
    return 0;
}

G.谁说我不聪明

题目描述

输入描述

输出描述

样例输入

2 1
8 4
4 7

样例输出

ff
xy
ff

思路

威佐夫博弈

代码

#include <iostream>
#include <cmath>

using namespace std;

int main() {
    int x, y;
    double t = (1 + sqrt(5)) / 2;
    while (cin >> x >> y) {
        if (x < y) swap(x, y);
        
        if ((int)((x - y) * t) == y) puts("ff");
        else puts("xy");
    }
    return 0;
}

K.别走泥坑

题目描述

输入描述

输出描述

样例输入

1 2 7
0 2
4 2
3 1
1 1
2 2
-1 1
-1 3

样例输出

11

思路

正常走迷宫即可, 主要是注意下标的hash

代码

#include <iostream>
#include <algorithm>
#include <queue>

#define x first
#define y second

using namespace std;
typedef pair<int, int> PII;

const int N = 1e3 + 10;

int X, Y, n;
bool g[N][N];
int d[N][N];

int dx[] = { 0, 0, 1, -1 };
int dy[] = { 1, -1, 0, 0 };

void bfs() {
    queue<PII> q;
    q.push({ 0 + 500, 0 + 500 });
    g[0 + 500][0 + 500] = true;

    while (q.size()) {
        PII t = q.front();
        q.pop();
        if (t.x == X && t.y == Y) break;

        for (int i = 0; i < 4; i ++) {
            int tx = t.x + dx[i], ty = t.y + dy[i];
            if (tx < 0 || tx > 1000 || ty < 0 || ty > 1000) continue;
            if (g[tx][ty]) continue;

            g[tx][ty] = true;
            d[tx][ty] = d[t.x][t.y] + 1;
            q.push({ tx, ty });
        }
    }
}

int main() {
    cin >> X >> Y >> n;
    for (int i = 0; i < n; i ++) {
        int a, b;
        cin >> a >> b;
        g[a + 500][b + 500] = true;
    }

    bfs();

    cout << d[X + 500][Y + 500] << endl;
    return 0;
}

L.最终圣战(感谢张佳豪学长的分享)

题目描述

输入描述

输出描述

样例输入

2
1 1 1 3 2 1
1 1 1 1 3 1

样例输出

2.9850
3.8264

思路

这道题是个数学题,具备三角函数知识与基本编程知识的就可以解掉,首先有三种情况

  • fabs(x-rx) <= rr
    ∠A的角度我们是可以求出来的,根据1/2absinC这个公式可以求出∠A的大小,然后再求小段圆弧所对应的角度,这个角度可以用大角减去上面角的角度得到,上面角已知两边并且是个直角三角形,根据公式得到小角的角度,对应的圆弧长就是小角+90度,总长度就是直线+这段圆弧的长度
    请添加图片描述

  • fabs(x-rx) > rr && ry <= y
    这种情况就是1/4段圆弧长 + 直线距离

  • fabs(x-rx) > rr && ry > y
    有些人看到这里会疑惑,但是题目中说明一定有解,那么这个圆上的点一定可以走,而坐标,半径已知,我们不难求出总长度

请添加图片描述

代码

#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
#define PI 3.1415926535

double len(double a, double b, double c, double d)
{
    return sqrt((a - c) * (a - c) + (b - d) * (b - d));
}

double hu(double r, double jd)
{
    return 2 * PI * r * (jd / 360.0);
}
int main()
{
    // freopen("D:\\1.in", "r", stdin);
    // freopen("D:\\9.out", "w", stdout);
    int t;
    cin >> t;
    while (t--)
    {
        double rx, ry, r, x, y, jd, rr;
        double ans = 0;
        cin >> rx >> ry >> rr >> x >> y >> r;
        if (fabs(rx - x) <= r)
        {
        	/*
				以下是根据S = 1/2absinC,以及反三角函数求角度,最后求弧长
			*/
            double S = rr * fabs(y - ry) / 2;
            double sinC = S * 2 / (len(rx, ry, x, y) * rr);
            double jd = asin(sinC) * 180.0 / PI + 90.0;
            double jd1 = acos(r / len(rx, ry, x, y)) * 180.0 / PI;
            ans = hu(rr, jd - jd1) + sqrt(len(rx, ry, x, y) * len(rx, ry, x, y) - r * r);
        }
        else
        {
            ans += hu(rr, 90.0);
            // cout << ans << endl;
            if (y >= ry)
            {
                if (x < rx)
                    ans += len(rx - rr, ry, x, y);
                else
                    ans += len(rx + rr, ry, x, y);
            }
            else
            {
                ans += fabs(x - rx) - fabs(rr) - sqrt(r * r - (y - ry) * (y - ry)) + r;
            }
        }
        printf("%.4lf\n", ans);
    }
    return 0;
}

M.简单的滑动解锁

题目描述

输入描述

输出描述

样例输入

2
0 1 
1 1 
0 1 
0 0 

样例输出

YES

思路

首先是肯定要枚举钥匙的四种旋转情况, 然后怎么判断是否重合呢?
题目中体现出来一个滑动的概念, 那么对于钥匙的i行j列, 直到锁孔的i行j列都不能有阻拦
特别主要TLE, 我们只需要枚举每一行最右边一个1即可

代码

#include <iostream>
#include <cstring>

using namespace std;

const int N = 1010;

int n;
int a[N][N], b[N][N], c[N][N];

bool check() {
    for (int i = 0; i < n; i ++)
        for (int j = n - 1; j >= 0; j --)
            if (a[i][j] == 1) {
                for (int k = 0; k <= j; k ++)
                    if (b[i][k] == 1)
                        return false;
                break;
            }
    return true;
}

void swap() {
    int l = 0, r = n - 1;
    for (int i = 0 ; i < n; i ++)
        for (int j = 0; j < n; j ++) {
            c[l][r] = a[i][j];
            l ++;
            if (l >= n) l = 0, r --;
        }
    memcpy(a, c, sizeof a);
}

int main() {
    scanf("%d", &n);
    for (int i = 0; i < n; i ++)
        for (int j = 0; j < n; j ++)
            scanf("%d", &a[i][j]);
    for (int i = 0; i < n; i ++)
        for (int j = 0; j < n; j ++)
            scanf("%d", &b[i][j]);

    for (int i = 0; i < 4; i ++) {
        if (check()) {
            puts("YES");
            return 0;
        }
        swap();
    }
    puts("NO");
    return 0;
}

N.翻译官

题目描述

输入描述

输出描述

样例输入

6
abCaDb

样例输出

abcadb

思路

遍历字符串, 将大写字符转化为小写即可, A的ASCII码比a小32

代码

#include <iostream>

using namespace std;

int n;
string str;

int main() {
    cin >> n;
    cin >> str;
    for (int i = 0; i < n; i ++)
        if (str[i] < 'a')
            str[i] += 32;
    cout << str << endl;
    return 0;
}

总结

题目总体难度不大, 思维量很小, 但是要求有一定的代码实现能力
题目还是没有开好, 在大家都做了好多的时候我一个也没有出来, 竟然会没有注意到中文乱码, 比赛慌神 果然还是练习太少了

posted @ 2021-12-12 08:27  哇唔?  阅读(493)  评论(0编辑  收藏  举报