日期:2023-10-03
学号:S12418
一:
总分数:40
T1【复读机(repeater)】:0
T2【小可的矛与盾(spearshield)】:40
T3【不合法字符串(illegality)】:0
T4【虚假的珂朵莉树(kodori)】:0
二、比赛过程
第一题低级错误
第二题不知道abs返回int
第三题时间超限
第四题和没写差不多(
讽刺的是,考试结束自己测的T1T2立马全100了
三、比赛分析
T1【复读机(repeater)】:
1、题目大意
有多组测试数据。
给定一个字符串,其中含有若干个数字,将数字之前的部分乘以数字后输出。
2、比赛中的思考
先读入数字,如果后面是别的字符就转,也可以当场转;
如果最后面没有数字,将该字符串后面接上最后的字符,输出。
很简单但是我写错了(
3、解题思路
同上。甚至连long long 都不用 直接暴力枚举就行
4、AC代码
#include<iostream>
#include<string>
#include<cstdio>
using namespace std;
int main(){
int t;
cin>>t;
while(t--){
int l;
cin>>l;
string a = "",b = "",c = "";
int number = 0;
cin>>a;
for(int i = 0;i<l;i++){
if(a[i]>='0'&&a[i]<='9'){
number = number*10+(a[i]-'0');
}
else{
if(number>1){
b+=c;
c = "";
string s = b;
for(int j = 1;j<number;j++){
b+=s;
}
}
number = 0;
c+=a[i];
}
}
if(number ==1) b+=c;
else if(number>1){
b+=c;
string s = b;
for(int j = 1;j<number;j++){
b+=s;
}
}
cout<<b<<endl;
}
return 0;
}
T2【小可的矛与盾(spearshield)】:
1、题目大意
n个人站一排 01串分别表示“当前人只能使用矛/盾”,且当前人的“力量” = 下标
让你找到一个点将这排人分两半,做到abs(前面,矛的总攻击力)-abs(后面,盾的总防御力)的值最小。
2、比赛中的思考
水。
太水了。
数据简单到离谱。
但是这题我还是没得满分。
为什么?我又没上过D2的课程我怎么知道abs返回值是int类型a.......
3、解题思路
先求出盾的总防御 再从前向后遍历字符串求出矛的总攻击(记得减去盾)最后取最小值即可
4、AC代码
#include<iostream>
#include<string>
#include<cmath>
#include<cstdio>
using namespace std;
long long n;
string s;
int main(){
cin>>n;
cin>>s;
s = "-" + s + "-";
long long w = 0,v = 0;
for(long long i = 0;i<n+1;i++){
if (s[i] == '1'){
v += i;
}
}
long long res = v;
for(int i = 0;i<n+1;i++){
if (s[i] == '0') w += i;
if (s[i] == '1') v -= i;
res = min(res, abs(w-v));
}
cout<<res;
return 0;
}
T3【不合法字符串(illegality)】:
1、题目大意
同上,有多组输入。
对于每一组数据:有n个字符串(可能是单个字符),ta们是“非法的”,现给定一个大字符串,这些“非法的”小字符串不能出现在大字符串中(对于长度>=2的非法字符串,在大字符串中出现一部分被认为是合法的。)
如果检测出来非法,用最少的*号替换掉。
最后输出经过替换的大字符串。
2、比赛中的思考
好好做了 但是没过
解法比较暴力 时间超了(但是为什么是0分?)
利用find函数,更改非法字符串最后一个字符。(字符越靠后,其他非法字符串含有这个字符的可能性越高)当然,代码很少,时间复杂度很高(O(n^3))
3、解题思路
最后一个字符的思路仍然有效。
方法如下:遍历过程中,查找以当前字符为结尾的“非法”字符串是否存在。(使用strcmp函数截取)
如果存在,则用 * 号替换掉;运用此方法,时间复杂度为O(n log n)
4、AC代码
T4【虚假的珂朵莉树(kodori)】:
1、题目大意
小可有一棵树,有 n 个节点,根节点为 1, 每个节点都有一个权值。
设每个节点与根节点距离是这个节点的深度。
小可会在这棵树上增加 m 条虚假边,任意一条虚假边不会和原来的树边或其他虚假边重合(增加的虚假边不影响节点深度)。
之后小可会进行 q 次操作:
操作1: 让结点 u 的权值增加 k ,并对与结点 u 相邻的结点中,深度比结点 k 小的结点重复操作1。 操作2:让结点 u 的权值增加 k ,并对与结点 u 相邻的结点中,深度比结点 u 大的结点重复操作2。
小可想知道,经过 q 次操作之后,所有的节点的权值是多少。
2、比赛中的思考
用链表建树的方法忘了,所以连暴力也没写出来(((
唉.....
(话说这个珂朵莉树是什么东西?)
3、解题思路
链表建珂朵莉树,通过空间换时间
每一次操作都立刻进行肯定是不行的,所以我们把所有的操作记录下来最后统一结算
深搜dfs开团,然后前缀和打辅助,最后状态表(操作1和操作2结算)打输出,结束战斗
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 M = 5000050;
const int N = 1000010;
vector<pr>g;
long long a[N],up[N],down[N];
int n,m,q,d[N];
int head[M],Next[M],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-1;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());//按照深度从小到大排,更新操作2应该增加的权值
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的孩子,才进行操作2的更新
//用前缀和更新要累加的权值
if(d[y]>d[x]){
down[y] = (down[y]+down[x])%p;
}
}
}
reverse(g.begin(),g.end());//按照深度从大到小排,更新操作1
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];
//如果y的深度<x,说明y是x的父亲
if(d[y]<d[x]){
up[y] = (up[y]+up[x])%p;
}
}
}
for(int i = 1;i<=n;i++){
cout<<(down[i]+up[i]+a[i])%p<<" ";
}
return 0;
}