信息工程大学第五届超越杯程序设计竞赛 题解

本文概述了信息工程大学第五届超越杯程序设计竞赛中的五道题目,包括简单的数据修改、分数计算、数对操作模拟、人员动态问题和树结构处理等,展示了不同的算法策略和思路。
摘要由CSDN通过智能技术生成

信息工程大学第五届超越杯程序设计竞赛 \huge{信息工程大学第五届超越杯程序设计竞赛} 信息工程大学第五届超越杯程序设计竞赛

写在前面

本篇题解按照题目难易顺序进行排序

大致难易顺序为:A<M<F<D<C<E<G<K<H<B<I<J

A. 遗失的旋律

这道题其实不是一道签到题,但是数据范围太水了比较友好(可能是为了适应校赛难度),暴力就可以过,所以就成了一道签到题。

题意

给定一个长度为 n n n 01 01 01 s s s,代表两种操作:

  1. 0 0 0代表:x++;
  2. 1 1 1代表:x *= 2;

然后给定m次询问,对应两种情况:

  1. o p   y op~ y op y:翻转 s [ y ] s[y] s[y]
  2. o p   x   l   r op~ x~ l~ r op x l r x x x为初始值,执行 s [ l , r ] s[l,r] s[l,r]区间的操作,并返回结果。

思路

暴力…

标程

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

const int N = 1e6+5;
int mod = 998244353;

void solve() {
	int n,m; cin >> n >> m;
	string s; cin >> s;
    int res = 0, le = 0, re = 0;
    
    while(m -- ) {
        int op; cin >> op;
        if(op == 1) {
            int y; cin >> y; y --;
            if(s[y] == '1') s[y] = '0';
            else s[y] = '1';
        } else {
            int x, l, r; cin >> x >> l >> r;
            l --,r --;
            for(int i = l ; i <= r; i++) {
                if(s[i] == '0') x += 1;
                else x = x * 2;
                x = x % mod;
            }
            cout << x << endl;
        }
    }
}

signed main (void) {
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	int T = 1;
// 	cin >> T;
	while(T--) solve();
	return 0;
 }

M. Monika’s game

题意

给定一个整数 x x x,然后可以操作若干次,每次可以将 x x x拆分为不为零的两个数,然后总分会加上拆分的较大数,求最多能加多少分

思路

每次都将 x x x拆分为 1 1 1 x − 1 x-1 x1,结果为: 1 + 2 + 3... + n − 1 = n × n − 1 2 1+2+3...+n-1=n\times\frac{n-1}{2} 1+2+3...+n1=n×2n1

标程

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

void solve()
{
	int n; cin >> n;
	n --;
	cout << (n * n + n) / 2 << '\n';
}

signed main (void) {
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	int T = 1; 
    cin >> T;
	while(T--) solve();
	return 0;
 } 

F. 不规则的轮回

题意

给定 n n n 个数对,对于每个数对$ (x,y) $会持续执行以下操作直到 x = y x=y x=y

  1. x > y x > y x>y 时, x = x − y x = x - y x=xy
  2. x < y x < y x<y 时, y = y − x y = y - x y=yx

同时有 q 次询问,每次给定一个数对 x q , y q x_q,y_q xq,yq​, 问上述给定的 n 个数对的执行过程中出现了 x q , y q xq,yq xq,yq 的次数。

思路

模拟,然后用一个二维数组记录,然后 O ( 1 ) O(1) O(1)查询。

标程

#include<bits/stdc++.h>
#define int long long 
#define endl "\n"
using namespace std;
const int mod=1000000;
const int N = 1e4+5;
int a[N],b[N],c[N][N];
void solve() {
	int n,m,l,r; cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i]>>b[i]; 
	for(int i=1;i<=n;i++){
		int aa=a[i],bb=b[i];
		c[aa][bb]++;
        while(aa!=bb){
			if(aa>bb){
                aa -= bb;
			}else if(aa<bb){
                bb-=aa;
            }
            c[aa][bb]++;
		}
	}
	cin>>m;
	while(m--){
		cin>>l>>r;
		cout<<c[l][r]<<endl;
	}
	return ;
}
signed main (void)
{
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	int T = 1;
	//cin >> T;
	while(T--) solve();
	return 0;
 } 

D. 实验室有多少人

题意

给出 n n n次记录,每次记录有两个数组 a , b a,b a,b,代表当前有一个人从 a a a时刻来, b b b时刻离开;求实验室最多有多少人。

1 ≤ n ≤ 1 × 1 0 6 1\le n\le 1\times10^6 1n1×106 1 ≤ x , y ≤ 1 × 1 0 9 1\le x,y\le 1\times10^9 1x,y1×109

思路

跟据数据范围,这道题如果采用差分标记区间的方法,必然会导致超时。

跟据 n n n的范围,我们考虑在 O ( n l o g n )   o r   O ( n ) O(nlogn)~or~ O(n) O(nlogn) or O(n)的时间复杂度解决。

n n n组数分别有 n n n个数字代表某个人在当前时刻来, n n n个数字代表这个人在当前时刻离开。

因此我们需要将这 2 n 2n 2n个数字存起来,排序,然后遍历并记录最多人数。

关键在于排序:按照时间升序排列,相同时间来的人排在前面。

标程

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

const int N = 1e6+5;
int arr[N];
#define PII pair<int, int>

bool cmp(PII n1, PII n2) {
    if(n1.first == n2.first) return n1.second < n2.second;
        return n1.first < n2.first;
}

void solve() {
	int n; cin >> n;
    vector<PII> a;
    for(int i = 1; i <= n; i ++ ) {
        int x, y; cin >> x >> y;
        a.push_back({x, 1});
        a.push_back({y, 2});
    }
    sort(a.begin(), a.end(), cmp);
    
    int mx = 0, x = 0;
    for(PII n1 : a) {
        if(n1.second == 1) x ++;
        else x --;
        mx = max(mx, x);
    }
    
    cout << mx << endl;
}

signed main (void) {
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	int T = 1;
//	cin >> T;
	while(T--)solve();
	return 0;
 } 

C. 财政大臣

题意

给出一棵有 n n n个节点的树( 1 , 2 , 3 , . . . , n 1,2,3,...,n 1,2,3,...,n),父节点的变化将会影响所有子节点,然后给出父节点的变化,求最后所有节点的值。每个节点都给的有初始值,然后根节点为 1 1 1

思路

跟据题意可知,每个节点的结果都是:父节点带来的变化+自身的变化+自身的初始值

因此我们将所有变化用数组存起来,然后遍历整棵树,计算变化值,最后输出时加上初始值。

标程

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

const int N = 1e5+5;
vector<int> a[N];
int b[N], c[N];	//b数组记录初始值,c数组记录变化值

void bianli(int x) {
    if(a[x].empty()) return;
    for(auto i : a[x]) {
        c[i] += c[x];
        bianli(i);
    }
}

void solve() {
	int n, m; cin >> n >> m;
    for(int i = 1; i < n; i ++ ) {
        int x, y; cin >> x >> y;
        a[x].push_back(y);
    }
    for(int i = 1; i <= n; i ++ ) cin >> b[i];
    
    while(m -- ) {
        int op, x, y; cin >> op >> x >> y;
        if(op == 1) c[x] += y; 
        else c[x] -= y; 
    }
    bianli(1);
    
    for(int i = 1; i <= n; i ++ ) 
        cout << b[i] + c[i] << " \n"[i == n]; 
}

signed main (void) {
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	int T = 1;
// 	cin >> T;
	while(T--) solve();
	return 0;
}

E. 在雾中寻宁静

题意

给出一棵有 n n n个节点的树( 1 , 2 , 3 , . . . , n 1,2,3,...,n 1,2,3,...,n),然后给节点染色,同时该节点的子节点都会染成该颜色。最后输出每个节点的颜色。每个节点的初始颜色为 0 0 0,然后根节点为 1 1 1

思路

与F题类似,本题需注意前面染的颜色可能会被后面的颜色覆盖。因此本题只需在记录染色操作时记录下先后顺序,记录顺序也非常简单,用二元组。

标程

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

const int N = 2e5+5;
vector<int> a[N];
pair<int, int> c[N];

void bianli(int x) {
    if(a[x].empty()) return;
    for(auto i : a[x]) {
        if(c[i].second < c[x].second) //判断是否再此之后染色
            c[i] = c[x];
        bianli(i);
    }
}

void solve() {
	int n; cin >> n;
    for(int i = 1; i < n; i ++ ) {
        int x; cin >> x;
        a[x].push_back(i + 1);
    }
    int m; cin >> m;
    for(int i = 1; i <= m; i ++ ) {
        int x, y; cin >> x >> y;
        c[x].first = y;
        c[x].second = i;	//记录顺序
    }
    bianli(1);
    
    for(int i = 1; i <= n; i ++ ) 
        cout << c[i].first << " \n"[i == n]; 
}

signed main (void) {
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	int T = 1;
// 	cin >> T;
	while(T--) solve();
	return 0;
}

G.完美数字

题意

给定 n , k n,k n,k,然后给出 n n n个整数,要求计算 n n n个整数中,乘积尾数有 k k k个以上的 0 0 0 的序列区间个数。

思路

首先,我们需要知道一串数中序列乘积有几个0,这是怎么计算的?

我们发现,所有乘积尾数中包含0的数中,其因子必然有2和5

因此,我们需要统计序列中每个数字是由多少个 2 2 2和多少个 5 5 5相乘得到。

由于下面要求区间是否符合条件,我们可以将 2 2 2 5 5 5的个数用前缀和维护。

那么,区间中所有数相乘后尾数 0 0 0的个数即为: m i n ( s u m 2 , s u m 5 ) min(sum_2, sum_5) min(sum2,sum5)

最后,若当前区间符合条件,那么当前区间继续延申当然也符合条件。

标程

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

const int N = 2e5 + 5;
int a[N], a2[N], a5[N],b2[N],b5[N];

void check(int x,int i) {
    int t = x;
    while(t % 2 == 0) t /= 2, a2[i] ++;
    t = x;
    while(t % 5 == 0) t /= 5, a5[i]++;
}

void solve()
{
	int n, k; cin >> n >> k;
    for(int i = 1; i <= n; i ++ ) {
        cin >> a[i];
        check(a[i],i);
    }
    
    for(int i = 1; i <= n; i ++ ) {
        b2[i] = b2[i-1] + a2[i];
        b5[i] = b5[i-1] + a5[i];
    }
    
    int res = 0;
    for(int l=0;l<=n;l++) {
    	for(int r = l + 1; r <= n; r ++) {
    		int t2 = b2[r] - b2[l];
	    	int t5 = b5[r] - b5[l];
	    	int sum = min(t2, t5);
	    	if(sum >= k) {
	    		res += n - r + 1;
	    		break;
			}
		}
	}
    cout << res << endl;
}

signed main (void) {
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	int T = 1;
// 	cin >> T;
	while(T--) solve();
	return 0;
}  

K.天使的拼图

题意

给出一个图形,然后给出若干组矩形的边长,问是否能由该图形刚好填充这个矩形。

思路

跟据题目提供的图形,我们可以拼出图二 3 × 4 3\times4 3×4大小的矩形。

试一试可知只能拼成如图三图四这种图形。
在这里插入图片描述

标程

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

void solve(){
    int n,m;cin>>n>>m;
    if(n<m)swap(n,m);
    if((n%3==0&&m%4==0)||(n%4==0&&m%3==0)||((n%12==0)&&m>=7)||((m%12==0)&&n>=7))puts("Yes");
    else puts("No");
}

signed main (void) {
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	int T = 1;
// 	cin >> T;
	while(T--) solve();
	return 0;
}  

H.春天与花儿

题意

给出一个长度为 n n n的序列,我们可以对序列进行操作:

  • 选择一个数,将其加一。

给出一个数字 k k k,问至少操作几次可以让序列的乘积变为其倍数。

思路

赛时没过,调了半天,狠狠坐牢了一把。。。

容易发现本题数据范围很小,应该要考虑到特判或者爆搜的情况!

  • 1 − 6 1-6 16中,只有 4 , 6 4,6 4,6可以由除一之外的数字相加得到。
  • 如果 x x x可以整除 y y y,那么 x x x中至少包含 y y y的一对因子。

标程(DFS)

想要乘积是 m m m的倍数,则乘积必然包含m的所有因子,因此我们暴力搜索出满足所有可能,找到的第一个解决方案即为最优解。

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

int i,j,k,m,n,t,res,a[66];

bool chk(){
	if(a[0])return 1;
	int sb=1;
	for(i=1;i<m;i++){
		for(j=1;j<=min(m-1,a[i]);j++)sb*=i;
		sb%=m;
	}
	if(!sb)return 1;
	return 0;
}

void dfs(int dep){
	if(dep>res)return;
	if(chk()){
		res=min(res,dep); return;
	}
	int i,j,k;
	for(i=1;i<m;i++)if(a[i]){
		j=(i+1)%m;
		a[i]--; a[j]++;
		dfs(dep+1);
		a[i]++; a[j]--;
	}
}

int main(){
	ios::sync_with_stdio(0); cin.tie(0);
	cin>>n>>m; res=m-1;
	for(i=1;i<=n;i++){
		cin>>k; a[k%m]++;
	}
	dfs(0);
	cout<<res;
}
  • 25
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值