前言
A 到 E 一马平川,然后 F 坐牢五十分钟 qwq......
一、A - Repdigit
cpp
#include <bits/stdc++.h>
using namespace std;
/* /\_/\
* (= ._.)
* / > \>
*/
/*
*想好再写
*注意审题 注意特判
*不要红温 不要急躁 耐心一点
*WA了不要立马觉得是思路不对 先耐心找反例
*/
#define dbg(x) cout<<#x<<endl;cout<<x<<endl;
#define vdbg(a) cout<<#a<<endl;for(auto x:a)cout<<x<<" ";cout<<endl;
#define YES cout<<"YES"<<endl;return ;
#define Yes cout<<"Yes"<<endl;return ;
#define NO cout<<"NO"<<endl;return ;
#define No cout<<"No"<<endl;return ;
#define endl '\n'
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
const int INF=1e9;
const ll INFLL=1e18;
const int dx[]={-1,1,0,0};
const int dy[]={0,0,-1,1};
const int ddx[]={-2,-1,1,2,2,1,-1,-2};
const int ddy[]={1,2,2,1,-1,-2,-2,-1};
void solve()
{
string s;
cin>>s;
set<char>st;
for(auto ch:s)
{
st.insert(ch);
}
if(st.size()==1)
{
cout<<"Yes"<<endl;
}
else
{
cout<<"No"<<endl;
}
}
void init()
{
}
signed main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int t=1;
//cin>>t;
init();
while(t--)
{
solve();
}
return 0;
}
没啥好说的,直接 set 去重放空大脑了()
二、B - Digit Sum
cpp
#include <bits/stdc++.h>
using namespace std;
/* /\_/\
* (= ._.)
* / > \>
*/
/*
*想好再写
*注意审题 注意特判
*不要红温 不要急躁 耐心一点
*WA了不要立马觉得是思路不对 先耐心找反例
*/
#define dbg(x) cout<<#x<<endl;cout<<x<<endl;
#define vdbg(a) cout<<#a<<endl;for(auto x:a)cout<<x<<" ";cout<<endl;
#define YES cout<<"YES"<<endl;return ;
#define Yes cout<<"Yes"<<endl;return ;
#define NO cout<<"NO"<<endl;return ;
#define No cout<<"No"<<endl;return ;
#define endl '\n'
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
const int INF=1e9;
const ll INFLL=1e18;
const int dx[]={-1,1,0,0};
const int dy[]={0,0,-1,1};
const int ddx[]={-2,-1,1,2,2,1,-1,-2};
const int ddy[]={1,2,2,1,-1,-2,-2,-1};
void solve()
{
int n,k;
cin>>n>>k;
int ans=0;
for(int i=1;i<=n;i++)
{
int x=i;
int sum=0;
while(x)
{
sum+=x%10;
x/=10;
}
if(sum==k)
{
ans++;
}
}
cout<<ans<<endl;
}
void init()
{
}
signed main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int t=1;
//cin>>t;
init();
while(t--)
{
solve();
}
return 0;
}
因为值域不大,所以直接暴力即可,反正每次也没多少位。
三、C - AtCoder Riko
cpp
#include <bits/stdc++.h>
using namespace std;
/* /\_/\
* (= ._.)
* / > \>
*/
/*
*想好再写
*注意审题 注意特判
*不要红温 不要急躁 耐心一点
*WA了不要立马觉得是思路不对 先耐心找反例
*/
#define dbg(x) cout<<#x<<endl;cout<<x<<endl;
#define vdbg(a) cout<<#a<<endl;for(auto x:a)cout<<x<<" ";cout<<endl;
#define YES cout<<"YES"<<endl;return ;
#define Yes cout<<"Yes"<<endl;return ;
#define NO cout<<"NO"<<endl;return ;
#define No cout<<"No"<<endl;return ;
#define endl '\n'
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
const int INF=1e9;
const ll INFLL=1e18;
const int dx[]={-1,1,0,0};
const int dy[]={0,0,-1,1};
const int ddx[]={-2,-1,1,2,2,1,-1,-2};
const int ddy[]={1,2,2,1,-1,-2,-2,-1};
void solve()
{
int n;
cin>>n;
vector<ll>a(n+1);
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
sort(a.begin()+1,a.end());
vector<ll>ans;
if(n%2==0)
{
int ok=1;
for(int i=1,j=n;i<j;i++,j--)
{
if(a[i]+a[j]!=a[1]+a[n])
{
ok=0;
break;
}
}
if(ok)
{
ans.push_back(a[1]+a[n]);
}
}
int pos=n;
while(pos>=1&&a[pos]==a[n])
{
pos--;
}
if(pos%2==0)
{
int ok=1;
for(int i=1,j=pos;i<j;i++,j--)
{
if(a[i]+a[j]!=a[n])
{
ok=0;
break;
}
}
if(ok)
{
ans.push_back(a[n]);
}
}
sort(ans.begin(),ans.end());
for(auto x:ans)
{
cout<<x<<" ";
}
cout<<endl;
}
void init()
{
}
signed main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int t=1;
//cin>>t;
init();
while(t--)
{
solve();
}
return 0;
}
这个题还真不好想,还真得被卡一会儿......
首先,不难想到肯定是有可能这 n 个数两两一对构成一种合法方案。由于这样必然是小数和大数组合,那么排序后若 n 是偶数就两头检验一下即可。之后还可以想到,其实也可以是最大值自己不变,然后剩余的数两两组合。那么就是把所有最大值抓出来,然后检查剩余数即可。此时可以发现,一共就只有这两种可能。
四、D - Many Repunit Sum
cpp
#include <bits/stdc++.h>
using namespace std;
/* /\_/\
* (= ._.)
* / > \>
*/
/*
*想好再写
*注意审题 注意特判
*不要红温 不要急躁 耐心一点
*WA了不要立马觉得是思路不对 先耐心找反例
*/
#define dbg(x) cout<<#x<<endl;cout<<x<<endl;
#define vdbg(a) cout<<#a<<endl;for(auto x:a)cout<<x<<" ";cout<<endl;
#define YES cout<<"YES"<<endl;return ;
#define Yes cout<<"Yes"<<endl;return ;
#define NO cout<<"NO"<<endl;return ;
#define No cout<<"No"<<endl;return ;
#define endl '\n'
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
const int INF=1e9;
const ll INFLL=1e18;
const int dx[]={-1,1,0,0};
const int dy[]={0,0,-1,1};
const int ddx[]={-2,-1,1,2,2,1,-1,-2};
const int ddy[]={1,2,2,1,-1,-2,-2,-1};
const int MAXN=2e5+10;
void solve()
{
int n;
cin>>n;
vector<ll>a(n+1);
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
vector<int>cnts(MAXN+1);
for(int i=1;i<=n;i++)
{
cnts[1]++;
cnts[a[i]+1]--;
}
for(int i=1;i<=MAXN;i++)
{
cnts[i]+=cnts[i-1];
}
for(int i=1;i<=MAXN;i++)
{
cnts[i+1]+=cnts[i]/10;
cnts[i]%=10;
}
string s;
int zero=1;
for(int i=MAXN;i>=1;i--)
{
if(cnts[i]>0)
{
zero=0;
}
if(cnts[i]!=0||!zero)
{
s+=(char)(cnts[i]+'0');
}
}
cout<<s<<endl;
}
void init()
{
}
signed main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int t=1;
//cin>>t;
init();
while(t--)
{
solve();
}
return 0;
}
因为位数很大,所以肯定不能直接模拟。那么考虑先维护出每一位 +1 的次数,这个是可以通过差分然后前缀和解决的,当然线段树也可以()。之后,从低位到高位开始进位,每次往后递推,最后再从高位到低位拼字符串即可。注意,如果最后使用 s.erase(s.begin())去掉高位 0 的话会超时,咱也不知道为什么......
五、E - Sparse Range
cpp
#include <bits/stdc++.h>
using namespace std;
/* /\_/\
* (= ._.)
* / > \>
*/
/*
*想好再写
*注意审题 注意特判
*不要红温 不要急躁 耐心一点
*WA了不要立马觉得是思路不对 先耐心找反例
*/
#define dbg(x) cout<<#x<<endl;cout<<x<<endl;
#define vdbg(a) cout<<#a<<endl;for(auto x:a)cout<<x<<" ";cout<<endl;
#define YES cout<<"YES"<<endl;return ;
#define Yes cout<<"Yes"<<endl;return ;
#define NO cout<<"NO"<<endl;return ;
#define No cout<<"No"<<endl;return ;
#define endl '\n'
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
const int INF=1e9;
const ll INFLL=1e18;
const int dx[]={-1,1,0,0};
const int dy[]={0,0,-1,1};
const int ddx[]={-2,-1,1,2,2,1,-1,-2};
const int ddy[]={1,2,2,1,-1,-2,-2,-1};
void solve()
{
ll n,d;
cin>>n>>d;
vector<ll>a(n+1);
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
ll ans=0;
set<ll>st;
for(int l=1,r=1;l<=n;l++)
{
auto up=st.upper_bound(a[r]);
auto down=st.lower_bound(a[r]);
while(r<=n&&(st.find(a[r])==st.end()&&(up==st.end()||*up-a[r]>=d)&&(down==st.begin()||a[r]-*prev(down)>=d)) )
{
st.insert(a[r]);
r++;
if(r<=n)
{
up=st.upper_bound(a[r]);
down=st.lower_bound(a[r]);
}
}
ll len=r-l;
ans+=len;
st.erase(a[l]);
}
cout<<ans<<endl;
}
void init()
{
}
signed main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int t=1;
//cin>>t;
init();
while(t--)
{
solve();
}
return 0;
}
算是个比较简单的 E 了。
可以比较自然地想到,对于对当前的合法区间 (l,r),若此时加入 a[r+1] 就会导致区间不合法,那么就只能将 a[l] 拿出区间,再看是否能加入。所以这个就可以通过双指针维护,那么再拿 set 维护区间的每个数,每次二分查第一个大于和小于当前数的数,看看是否相差大于等于 d 即可。
六、F - Half and Median
cpp
#include <bits/stdc++.h>
using namespace std;
/* /\_/\
* (= ._.)
* / > \>
*/
/*
*想好再写
*注意审题 注意特判
*不要红温 不要急躁 耐心一点
*WA了不要立马觉得是思路不对 先耐心找反例
*/
#define dbg(x) cout<<#x<<endl;cout<<x<<endl;
#define vdbg(a) cout<<#a<<endl;for(auto x:a)cout<<x<<" ";cout<<endl;
#define YES cout<<"YES"<<endl;return ;
#define Yes cout<<"Yes"<<endl;return ;
#define NO cout<<"NO"<<endl;return ;
#define No cout<<"No"<<endl;return ;
#define endl '\n'
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
const int INF=1e9;
const ll INFLL=1e18;
const int dx[]={-1,1,0,0};
const int dy[]={0,0,-1,1};
const int ddx[]={-2,-1,1,2,2,1,-1,-2};
const int ddy[]={1,2,2,1,-1,-2,-2,-1};
void solve()
{
ll n,m;
cin>>n>>m;
vector<ll>a(n+1);
ll tot=0;
for(int i=1;i<=n;i++)
{
cin>>a[i];
tot+=a[i];
}
auto check=[&](int x)->bool
{
int cnt=0;
//只考虑大于等于x的数,每个数拆后有几个
map<int,ll>mp;
for(int i=1;i<=n;i++)
{
if(a[i]>=x)
{
cnt++;
//拆
if(a[i]>=2*x-1)
{
int v=a[i];
//v和v+1的个数
int c0=1,c1=0;
while(v>=2*x-1)
{
if(v%2)
{
//v/2 -> v/2,v/2+1
//(v+1)/2 -> v/2+1,v/2+1
c1=2*c1+c0;
}
else
{
//v/2 -> v/2,v/2
//(v+1)/2 -> v/2,v/2+1
c0=2*c0+c1;
}
v/=2;
}
if(v>=x)
{
mp[v]+=c0;
}
if(v+1==2*x-1)
{
mp[x]+=c1;
}
else
{
mp[v+1]+=c1;
}
}
//不拆
else
{
mp[a[i]]++;
}
}
}
ll h=(n+m+1)/2;
//次数不够
if(cnt+m<h)
{
return false;
}
//找大于等于x最小的h个
ll need=h;
ll sum=0;
for(auto [k,v]:mp)
{
if(need<=v)
{
sum+=need*k;
need=0;
break;
}
sum+=k*v;
need-=v;
}
//木棍不够
if(need>0)
{
return false;
}
//操作过多
return tot-sum>=(n+m)/2;
};
int l=1;
int r=1e9;
int mid;
int ans;
while(l<=r)
{
mid=l+r>>1;
if(check(mid))
{
ans=mid;
l=mid+1;
}
else
{
r=mid-1;
}
}
cout<<ans<<endl;
}
void init()
{
}
signed main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int t=1;
cin>>t;
init();
while(t--)
{
solve();
}
return 0;
}
对于最大最小化中位数的问题,永远先考虑二分答案!!
那么在每次二分时,去 check 是否能做到中位数大于等于 x,如果能就往后二分,否则往前二分。那么 check 时就还是常见的 trick,考虑将大于等于 x 的数看作 1,小于 x 的数看作 -1。那么若最后的累加和大于等于 0,就说明中位数大于等于 x。
在 check 时,其实就是要最后的中位数尽可能大,也就是要让大于等于 x 的数的个数 尽可能多,那么就可以分类讨论一下:
第一种情况,对于大于等于 2x 的数,让其分成两个大于等于 x 的数必然可以使个数 +1,那么就优先分。
第二种情况,对于 2x-1,其可以分成 x 和 x-1,所以贡献就是 0。
第三种情况,对于大于等于 x 的数,分裂的话会使个数 -1。
第四种情况,对于小于 x 的数,分不分裂是一样的。
所以,在操作的过程中就应该优先操作第一类数,再操作第二和第四类数,最后实在没办法了再操作第三类数。
首先可以发现,在最乐观的情况下,若原本大于等于 x 的个数加上 m 仍然不够 h 个,那么就一定不行。之后还可以发现,在把所有第一类数和第二类数都分完后,若此时大于等于 x 的数还小于 h,那么就依然说明不可能。此时在讨论完这两种情况后,如果还不行,那么就只能是因为 m 过大,导致把原本有贡献的数也给拆掉了。
由于经历了之前两轮,此时剩下的数必然都是小于 2x-1 的,且当前大于等于 x 的个数必然大于等于 h。因为要继续拆,所以先可以考虑把大于等于 x 中最小的 h 个数保护起来,去看能否在剩下的数中用完 m 次操作。之后,因为对于数字 x,其可以最终被拆成 x 个 1。那么假设保护的数的累加和为 s,那么剩下的数就是 ,也就是拆到最后除了保护的数以外数的个数。那么若
,那么就说明不可能达成。否则,就说明必然可以达成。
总结
这个 F 题怎么说呢,感觉没啥没见过的东西,但就是写不出来......