周一:周赛
D
(真菜啊橙题都不会做,我真是杂鱼杂鱼)
错解
- 一开始想并查集记录每个字母在s中的下标,对每一次询问根据记录的下标改变字符,结果T了
- 忽略了最坏情况:字符串全是一个字符,下标要遍历n次,O(NQ),1e10
思路
- 要优化Q,要跳过:
- r->a->r 间接变换
- a->a
- 字符串里没有该字符
- 考虑维护26个字符更换的映射
间接变换:考虑先r->a,后a->r
- 前面的变换会影响后面的 比如r->a影响之后a->r变化的个数
- 但是后边的不会影响前面的,最后该是什么就是什么 比如最后a变:a->r,r变:r->a->r
F
思路
- 删数:sum-a[i]
- 变负数:sum-2*a[i]
- Bob肯定尽可能x个元素都用上才最优
- Alice操作是为了防止Bob在sum中减去更多的数,关注最大的几个数(要排序)中,Alice删去多少个数才使结果最优(更大)。
void solve(){
int n,x,k;
int a[N]={0};
cin>>n>>k>>x;
int sum=0;
forr(i,1,n){
cin>>a[i];sum+=a[i];
}
sort(a+1,a+n+1,[&](int aa,int bb){
return aa>bb;
});
int l=0,r=x;//初始:alice不删除 l,r是bob取负数的区间
forr(i,1,x)sum-=2*a[i];
int ans=sum;
while (l+1<=k)//l在循环里还会+1 要判断l+1和k
{
l++,r++;
sum=sum+a[l]-2*a[(r<=n?r:0)];
ans=max(ans,sum);
//cout<<ans<<endl;
}
cout<<ans<<endl;
}
G
打眼一看以为是dp,但是发现第i个任务可以完成,如果所有任务j<i至少完成过一次
,说明前面的状态影响后面的,有后效性。感觉直接想dp我的猪脑会过载。
思路:贪心
- k次用于:做任务首次+再做几遍之前任务
- 为了保证最优,剩下次数用来“再做几遍”需要最大 b [ i ] b[i] b[i]
- 由题意,“首次做任务”一定是从前往后的,所以1~n枚举:要做前几个任务?
#include <bits/stdc++.h>
#define forr(i,l,r) for(int i=l;i<=r;i++)
#define reforr(i,l,r) for(int i=r;i>=l;i--)
#define int long long
#define endl '\n'
using namespace std;
const int N=2e5+10;
void solve(){
int n,k;
cin>>n>>k;
vector<int>a(n+2),b(n+2);
forr(i,1,n)cin>>a[i];
forr(i,1,n)cin>>b[i];
int maxb=0,ans=0,sum=0;
forr(i,1,n){
if(i>k)break;
sum+=a[i];//首次
maxb=max(maxb,b[i]);//找最大b[i]
ans=max(ans,sum+maxb*(k-i));
}
cout<<ans<<endl;
}
signed main(){
ios::sync_with_stdio(false);
int t;
t=1;
cin>>t;
while (t--){
solve();
}
return 0;
}
C
思路:二分
- 因为数据范围过大,直接二分判断是第几小
- 判断“第几小”:对mid 前面有 m i d / n mid/n mid/n个n的倍数 m i d / m mid/m mid/m个m的倍数 m i d / n ⋅ m mid/n·m mid/n⋅m个nm的公倍数
- mid前边不能有n、m公倍数 所以第几小= m i d / n + m i d / m − 2 ⋅ m i d / n ⋅ m mid/n+mid/m-2·mid/{n·m} mid/n+mid/m−2⋅mid/n⋅m
int check(int mid){
int x=n*m/__gcd(n,m);
//int tmpk=mid/n+mid/m-2*mid/x;
int tmpk=mid/n+mid/m-mid/x*2;//mid/x是既能除n又能除m的 注意除的是 lcm(n,m)=n*m/__gcd(n,m)
/*先除再乘二*/
return tmpk>=k;
}
void solve()
{
cin>>n>>m>>k;
int l=0,r=5e18+10,ans=0;
while(l<=r){
int mid=(l+r)>>1;
if(check(mid)){
// ans=mid;
// cout<<ans<<endl;
r=mid-1;
}//"tmp<k"答案在r中
else l=mid+1;
}
cout<<l<<endl;
}
一个小细节调了一小时我靠 我还以为二分的问题 想揍自己:)
int a=6,b=4;
cout<<2*a/b<<' '<<a/b*2<<endl;//输出:3 2 这俩运算顺序不一样 结果也不一样
//{2*a=12 12/b=3} {a/b=1 1*2=2}
周二:济南ccpc
A 签到
(对不起签到题还wa我太菜了
J
一个点(密度为d)删除,其他点密度变化情况
-
d的点密度不变
- <=d的点密度变小,因为大的那个值都靠这个点了
所以只需要把密度<=k的点删了
int buc[N][3]={0};
void solve(){
int n;
cin>>n;
vector<int>x(n+5,0),y(n+5,0),z(n+5,0);
memset(buc,0,sizeof(buc));
vector<int>v(n,0);
forr(i,1,n){
cin>>x[i]>>y[i]>>z[i];
buc[x[i]][0]++;buc[y[i]][1]++;buc[z[i]][2]++;
}
forr(i,1,n){
int maxn=max(buc[x[i]][0],max(buc[y[i]][1],buc[z[i]][2]));
v[i-1]=maxn;
}
sort(v.begin(),v.end(),[&](int a,int b){
return a<b;
});
//for(auto i:v)cout<<i<<' ';
//cout<<endl;
int ans;
forr(k,0,n-1){
//找<k的元素位置
ans=upper_bound(v.begin(),v.end(),k)-v.begin();//记得要减去初始位置
// forr(i,0,n-1){
// if(v[i]>k)break;
// ans++;
// } 枚举TLE了 所以使用upper_bound二分
cout<<ans<<' ';
}
cout<<endl;
}
周三、周四:练签到、铜牌题
vp了2023合肥区域赛 太恶心了 数论、图论、dp往我脸上招呼 破大防
23年济南区域赛
A:找规律?
思路:
- 先列举足够例子找满足条件的规律
- 方向是乱的,只关心种类就可以
- 一座“山峰”
- 两种括号交替([()]) Yes 只有中间两个连续
- 括号交错([)] No
- 多座“山峰”
- ()() 或者([…])([…])外围括号相同 No
- (…)[…]外围括号不同 Yes
- 就两种括号,所以只能有两座“山峰”使用不同外围括号
- 判断条件:前后两个连续的个数不超过2个 相同种括号一直连续的不超过2个
int judge(char c){
if(c=='('||c==')')return 0;
else if(c=='['||c==']')return 1;
return -1;
}
void solve()
{
string s;
cin>>s;
int len=s.size();
int cnt=0,con=0;
forr(i,1,len-1){//记录前后括号种类
if(judge(s[i])==judge(s[i-1])){
cnt++;
con++;
}else con=0;
if(con>2)return cout<<"No"<<endl,void();
}
// cout<<cnt<<' ';
if(cnt>2||cnt==0)cout<<"No"<<endl;
else cout<<"Yes"<<endl;
}
D:水
好想,用时15min左右
I:贪心
- 限制:l<r a l a_l al> a r a_r ar逆序对
- 次数 ⌊ n / 2 ⌋ ⌊n/2⌋ ⌊n/2⌋保证 每次至少两个数排好,该次数内可以排成有序
- 贪心:让每次排序区间尽量大
- 找最左最右的
- 先找l: a i ≠ i a_i≠i ai=i意味着这个地方要排序,所以从最左边开始找
- 再确定r:从最右找第一个小于
a
i
a_i
ai的
破防了,自己的代码T了,调了多半小时,md一个签到题耗了一小时。
dalao的AC代码
const int N=1e2+10;
int a[N];
int lans[N],rans[N];
void solve()
{
memset(a,0,sizeof(a));
memset(lans,0,sizeof(lans));
memset(rans,0,sizeof(rans));
int n;
cin>>n;
int flag=0;
forr(i,1,n){
cin>>a[i];
if(a[i]!=i)flag=1;
}
if(!flag)return cout<<0<<endl,void();
int cnt=0;
int l=1;
forr(l,1,n){
if(a[l]!=l){
int r=n;
for(r=n;r>l;r--){
if(a[r]<a[l])
{
cnt++;
lans[cnt]=l;
rans[cnt]=r;
sort(a+l,a+r+1);break;
}
}
}
}
cout<<cnt<<endl;
forr(i,1,cnt){
cout<<lans[i]<<' '<<rans[i]<<endl;
}
}
周六:签到+热身赛
- 好多人啊。
- 感觉坐在赛场上比平时vp更急更紧张,热身赛过题纯靠队友。看到别的dalao做完题都走了,我拿题的手微微颤抖,心态炸裂。
周日:正式赛
前两小时队友a出M和J之后,我一直看C题,感觉很似曾相识,像囚犯问题,但是忘了该怎么做。董学长说是分治,但是我不清楚分过之后怎么治,于是手动找规律,最后拼命也没找出来,此时写文章时搜了下,感觉自己很蠢。传送门:使用二进制理解。
H题和董学长讨论思路后发现还有两点在一个角度的情况没考虑全。
以下是本次比赛心得
- 好好吃饭,不然做题会头晕。
- 先看数据范围,根据时间复杂度选择合适算法。
- 减少感觉成分,要有足够实际和具体的实现思路。
- 稳如老狗,减少情绪化,减少被场上环境干扰。
- 加训!!!
- 下一步主要锻炼思维,尽力向图论数论推进。