CSP-S模拟赛1总结

T1

题面

路灯照明

时间限制:1秒        内存限制:256M

题目描述

小可是一个相信光的人,他最近在研究光与距离的问题。

给定一个 2∗22∗2 的网格,每个网格都有一盏路灯,且都在格点上,即:四盏路灯的位置分别是左上角, 右上角,左下角,右下角。

路灯都是需要耗电的,且耗电量与亮度有关,如果一盏路灯的耗电量是 x ,则它可以为他所在的格子提供 x 的亮度,并且为他相邻的格子提供 ⌊​2​​x​​⌋,为他对角的格子提供 ⌊​4​​x​​⌋的亮度,其中⌊x⌋表示对 x 向下取整。

某一个格子的亮度为四盏路灯为他提供的亮度之和,例如 左上角的灯耗电量为 4, 右上角的灯耗电量为 7,右下角的灯耗电量为 8,左下角的灯耗电量为 0,那么 左上角这个格子的亮度就是 4+⌊72⌋+⌊74⌋+04+⌊​2​​7​​⌋+⌊​4​​7​​⌋+0 。

现在我们对四个格子的最低亮度提出了要求,我们想要让四个格子的亮度都达到标准。你可以将每一盏灯的耗电量调节为任何一个大于等于零的整数,为了省电, 你希望四盏灯的耗电量之和尽可能的小,请问四盏灯的最小耗电量之和是多小?

输入描述

输入四个整数:a,b,c,d ,分别表示左上、右上、左下、右下 四个格子要求的亮度之和。

输出描述

输出一行一个整数表示四盏灯的最小耗电量之和。

输入样例1

  1. 50 24 25 12

输出样例1

  1. 50

样例说明1

左上角的位置的灯耗电量设置为 50,其它三个位置设为 0。即可满足亮度要求

输入样例2

  1. 8 8 8 8

输出样例2

  1. 15

样例说明2

4 盏灯耗电量依次为 4 3 4 4

数据描述

对于20%的数据:1≤a,b,c,d≤50

对于70%的数据:1≤a,b,c,d≤400

对于100%的数据:1≤a,b,c,d≤1500

解题思路

70分部分:枚举a,b,c的亮度,计算出d收到的影响,从而推出d的亮度,可以达到O(n^3)的时间复杂度

code

#include<bits/stdc++.h>
#define MAXN 100
#define ull unsigned long long int
#define ll long long int
using namespace std;
int a,b,c,d;
long long aa,bb,cc,dd;
int ans=0x3f3f3f3f;
int main(){
	freopen("lighting.in","r",stdin);
	freopen("lighting.out","w",stdout);
	scanf("%d%d%d%d",&a,&b,&c,&d);
	aa=a,bb=b,cc=c,dd=d;
	if(aa*bb*cc*dd<=10000000){
	for(int i=0;i<=a;i++){
		for(int j=0;j<=b;j++){
			for(int k=0;k<=c;k++){
				for(int o=0;o<=d;o++){
					if(i+j/2+k/2+o/4>=a&&j+i/2+o/2+k/4>=b&&k+i/2+o/2+j/4>=c&&o+j/2+k/2+i/4>=d){
						ans=min(ans,i+j+k+o);
					}
				}
			}
		}
	}
	}
	else{
	for(int i=0;i<=a;i++){
		for(int j=0;j<=b;j++){
			for(int k=0;k<=c;k++){
				int o=d-(j/2+k/2+i/4);
				if(i+j/2+k/2+o/4>=a&&j+i/2+o/2+k/4>=b&&k+i/2+o/2+j/4>=c&&o+j/2+k/2+i/4>=d){
					ans=min(ans,i+j+k+o);
				}
				
			}
		}
	}
	}
	cout<<ans;
	//i j
	//k o
	fclose(stdin);
	fclose(stdout);
	return 0;
}

100分部分

对总答案进行二分搜索,枚举a,d(对角线)的亮度,从而推出b的亮度大致范围,可以达到O(n^2logn)的时间复杂度

code

#include<bits/stdc++.h>
#define MAXN 100
#define ull unsigned long long int
#define ll long long int
using namespace std;
int a,b,c,d;
int ans=0x3f3f3f3f;
bool check(int mid){
	for(int i=0;i<=a;i++){
		for(int j=0;j<=d;j++){
			int need=max(a-i-j/4,d-j-i/4);//ad,haishengduoshao
			if((mid-i-j)/2<need){
				continue;
			}
			int now=mid-i-j;//haishengduoshaokaofenpei
			int bneed=max(0,b-i/2-j/2);
			int cneed=max(0,c-i/2-j/2);
			int bb=max(0,(bneed-now/4)*4/3);//gusuanqujian
			for(int k=max(0,bb-5);k<=min(now,bb+5);k++){//meijub
				if(k+(now-k)/4>=bneed&&k/4+now-k>=cneed){
			        return true;
				}
			}
		}
	}
	return false;
}
int main(){
	//freopen("lighting.in","r",stdin);
	//freopen("lighting.out","w",stdout);
	scanf("%d%d%d%d",&a,&b,&c,&d);
	int l=0,r=a+b+c+d,ans=a+b+c+d;
	while(l<=r){
		int mid=(l+r)>>1;
		if(check(mid)){
			ans=mid;
			r=mid-1;
		}
		else{
			l=mid+1;
		}
	}
	cout<<ans;
	//fclose(stdin);
	//fclose(stdout);
	return 0;
}

T2

题面

蚂蚁上树

时间限制:2秒        内存限制:256M

题目描述

小可很喜欢让别人爬, 于是他种了一棵树。然后在树上放了 n 只蚂蚁,看它们爬。

树上有 n 个点,编号是从 1 ~ n,令 1 号点为根节点,每个点上都放了一只蚂蚁。

除了1号点的蚂蚁以外,其他的蚂蚁都可以爬到它的父节点(当然也可以不爬,且只能爬一次)。

众所周知,蚂蚁是群居生物,每只蚂蚁都有一个快乐值 a​i​​,如果某个节点只有一只蚂蚁的话,这个快乐值是不会起作用的,如果某个节点上有多只蚂蚁,它们的快乐值就会起作用,快乐值为当前节点上的蚂蚁的异或值, 问所 有方案的所有快乐值之和。

输入描述

第一行:输入一个正整数 n,表示树的点数 。

第二行:输入 n 个数,表示每个蚂蚁的快乐值。

第三行:输入 n-1 个数字,表示 2~n 号节点的父节点编号。

输出描述

输出一个答案对10e9+7取模后的结果。

输入样例

  1. 3
  2. 1 2 4
  3. 1 2

输出样例

  1. 12

样例解释

树呈一条链,二号结点的父亲是一号结点,三号结点的父亲是二号结点。1,2,3 号结点蚂蚁的属性值分别为 1、2、4。这些蚂蚁共有 4 种爬的方案:

方案 1:[{1,2},{ },{4}] 表示第一个结点有属性值为 1,2 的两只蚂蚁,第二个结点无蚂蚁,第三个结点有属性值为 4 的一只蚂蚁。本方案的快乐值为 1 ⊕ 2 = 3,其中 ⊕ 表示异或。

方案 2:[{1,2},{4},{ }],本方案的快乐值为 3。

方案 3:[{1},{2},{4}],没有结点上有大于等于两只蚂蚁,所以快乐值为 0。

方案 4:[{1},{2,4},{ }],快乐值为 2 ⊕ 4 = 6 所有方案的快乐值之和为 3 + 3 + 0 + 6 = 12。

输入样例

  1. 3
  2. 1 2 2
  3. 1 1

输出样例

  1. 7

样例解释

方案 1:[{1},{2},{2}]

方案 2:[{1,2},{2},{ }]

方案 3:[{1,2},{ },{2}]

方案 4:[{1,2,2},{ },{ }]

所有方案的快乐值之和为 0 + 3 + 3 + 1 = 7

数据描述

本题共10个测试点:

测试点1:1≤n≤20

测试点2~3:1≤n≤1000

测试点4:树是一条链

测试点5~6:树是二叉树

测试点7~10:树形态没有特殊限制,1≤n≤10​5​​,0≤a​i​​≤10​9​​

T2simple

解题思路

10分部分

对每个节点上移或否的状态进行二进制枚举,O(2^n)的时间复杂度

code

if(n<=20){
		for(int i=0;i<=pt[n-1]-1;i++){
			int tm=i;
			cnt=0;
			for(int j=0;j<=21;j++){
				p[j]=0;
				ans[j]=0;
				fl[j]=0;
			}
			fl[1]=1;
			ans[1]=a[1];
			//cout<<"biaozhi  ";
			for(int j=2;j<=n;j++){
				if(tm&1){
					p[j]=1;
				}
				else{
					p[j]=0;
				}
				//cout<<p[j]<<' ';
				tm=(tm>>1);
			}
		//	cout<<endl;
			for(int j=2;j<=n;j++){
				if(p[j]==1){
					fl[f[j]]++;
					//cout<<"p "<<p[j]<<' '<<fl[f[j]]<<endl;
					ans[f[j]]=ans[f[j]]^a[j];
				}
				else{
					fl[j]++;
					//cout<<"p "<<p[j]<<' '<<fl[j]<<endl;
					ans[j]=ans[j]^a[j];
				}
			}
			for(int j=1;j<=n;j++){
				//cout<<fl[j]<<endl;
				if(fl[j]>1){
					cnt+=ans[j];
				}
			}
			//cout<<"cnt "<<cnt<<endl;
			tcnt+=cnt;
		}
	}

100分部分

对于每一个二进制位,对其1,0状态及其子节点上移个数和01状态分类讨论,并同时对其他剩余节点进行排列组合计算答案数量,与答案相乘后相加即可

code

void dfs(int pos){
	if(v[pos].size()==0){
		return;
	}
	tcnt+=a[pos]^a[v[pos][0]];
	tcnt+=a[pos]^a[v[pos][1]];
	dfs(v[pos][0]);
	dfs(v[pos][1]);
}
void(int u){
	memset(num,0,sizeof num);
	int tot=1;
	split(a[u],num);
	for(int v:e[u]){
		dfs(v);
		split(a[v],num);
		tot++;
	}
    if(tot==1)
        return;
    long long temp,r=two[tot-1],p=two[n-tot];
    if(u==1){
        r=two[tot-2];
    if(u!=1)
        p=two[n-tot-1];
    for(int i=0;i<30;i++){
    	if(num[i]){
    		temp=(1<<i);
    		long long xi=r;
    		if(u==1&&((a[u]>>i)&1)&&(num[i]==1))
    			xi=(xi*2)%mod;
    		ans=(ans+temp*xi%mod+mod)%mod;	
		}
		
	}
	ans=(ans+a[u]*p%mod+mod)%mod;
	if(u!=1)
		for(int v:e[u])
			ans=(ans-a[v]*p%mod+mod)%mod;	
}

T3

题面

AB串

时间限制:1秒        内存限制:512M

题目描述

你有 n 个字母 A,m 个字母 B,你可以将这些字母组成成为一个字符串,你需 要使得这个字符串的权值尽量大。现在我们以如下规则计算这个字符串的权值。

1.每有连续的 a 个 A,且下一个字母依旧是 A,则权值 +1。假设 a= 3,且 连续有 7 个 A,那么根据此规则,权值 +2。你可以理解一段长度为 cntA 的 A 所获得的权值为⌊​a​​cntA−1​​⌋,
2.每有连续的 b 个 B ,且下一个字母依旧是 B,则权值 +1。
3.上一个字母和当前字母不一样时,权值 +1。(第一个字母前面没有字母,也 会使得权值 +1,详见样例 1)

假设当前字母是 B,则至少需要有连续 c 个字母 B,下一个字母才可以切换成 A。字母 A 切换到字母 B 没有任何限制。

请问你能构造的字符串权值最大可能是多少?

输入描述

第一行输入一个正整数 t ,表示测试样例组数。

接下来 t 行:每行输入五个正整数 n,m,a,b,c,表述如题。

输出描述

对于每一组输入数据,输出一行一个正整数表示答案。

输入样例

  1. 6
  2. 1 1 1 1 1
  3. 5 4 3 3 2
  4. 5 5 3 3 2
  5. 3 9 3 3 3
  6. 7 3 3 5 8
  7. 4 7 2 8 5

输出样例

  1. 2
  2. 5
  3. 6
  4. 8
  5. 4
  6. 5

样例说明

样例 1 可以设计为 AB 或者 BA,第一个字母前面没有字母,初始使得权值为 1,第二个字母和第一个不一样,权值 +1,总权值为 2。

样例 2 可以设计为 ABBAAAABB,也可以设计成 ABBAAABBA,权值均为 5。

对于样例 3,可以设计为 ABBABBAAAB,权值为 6。

对于样例 4,可以设计为 ABBBBABBBBAB,权值为 8。

对于样例 5,可以设计为 AAAAAAABBB,权值为 4。

对于样例 6,可以设计为 AAABBBBBABB 。

数据描述

对于20%的数据:1≤t≤5,1≤n,m,a,b,c≤10

对于50%的数据:1≤t≤50,1≤n,m,a,b,c≤100

对于100%的数据:1≤t≤50,1≤n,m,a,b,c≤10​5​​

T3simple

实在会不了一点,先放个代码

code

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#include <ext/rope>
#include <iostream>
#include <map>
#include <queue>
#include <random>
#include <set>
#include <stack>
#include <vector>
#define CLOSE ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
#define isd(c) ('0' <= (c) && (c) <= '9')
#define isa(c) ('a' <= (c) && (c) <= 'z')
#define isA(c) ('A' <= (c) && (c) <= 'Z')
#define mem(a, b) memset(a, b, sizeof a);
#define N 100005
#define M 2000005
#define mod 1000000007
#define inf 0x3f3f3f3f
#define infll 0x3f3f3f3f3f3f3f3f
#define ll long long
#define ull unsigned long long
#define PI acos(-1)
#define endl "\n"
#define pii pair<int, int>
#define F first
#define S second
#define bug cout << endl << " .....here!...." << endl;
//#pragma GCC optimize("O3")
using namespace std;
using namespace __gnu_cxx;
using namespace __gnu_pbds;
int main()
{
    CLOSE
    int t;
    cin >> t;
    // bbbbabbbba
    while (t--)
    {
        int n, m, a, b, c;
        cin >> n >> m >> a >> b >> c;
        int maxn = 0;
        for (int i = 0; i <= min(n, m / c); i++)
        {
            int sum = 0;
            if (i == 0)
            {
                sum++;
                sum += (n - 1) / a;
                sum += (m - 1) / b;
            }
            else
            {
                sum++;
                sum += (i - 1) * 2;
                if (n - i >= 1)
                {
                    sum++;
                    int x = n - i - 1;
                    sum += x / a;
                }

                if (c > b)
                    sum += (c - 1) / b * i;

                if (m - c * i >= 1)
                {
                    sum++;

                    int x = m - i * c - 1;
                    int md = (((c - 1) / b + 1) * b) - (c - 1);
                    if (x / md >= i)
                        sum += i, x -= md * i;
                    else
                    {
                        sum += x / md;
                        x -= x / md * md;
                    }
                    sum += x / b;
                }
            }
            // cout << i << " " << sum << endl;
            maxn = max(maxn, sum);
        }
        cout << maxn + 1 << endl;
    }

    return 0;
}

解题思路

T4

题面

奇怪的函数

时间限制:3秒        内存限制:256M

题目描述

小可有一个奇怪的函数 F(x), 这个函数的输入参数是一个正整数 x ,为了得到这个函数的运算结果,这个函数需要依次进行 n 个步骤,每个步骤是如下三种形式之一:

1.x+=val​i​​
2.x=min(x,val​i​​)
3.x=max(x,val​i​​)

依次执行完这 n 个步骤之后,这个函数就可以安心输出答案了。

现在,小可得到了这个函数,他想简化这个函数,确切的来说,他有 q 个问题,每个问题要么是修改这个函数的某一个步骤,要么给定一个 x ,询问当前F(x) 的值,请帮助他完成这个过程。

输入描述

第一行:输入一个正整数 n ,表示这个函数的步骤数量。

接下来 n 行:每行输入两个正整数opt val opt表示这是第几种操作,val表示这一次操作对应的权值。

接下来一行:输入一个正整数 q ,表示问题的个数。

接下来 q 行:每行输入一个问题。形式为如下的四个操作之一。

1 pos val 表示把第 pos 个步骤改成 x+=val

2 pos val 表示把第 pos 个步骤改成 x=min(x,val)

3 pos val 表示把第 pos 个步骤改成 x=max(x,val)

4 x 表示询问,此时 F(x) 是多少。

输出描述

对于每一个操作4的查询,输出一行一个数字表示答案。

输入样例

  1. 10
  2. 1 48
  3. 1 50
  4. 1 180
  5. 2 957
  6. 1 103
  7. 1 100
  8. 1 123
  9. 3 500
  10. 1 66
  11. 1 70
  12. 3
  13. 4 20
  14. 4 50
  15. 4 700

输出样例

  1. 760
  2. 790
  3. 1419

数据描述

对于100%的数据:1≤n,q≤3∗10​5​​,操作 2,3,4 涉及的 val 在[1,108][1,10​8​​] 之间,所有加法操作涉及的 val 在[1,200][1,200] 之间。

解题思路

5分部分

暴力枚举即可

7~11测试点部分

由于该函数再min断点之间是单调递增的,所以可以二分查找答案

code

真不巧,代码走丢了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值