0.导读
开坑,记录下做过的题。只会签到捏。
题解不完全,不会,先开个坑,兴许寒假打完5场比赛填坑
1.2022SWJTU月赛
M.发工资
思想:贪心地将每个元素修改为平均数,代价传给后继元素。若元素为平均数,(因前驱的修改变为了平均数,无需修改了)
int solve()
{
int ans=0;
for(int i=0;i<n;i++) a[i]-=avr;
for(int i=0;i<n;i++)
{
if(a[i])
{
a[i+1]+=a[i];
ans++;
}
}
return ans;
}
C.积水问题
-
法一.贪心分治
找出被两个极大值包裹的区间,区间的权易求
左右区间重复如上分治操作
-
法二.排序加速
用高度的降序排列p作为辅助,维护一个t的已solve的lr区间,每次迭代入一个次大高度,拓展lr区间
int solve()
{
p[0].id=0,p[n+1].id=n+1;//两侧用0包裹
sort(p,p+1+n+1);
int l,r;
//lr最高柱重合
l=r=p[0].id;
//每次ask一个较大的数,然后判断这个数的id在那个区间,然后这段区间易求权
//0.lr维护的是已经ask过的区间
//易证内嵌for总共只会遍历一边数组,所以时间复杂度是O(2n)
for(ri i=1;i<=n+1;i++)
{
if(p[i].id>r)
{
int top=min(p[i].val,t[r]);
for(int j=r+1;j<p[i].id;j++) ans+=top-t[j];
r=p[i].id;
}
if(p[i].id<l)
{
int top=min(p[i].val,t[l]);
for(int j=p[i].id+1;j<l;j++) ans+=top-t[j];
l=p[i].id;
}
}
return ans;
}
J.alwaysRank
题面
在二维平面里有n个得分点(xi,yi),\{R^2\}内任取一点(i,j),其得分为到这些得分点的曼哈顿距离之和。求得分最少的点的数目
曼哈顿距离定义:Manhattan(p1,p2)=|x_1-x_2|\ +\ |y_1-y_2|
解析
曼哈顿距离是的x轴与y轴是离散化的,
考查一维数轴上放置n个得分点——
若n为奇数,有且仅有中位数那个点得分最少,得分为Σx_r-x_l,l+r=n
若n为偶数,中位数区间的所有点都是得分最少的点
考查二位数轴上的积分点,考查x轴上积分最少的点与y轴上积分最少的点,显然是col*row;
int solve()
{
int row=0,cow=0;
sort(x+1,x+1+n);
sort(y+1,y+1+n);
if(n%2) row=1,col=1;
else row=x[n/2+1]-x[n/2]+1,col=y[n/2+1]-y[n/2]+1;
returncol*row;
}
寒假打完比赛回来填坑……
2.新秀杯初赛
A. 最长括号匹配
Description
给定一个只含有'('和')'字符的字符串,长度为 n(1≤n≤10^3)
。求最长有效括号子串(格式正确且连续)的长度。
包括形如(()),()()
Solution
以每一个(为可能的字串起点,往后检查匹配长度,记录(个数ls和)个数rs,故当rs>ls时这是一段非法的字串,当ls>rs时可以往后搜索,当ls==rs是一段可行的字串,需要记录答案然后继续往后搜索
Code
#include<bits/stdc++.h>
using namespace std;
string s;
int ans=0;
int main()
{
cin>>s;
for(int i=0;i<s.size();i++)
{
if(s[i]=='(')
{
int ls=0,rs=0;
for(int j=i;j<s.size();j++)
{
if(s[j]=='(') ls++;
else rs++;
if(ls==rs)
{
ans=max(ans,j-i+1);
}
if(rs>ls)
{
i=j-1;
break;
}
}
}
}
cout<<ans;
return 0;
}
O(n)解法
D.救救qq
水题不贴
E.帮帮小冷
Description
小冷有一个大小为n的数组,小冷不喜欢很大的数,他想尽可能使数组的和尽可能小,现在只有一种使数组变小的办法,若数字c为数组中每一个数的因数的话,那么便可以数组中的所有数都除以c,小冷想知道最小的数组的和,你能帮帮他吗qwq。
Solution 区间gcd问题,可以用ST表 数据太弱只有一个查询,On跑了,水题
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5,M=1e4+5;
long long a[N];
int n;
long long ans=0;
long long by[N];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>n;
for(int i=0;i<n;i++) cin>>a[i];
by[0]=a[0];
for(int i=1;i<n;i++)
{
//cout<<by[i-1]<<"\n";
by[i]=__gcd(by[i-1],a[i]);
}
for(int i=0;i<n;i++) ans+=a[i]/by[n-1];
cout<<ans;
return 0;
}
I.西南胶带也是胶带啊
Description
在由于XCPC主办方多次将SWJTU和XAJTU的物资发错,两校ACM校队达成协议:将错就错。
现在XAJTU和SWJTU都有不超过26支队伍。其中XAJTU有Au1支队伍能拿金牌,Ag1支队伍能拿银牌,Cu1支队伍能拿铜牌;而SWJTU有Au2支队伍能拿金牌,Ag2支队伍能拿银牌,Cu2支队伍能拿铜牌。
两校如果拿到自己学校队伍的物资,则令对应的队伍参赛,在剩余的队伍中,未拿到自己物资的队伍由强到弱给按物资顺序拿对方物资,给对方代打
假设每支队伍都会正常发挥,获取到其校内排名对应的奖牌。如果SWJTU的获得金牌数量比原本能获得的多,那么宋老板会很开心,如果获得金牌的数量不变,而获得银牌的数量变多了,宋老板也会很开心,实在不行铜牌数量增多了宋老板也会很开心!
如果宋老板很开心,他就会请大家在星期四吃麦,请问大家有没有机会在周四吃到麦。
Solution
小模拟,题面写的真烂
Code
#include<bits/stdc++.h>
using namespace std;
int au1,ag1,cu1,au2,ag2,cu2;
string xat,swt;
char Upper[26];
int len=0;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>au1>>ag1>>cu1>>au2>>ag2>>cu2;
cin>>xat>>swt;
int cnt=0,nau=0,nag=0,ncu=0;
int au=0,ag=0,cu=0;
int oau=0,oag=0,ocu=0;
for(int i=0;i<xat.size();i++)
{
//仙交摸到稀胶
if(xat[i]>='a' && xat[i]<='z')
{
cnt++;
//稀胶自己打,o
if(xat[i]-'a'<=au2) oau++;
else if(xat[i]-'a'+1>au2 && xat[i]-'a'+1<=au2+ag2) oag++;
else if(cnt<=au2+ag2+cu2)ocu++;
//仙交代打 ,n
if(cnt<=au1) nau++;
else if(cnt>au1 && cnt<=au1+ag1) nag++;
else if(cnt<=au1+ag1+cu1) ncu++;
}
}
if(nau>oau || nag>oag || ncu>ocu) cout<<"Yeah!";
else cout<<"No!";
return 0;
}
J.最强的对手!至大的危机!
Description
与蕈兽结下坚定的羁绊!经历多场激烈角逐后,旅行者和派蒙终于闯进了蕈兽大作战“月莲杯”的决赛!他们将继承八重神子和莱依拉的信念,挑战最强的驯兽师海妮耶,并揭穿隐藏在“月莲杯”大赛之下的阴谋!
但在此之前,派蒙决定先与蕈兽伙伴们进行元素反应特训...
由于派蒙不想计算元素反应中的元素附着保留概率,所以她将元素反应机制略微改变了,改变后的元素反应特训将以如下规则展开:
已知,每种蕈兽都有一种元素属性:
Bongo-Head(咚咚小圆帽)为Hydro(水元素)的蕈兽,Twirly-Whirly(转转悠悠兽)为Anemo(风元素)的蕈兽,Blitzara(百雷遮罗)为Electro(雷元素)的蕈兽。
当蕈兽释放元素战技,木桩会被附着上对应的元素,若木桩上早已有元素附着,将会产生元素反应,并且将木桩上的元素附着更改为当前元素。需要注意的是,风元素不会附着于木桩。元素反应如下所示:
感电 Electro-Charged :水元素-Hydro+雷元素-Electro
扩散 Swirl : 风元素-Anemo+雷元素-Electro/水元素-Hydro
在元素反应特训中,派蒙将蕈兽伙伴们进行编队,分别放在一号位,二号位和三号位上。接下来,她会发出一连串仅含'1','2','3'的指令,指示位于该号位上的蕈兽对木桩释放元素战技!
而你,旅行者,将作为特训中的木桩!请你精准的、按顺序的输出每次元素反应的类型。若特训中未产生元素反应,请你输出"hehe"(不含双引号)。
注:请遵循题面词汇的大小写格式。
Input
每个样例包含多组测试数据,第一行输入一个整数 T,代表该组样例中测试数据的数量。
对于每组测试数据,第一行包含三个字符串,中间以空格隔开,代表蕈兽的编队顺序。
对于每组测试数据,第二行包含一个长度不小于1的,仅含阿拉伯数字'1','2','3'的字符串。
Output
对于每组测试数据,按顺序输出元素反应(以英文词汇输出),中间以空格隔开。若无元素反应,请输出“hehe” (不含双引号)。
请在每组测试数据的输出之后添加一个换行符。
Solution
模拟,挺好的,就是不懂叩
,读了很久题面。感电是加法不区分两种元素的先后,扩散会使木桩失去附着元素
用map<string,char>映射指令顺序op为打出的元素顺序,再遍历元素顺序模拟就好了
Code
#include<bits/stdc++.h>
using namespace std;
map<string,char> F;
int T;
string rct[3]={"Swirl","Electro-Charged","hehe"};
void solve(string op)
{
bool flag=1;
char state='0';
if(op[0]!='2') state=op[0];
for(int i=1;i<op.size();i++)
{
// cout<<"ops:"<<state<<' '<<op[i]<<"\n";
if(state!='0')
{
//扩散后没有元素
if(op[i]=='2' && (state=='1' || state=='3'))
{
cout<<rct[0]<<' ';
flag=0;
state='0';
}
//感电还有元素
if((op[i]=='3' && state=='1') || (op[i]=='1' && state=='3'))
{
cout<<rct[1]<<" ";flag=0;
//state='0';
}
if(op[i]!='2' && state!='0') state=op[i];
//cout<<"newstate:"<<state<<"\n";
}
else if(op[i]!='2') state=op[i];
}
if(flag) cout<<rct[2];
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
F["Bongo-Head"]='1';
F["Twirly-Whirly"]='2';
F["Blitzara"]='3';
cin>>T;
while(T--)
{
string a[4],op;cin>>a[1]>>a[2]>>a[3]>>op;
for(int i=0;i<op.size();i++)
{
op[i]=F[a[op[i]-'0']];
}
// cout<<"op:"<<op<<'\n';
solve(op);cout<<'\n';
}
return 0;
}
K.超级炸弹人
Description
炸弹人是一款经典的小游戏。在超级炸弹人中,宋老板的魔法让炸弹得到了加强,在一个二维网格地图上放置的任意一个炸弹将在 t秒后爆炸,并瞬间杀死同行同列的所有玩家(炸弹可以与玩家重叠)。
林老师操控的炸弹人在网格中,一秒只能往上下左右移动到相邻的网格,请问在第 0 秒最少放置多少枚炸弹(可放置在任意位置),才能保证杀死林老师。
solution
不加证明地给出,在tony_Lin的逃生策略和炸弹的放置策略均为最优下,tony_Lin需要一直往对角线跑。其在ts内活动范围是一个径长2t+1的正棱形,覆盖这个棱形需要在对角线放满炸弹ANS:
cout<<(t/2)*2+1;
G.仙人掌攻击
Description
给出一段序列a(1<=n<=40),并有k次query给出hp,每次求其组合出∑select=hp的方案数
Solution
先给出自顶向下减而治之的递归
先升序排序便于考查
在考虑a[it]这株
下,有两种状态:选取it,考查考虑前it-1株仙人掌下组合出hp-a[it]的方案数和不选取这个仙人掌,考查考虑前it-1株仙人掌下组合出hp的方案数
//sort(a+1,a+n+1);
ll dfs(ll hp,ll it)
{
if(hp==0 || a[it]-hp==0) return 1;
if(sum[it]<hp || !it) return 0;
if(it>n ) return 0;
return dfs(hp-a[it],it-1) + dfs(hp,it-1);
}
在一冰老师的测试用例中,所有n和k都是拉满的,且永远无法破宋老板的防,即一般情况下会跑满dfs时间复杂度O(2^n),n=40
所以加个前缀和剪枝就能0ms过
T-Solution:分治
by赵老板:所以可以将40个数据分为两组,每组20个,枚举每组组内的结果并记录(此时需要枚举220≈106次),分别排序后,一组从小到大,另一组从大到小枚举,即可完成次数统计。也可以使用map、手撸二叉堆等方式实现查找。时间复杂度为O((2(n/2)*log(2(n/2)))。
我用map<ll,ll>mapr
映射全组合中各个atk的方案数。
Code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 45;
int n, m;
int a[N];
ll lhs[ (1 << 20) + 5], rhs[(1 << 20) + 5];
map<ll, ll> mapl, mapr;
int lenl = 0, lenr = 0;
void dfsl(ll atk, int lo, int hi)
{
if (lo == hi)
{
return;
}
for (int i = lo + 1; i <= hi; i++)
{
lhs[lenl++] = atk + a[i];
mapl[atk + a[i]]++;
dfsl(atk + a[i], i, hi);
}
}
void dfsr(ll atk, int lo, int hi)
{
if (lo == hi)
{
return;
}
for (int i = lo + 1; i <= hi; i++)
{
rhs[lenr++] = atk + a[i];
mapr[atk + a[i]]++;
dfsr(atk + a[i], i, hi);
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> a[i];
sort(a + 1, a + n + 1);
for (int i = 1; i <= n / 2; i++)
{
lhs[lenl++] = a[i];
mapl[a[i]]++;
dfsl(a[i], i, n / 2);
}
for (int i = n / 2 + 1; i <= n; i++)
{
rhs[lenr++] = a[i];
mapr[a[i]]++;
dfsr(a[i], i, n);
}
for (int k = 0; k < m; k++)
{
ll hp;
cin >> hp;
ll ans = 0;
for (int i = 0; i < lenl; i++)
{
if (lhs[i] == hp) ans++;
else
ans += mapr[hp - lhs[i]];//用映射加速右边的查询
}
for (int j = 0; j < lenr; j++)
{
if (rhs[j] == hp) ans++;
}
if (hp == 0) ans++;
cout << ans << '\n';
}
return 0;
}
H.宋老板与炒房
Awesome 二分答案...
Description
宋老板炒资金赔钱了,他决定把钱投资到房产市场上炒房子。
现在市场上有N(1<N<2e5)套房子,第i套房子现价为ai,3年后的预期价格为bi。(1<b<3a<1e9)
宋老板想投资其中的M套房子,宋老板很有钱,但是想让这些钱购买的房子的 ∑3年后预期价格/∑现价 最大,请你帮助宋老板计算该比例的最大值。
因为宋老板很聪明,所以这些房子的3年后预期价格都会超过现价的,但是因为宏观调控,3年后预期价格不超过现价的3倍
输出一个浮点数,要求保留小数点后4位,表示赚到钱最大的比例
Solution
典型错解是按涨价倍率排序,因为有些房子赔率高,但价值低,最终贡献一般
考查知识点:二分答案
对于答案空间有限的题,可以通过枚举答案检查其是否可行,最终找到最优答案。而这种枚举可以二分优化
本题答案空间是N=[1:10-4:3],枚举粒度为10-4二分枚举效率为O(long(n)),
枚举元素check方案:对于所枚举的倍率p,每套房子现价为a,预期价格为b。其贡献价值为b-ap。
最优解一定可行,但可行解不一定最优。我们假设整个解序列具有单调性,且一个数x为可行解,那么一般的,所有的x'(x'<x)都是可行解。并且,如果有一个数y是非法解,那么一般的,所有的y'(y'>y)都是非法解。
在这里,若sum>0,在p这个选择方案下的解都大于这个p,最优解一定大于这个p,p一定是非法解,搜索p右边的解
若sum<=0,我们的选择方案下的解<=p,搜索p左边的解
Code
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5,M=2e5+5;
int a[N],b[N];
int n,m;
const double er = 1e-4;
double c[N];
struct node
{
double val;
int id;
};
bool cmp(const node& lhs,const node& rhs)
{
return lhs.val>rhs.val;
}
double suma=0,sumb=0;
void binary (double l,double r)
{
vector<node> c(n);
//左闭右闭搜搜
while(r-l>er)
{
double p=(l+r)/2;
// cout<<p<<" ";
//ap-b的意义:这套房子对倍率p的贡献,ap-b>0说明这套房子的倍率小于p,反之大于p
//故优先选取ap-b低的房子,若组合出的sum>=0,说明倍率p难以达成,ans<=p
//反之倍率p可达成,向右搜索更有参考意义的p
//ans是小于二分答案p的最大值
for(int i=0;i<n;i++)
{
c[i].val=b[i]-a[i]*p;
c[i].id=i;
// cout<<c[i].val<<" ";
}
sort(c.begin(),c.end(),cmp);
double sum=0;
for(int i=0;i<m;i++) sum+=c[i].val;
// cout<<sum<<" ";
if(sum<=0)
{
suma=0,sumb=0;
for(int i=0;i<m;i++) suma+=a[c[i].id],sumb+=b[c[i].id];
r=p;
}
else
{
l=p;
}
// cout<<'\n';
}
}
int main()
{
ios::sync_with_stdio(false);
cin>>n>>m;
for(int i=0;i<n;i++) cin>>a[i];
for(int i=0;i<n;i++) cin>>b[i];
binary(1,3+er);
printf("%.4f",sumb/suma);
return 0;
}
看看ap-b分布:
可知按ap-b排列不等于按b/a排列
新秀杯决赛
Awesome//(ㄒoㄒ)/~~
A.装修
Description
给定矩形size为N(5e3)和M(1e5)次操作,每次为一个矩形区域Area(a,b,c,d)加上一个数h,最后查询矩形内最大值和最大值出现次数。
Solution:二维差分:将矩阵a按行分块,每次矩形加法拆解为(c-a)个行加法,在每一个行向量左端加上差分,右端减去差分即可,进一步,每一次操作都是对同一列的位置作差分,可以再优化出二维差分。
矩阵a的差分策略:
//按行对矩阵a分块,对于区间操作plus(a,b,c,d,h),将左列的del加h,将右列的del-h,,做列前驱差分。前缀和反解a策略为左元素加此差分
for(int i=a;i<=c;i++)
{
del[i][b]+=h;
del[i][d+1]-=h;
}
a[i][j]=a[i][j-1]+del[i][j];
del的差分策略
//对一列del+h的差分策略:上端点+h,下端点-h
ddel[a][b]+=h;
ddel[c+1][b]-=h;
ddel[a][d+1]-=h;
ddel[c+1][d+1]+=h;
del[i][j]=del[i-1][j]+ddel[i][j]
至次算法时间复杂度为O(m + n^2 ),可过
考查空间复杂度,开了三个N^2 数组,O(3n^2)=3*(5e3)^2*4/2^6=286MB
考查优化1:用动态数组管理ddel,del,和a,同一时间只需要两个数组
会tle的,因为尽管是O(n^2)=2.5e7级别,内存管理仍是很大的时间开销,会t
优化策略:del可以原地求前缀和
考查数据范围:a[i].maxn=1e5*1e5=1e10,大于int
与unsigned int
在windows下sizeof(long)=4
,就是int啦,所以要开long long
考查同时开long long a[N][N]
与int p[N][N]
,空间复杂度O(2n^2)=1*(5e3)^2*4+1*(5e3)^2/2^6=286MB
最终内存管理策略:每用一行del开始构造一行a,malloc一行a,每构造完毕一行a,free一行del (malloc比new快一丢丢?
AC代码资源消耗:
Time | Memory |
---|---|
646ms(主要消耗为n^2下malloc) | 194MB |
Code
#include<iostream>
#include<stdlib.h>
using namespace std;
const int N=5005,M=100005;
typedef long long ll;
int n,m;
ll ans=0;
int mey;
int* del[N];
ll* a[N];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>n>>m;
for(int i=0;i<=n+1;i++)
{
del[i]=(int*)malloc(sizeof(int)*(n+2));
for(int j=0;j<=n+1;j++) del[i][j]=0;
}
while(m--)
{
int a,b,c,d,h;
cin>>a>>b>>c>>d>>h;
del[a][b]+=h;
del[c+1][b]-=h;
del[a][d+1]-=h;
del[c+1][d+1]+=h;
}
//不懂,原地求前缀和吧
for(int j=1;j<=n;j++)
{
for(int i=1;i<=n;i++)
{
del[i][j]+=del[i-1][j];
// cout<<del[i][j]<<" ";
}
// cout<<'\n';
}
// cout<<'\n';
for(int i=1;i<=n;i++)
{
a[i]=(ll*)malloc(sizeof(ll)*(n+1));
a[i][0]=0;
for(int j=1;j<=n;j++)
{
a[i][j]=a[i][j-1]+del[i][j];
// cout<<a[i][j]<<" ";
}
free(del[i]);
// cout<<'\n';
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(a[i][j]>ans)
{
ans=a[i][j];
mey=0;
}
if(a[i][j]==ans) mey++;
}
free(a[i]);
}
cout<<ans<<" "<<mey;
return 0;
}
可以指出,频繁的new与delete,对资源的消耗是灾难性的(在O(n^2)=2.5e7下tle),还会有碎片化,内存泄露的问题
Reference:
3.ptmalloc
C.幸运农场Ⅰ
Description
宋老板有一片n行m列大小的农场(n*m<1e5),里面种有若干白菜。如图所示,在农场的每一格至多只有一颗白菜
求同行或同列或同斜线的白菜的对数
Solution
有四个直线系:i=C,j=C,i+j=C,i-j=C
因为C因为可能是负,用四个map来记录这个各个线系的白菜的个数,ans为\sum线系的C_n^2
Code
签到题不贴
若数据强度是(n,m<1e5)呢? 胸牌哥说拿头做。O(n^2)的IO捏
G.宋老板与序列
Description
给定A=\{a_1,a_2,...,a_n\},B=\{b_1,b_2,...,b_n\}两条1-n的序列
当|a_{i}-a_{j}|=i∣或者|a_{i}-a_{j}|=j∣时,可将a_{i}与a_{j}交换
问:能否进行若干次交换(包括00次)将序列AA变为序列BB
Solution
不懂证明地给出,一定可完成变换
Code
cout<<"YES";
6 1 4 2 3 5 7 →6 1 4 2 7 5 3 2 1 4 6 3 5 7 1 4 5 6 3 2 7 3 4 2 1 5 6 7 4 3 2 1 5 6 7 1 2 3 4 5 6 7
将a元素置于i位置,他就可以与a+i或a-i或 a=b+j交换
应该,也许,总能恢复为自然序列
H.宋老板与字符串
Description
对一段字符串s循环操作如下:
for 1:strlen(s) reverse(s(1,i));
打印最后字符串
Solution
不加证明地给出,就是这个字符串从后往前隔项遍历,再从前往后隔项遍历
K.STTTTL
Description
请编程实现以下两个操作:
操作1:向可重集合种添加一个整数x,格式为:1 x
操作2:从小到大输出可重集合中恰好出现yy次的整数,格式为:2 y
若集合中没有恰好出现yy次的整数则输出-1
Solution