CSP-J模拟赛四李奕岑补题报告

CSP-J模拟赛四李奕岑补题报告

日期:2023-10-03 周二
学号:S10228

一:

总分数:170
T1 [复读机(repeater)]:100分
T2 [小可的矛与盾(spearshield)]:70分
T3 [不合法字符串(illegality)]:0分
T4 [虚假的珂朵莉树(kodori)]:0分

二:比赛过程

第一题比较顺利,经过几次调试,得到了满分。
第二题一看题就用前缀和,样例也对了,就是前缀和写错了,得了70分。
三四道题当时不会做,也不能骗分,就没有做。

三:题目分析

[复读机(repeater)]

1、题目大意

给定一个长度为n的仅包含小写字母和数字的字符串。
小写字母表示消息,数字表示复读次数。
字符串内可能包含多个数字,要从左到右解析字符串。

2、比赛中的思考

当时没想到用string,只想到了用char。
在输入的过程中判断是数字还是字母。
是字母就读入,是数字就复读。
最后加个特判并输出。

3、解题思路

我的想法是对的。
要注意复读是不能写x+=x。
因为这样会让每次复读的数据*2。

4、AC代码

#include<iostream>
#include<cstdio>
using namespace std;
char x,b[500005];
int main(){
	int t,n;
	cin>>t;
	while(t--){
		int cnt=0,q=0,flag=0;
		cin>>n;
		for(int i=1;i<=n;i++){
			cin>>x;
			if(x<='9'){
				flag=1;
				int l=x-'0';
				q=q*10+l;
			}
			else if(flag==1){
				flag=0;
				if(q==0){
					cnt=0;
				}
				int cnt1=cnt;
				for(int j=2;j<=q;j++){
					for(int k=1;k<=cnt1;k++){
						cnt++;
						b[cnt]=b[k];
					}
				}
				q=0;
			}
			if(x>='a'){
				cnt++;
				b[cnt]=x;
			}
		}
		if(flag==1){
			if(q==0){
				cnt=0;
			}
			int cnt1=cnt;
			for(int j=2;j<=q;j++){
				for(int k=1;k<=cnt1;k++){
					cnt++;
					b[cnt]=b[k];
				}
			}
		}
		for(int i=1;i<=cnt;i++){
			cout<<b[i];
		}
		cout<<endl;
	}
	return 0;
}

[小可的矛与盾(spearshield)]

1、题目大意

n个小可战士站成一排,每个小可都有一个战斗力i。
小可们有不同的分工,有的充当矛,有的充当盾。
盾是0,矛是1。
矛的攻击力和盾的防御力与小可本身的战斗力相同。
小可们被分成两个阵营,一个是1到pos,是矛阵营,
一个是pos+1到n,是盾阵营。
求矛阵营战斗力减盾阵营战斗力的绝对值最小的pos。

2、比赛中的思考

最开始是从前往后记录矛的战力和,从后往前记录盾的战力和。
最后算出pos比较大小输出。

3、解题思路

这道题巨水。
这道题要用前缀和,用前缀和算出矛和盾的战力值,而不是像我一样。

4、AC代码

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
char x[100005];
long long a[100005],b[100005];
int main(){
    int n;
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>x[i];
        if(x[i]=='0'){
            a[i]=a[i-1]+i;
            b[i]=b[i-1];
        }
        else{
            b[i]=b[i-1]+i;
            a[i]=a[i-1];
        }
    }
    long long minn=5190909090;
    for(int i=1;i<=n;i++){
        minn=min(minn,abs(a[i-1]-(b[n]-b[i-1])));
    }
    cout<<minn;
    return 0;
}

[不合法字符串(illegality)]

1、题目大意

给出若干个不合法的字符串s[i]和一篇小说str,
需要把str中的不合法字符用 ∗ * 和谐掉。
要用尽量少的 ∗ *

2、比赛中的思考

看懂了但是不会做。string的操作都忘了。

3、解题思路

在和谐字符的时候,应该把单词的最后一个和谐能保证最优。
判断时先枚举小说的每一个字符,
再枚举每一个单词是否能和谐掉这个字符。
最后输出。

4、AC代码

#include<iostream>
#include<string>
using namespace std;
string s[12],ss;
int main(){
	int t,n;
	cin>>t;
	while(t--){
		cin>>n;
		for(int i=1;i<=n;i++){
			cin>>s[i];
		}
		cin>>ss;
		int m=(int)ss.length();
		ss=" "+ss;
		for(int i=1;i<=m;i++){
			for(int j=1;j<=n;j++){
				if(i<s[j].length()){
					continue;
				}
				if(ss.substr(i-s[j].length()+1,s[j].length())==s[j]){
					ss[i]='*';
				}
			}
		}
		for(int i=1;i<=m;i++){
			cout<<ss[i];
		}
		cout<<endl;
	}
	return 0;
}

[虚假的珂朵莉树(kodori)]

1、题目大意

一棵树,有n个节点,根节点为1,每个节点都有一个权值。
在这棵树上增加m条虚假边(增加的虚假边不会影响节点深度)。
之后进行q次操作。
操作1:让结点 u 的权值增加 k ,并对与结点 u 相邻的结点中,深度比结点 k 小的结点重复操作1。
操作2:让结点 u 的权值增加 k ,并对与结点 u 相邻的结点中,深度比结点 u 大的结点重复操作2。
求经过q次操作后所有节点的权值。

2、比赛中的思考

样例都有点没看懂。

3、解题思路

在输入真实边之后立刻深搜确定每一个节点的深度,
然后输入虚假边。
用延迟更新的方式计算出操作一和操作二应该增加的权值,
最后判断条件更新。
最后输出。

4、AC代码

#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
#define pr pair<int,int>
#define mk make_pair
using namespace std;
const long long p=1e9+7;
const int N=1000010;
const int M=10*N;
vector<pr>g;
long long a[N],up[N],down[N];
int n,m,q,d[N];
int head[N],Next[N],ver[M],tot=-1;
void ADD(int x,int y){
    ver[++tot]=y;
    Next[tot]=head[x];
    head[x]=tot;
}
void dfs(int x,int fa){//记录并更新每一个节点深度
    g.push_back(mk(d[x],x));//将节点及其深度放入数组
    for(int i=head[x];~i;i=Next[i]){
        //遍历u的邻接表更新深度
        int y=ver[i];
        if(y==fa){
            continue;
        }
        d[y]=d[x]+1;//计算深度
        dfs(y,x);
    }
}
int main(){
    memset(head,-1,sizeof(head));
    cin>>n>>m>>q;
    for(int i=1;i<=n;i++){
        cin>>a[i];//输入点权
    }
    for(int i=1;i<n;i++){//输入实际边
        int x,y;
        cin>>x>>y;
        ADD(x,y);
        ADD(y,x);
    }
    dfs(1,1);
    for(int i=1;i<=m;i++){//输入虚拟边
        int x,y;
        cin>>x>>y;
        ADD(x,y);
        ADD(y,x);
    }
    for(int i=1;i<=q;i++){
        int op,u,v;
        cin>>op>>u>>v;
        if(op==1){//延迟更新,记录深度比u小的应该增加的权值
            up[u]=(up[u]+v)%p;
        }
        else{//记录深度比u大的
            down[u]=(down[u]+v)%p;
        }
    }
    sort(g.begin(),g.end());//按照深度从小到大排,更新操作二应该增加的权值
    for(int i=0;i<g.size();i++){
        int x=g[i].second;
        for(int j=head[x];~j;j=Next[j]){
            //对于每一个节点,遍历它的邻接表
            //找到x的邻接点
            int y=ver[j];
            //如果y深度大于x,说明y是x的孩子,才进行操作二的更新
            //用前缀和更新要累加的权值
            if(d[y]>d[x]){
                down[y]=(down[y]+down[x])%p;
            }
        }
    }
    reverse(g.begin(),g.end());//按照深度从大到小排序,更新操作一增加的权值
    for(int i=0;i<g.size();i++){
        int x=g[i].second;//x是这个点的编号
        for(int j=head[x];~j;j=Next[j]){
            int y=ver[j];//找x的邻接点
            //如果y的深度小于x,说明y是x的父亲,才进行操作1的更新
            //同上
            if(d[y]<d[x]){
                up[y]=(up[y]+up[x])%p;
            }
        }
    }
    for(int i=1;i<=n;i++){
        cout<<(a[i]+up[i]+down[i])%p<<" ";
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值