I - Cyclic Apple Strings(思维)
每次可以把连续的一段1移到最后面,因此统计连续1的段数,特判最后一段1即可
#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/tree_policy.hpp>
using namespace std;
using namespace __gnu_pbds;
#define int long long
#define TR tree<int,null_type,less<int>,rb_tree_tag,tree_order_statistics_node_update>
typedef pair<int,int> PII;
const int N=3e6+10,M=2e5+10;
const int INF=1e18;
// const int mod=998244353;
const int mod=1e9+7;
mt19937_64 rng(chrono::steady_clock::now().time_since_epoch().count());
void solve(){
string s;
cin>>s;
int cnt=0;
bool f=false;
for(int i=1;i<s.size();i++){
if(s[i]<s[i-1]){
f=true;
break;
}
}
if(!f){
cout<<0<<"\n";
return;
}
for(int i=0;i<s.size();i++){
if(s[i]=='0')continue;
cnt++;
while(i+1<s.size()&&s[i+1]==s[i])i++;
}
cout<<cnt-(s.back()=='1')<<"\n";
}
signed main(){
cin.tie(0)->sync_with_stdio(0);
int T=1;
// cin>>T;
while(T--)solve();
return 0;
}K - Party Games(结论)

- 因此只有是必胜的
#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/tree_policy.hpp>
using namespace std;
using namespace __gnu_pbds;
#define int long long
#define TR tree<int,null_type,less<int>,rb_tree_tag,tree_order_statistics_node_update>
typedef pair<int,int> PII;
const int N=3e6+10,M=2e5+10;
const int INF=1e18;
// const int mod=998244353;
const int mod=1e9+7;
mt19937_64 rng(chrono::steady_clock::now().time_since_epoch().count());
void solve(){
int n;
cin>>n;
if(n%4==0||n%4==1)cout<<"Fluttershy\n";
else cout<<"Pinkie Pie\n";
}
signed main(){
cin.tie(0)->sync_with_stdio(0);
int T=1;
cin>>T;
while(T--)solve();
return 0;
}B - Countless Me(贪心)

- 可以发现值可以任意分配给每位数
- 二进制从高到低考虑,贪心填补总值,假设当前枚举到第位,那么看前位全部填是否能将剩余的值填满,如果可以,该位可以不填,否则该位必定产生贡献,贪心的把能填的填进去
#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/tree_policy.hpp>
using namespace std;
using namespace __gnu_pbds;
#define int long long
#define TR tree<int,null_type,less<int>,rb_tree_tag,tree_order_statistics_node_update>
typedef pair<int,int> PII;
const int N=3e6+10,M=2e5+10;
const int INF=1e18;
// const int mod=998244353;
const int mod=1e9+7;
mt19937_64 rng(chrono::steady_clock::now().time_since_epoch().count());
void solve(){
int n;
cin>>n;
vector<int> a(n);
int s=0;
for(auto &i:a)cin>>i,s+=i;
int ans=0;
for(int i=32;i>=0;i--){
if(s>n*((1ll<<i)-1)){
ans+=1ll<<i;
int x=min(n,s/(1ll<<i));
s-=x*(1ll<<i);
}
}
cout<<ans<<"\n";
}
signed main(){
cin.tie(0)->sync_with_stdio(0);
int T=1;
// cin>>T;
while(T--)solve();
return 0;
}F - Custom-Made Clothes(思维)

- 假设,那么
- 因此可以双指针维护,次询问就可以判断小于等于的数有多少个
- 考虑二分答案,询问次数位为,20000次左右足够询问
#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/tree_policy.hpp>
using namespace std;
using namespace __gnu_pbds;
#define int long long
#define TR tree<int,null_type,less<int>,rb_tree_tag,tree_order_statistics_node_update>
typedef pair<int,int> PII;
const int N=3e6+10,M=2e5+10;
const int INF=1e18;
// const int mod=998244353;
const int mod=1e9+7;
mt19937_64 rng(chrono::steady_clock::now().time_since_epoch().count());
int ask(int i,int j,int x){
cout<<"? "<<i<<" "<<j<<" "<<x<<endl;
cin>>i;
return i;
}
void claim(int x){
cout<<"! "<<x<<endl;
}
int n,k;
bool check(int mid){
int cnt=0;
for(int i=1,j=n;i<=n;i++){
while(j>=1&&!ask(i,j,mid))j--;
cnt+=j;
}
return cnt>=k;
}
void solve(){
cin>>n>>k;
k=n*n-k+1;
int l=1,r=n*n;
while(l<r){
int mid=l+r>>1;
if(check(mid))r=mid;
else l=mid+1;
}
claim(l);
}
signed main(){
cin.tie(0)->sync_with_stdio(0);
int T=1;
// cin>>T;
while(T--)solve();
return 0;
}M - Merge(思维)
题意:
,对数组进行操作,最大化数组(排序后的)的字典序
思路:
- 可以发现合成是一个斐波那契式的迭代,因此对于是否能合成数,递归深度不会超过
- 另一个重要的点是一个奇数和一个偶数生成一个奇数,因此偶数是不可再生的,因此递归时碰到偶数有就是有没有就是没有,不需对偶数递归,因此dfs求解答案可以从原来一个节点会产生两个分支变成一个分支
- 因此可以从大到小考虑每个值是否参与合成
- 复杂度
#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/tree_policy.hpp>
using namespace std;
using namespace __gnu_pbds;
#define int long long
#define TR tree<int,null_type,less<int>,rb_tree_tag,tree_order_statistics_node_update>
typedef pair<int,int> PII;
const int N=3e6+10,M=2e5+10;
const int INF=1e18;
// const int mod=998244353;
const int mod=1e9+7;
mt19937_64 rng(chrono::steady_clock::now().time_since_epoch().count());
void solve(){
int n;
cin>>n;
vector<int> a(n+1);
map<int,int> mp,mp1;
int mx=0;
for(int i=1;i<=n;i++){
cin>>a[i];
mp[a[i]]++;
}
sort(a.begin()+1,a.end(),greater<>());
vector<int> ans;
auto dfs=[&](auto dfs,int x){
if(mp[x]){
mp[x]--;
mp1[x]++;
return true;
}
if(x%2==0)return false;
int a=x/2,b=x-a;
if(dfs(dfs,a)&&dfs(dfs,b)){
return true;
}
return false;
};
ranges::sort(ans,greater<>());
for(int i=1;i<=n;i++){
if(!mp[a[i]])continue;
mp[a[i]]--;
mp1.clear();
if(dfs(dfs,a[i]+1)){
mp[2*a[i]+1]++;
continue;
}
for(auto [x,y]:mp1)mp[x]+=y;
mp1.clear();
if(dfs(dfs,a[i]-1)){
mp[2*a[i]-1]++;
continue;
}
for(auto [x,y]:mp1)mp[x]+=y;
mp[a[i]]++;
}
for(auto [x,y]:mp)for(int i=1;i<=y;i++)ans.push_back(x);
ranges::sort(ans,greater<>());
cout<<ans.size()<<"\n";
for(auto it:ans)cout<<it<<" ";
cout<<"\n";
}
signed main(){
cin.tie(0)->sync_with_stdio(0);
int T=1;
// cin>>T;
while(T--)solve();
return 0;
}D - ICPC(dp)

- 首先如果不能回头吃其他的菜品,可以暴力求出来
- 其次考虑回头的问题,可以dp
- 表示走到节点用了时间的最大值
dp_{i,j}=\max(dp_{i,j},dp_{i+1,j-1},dp_{i-1,j-1})
* 先枚举第二维,再枚举第一维转移即可 ```cpp #include<bits/stdc++.h> #include<ext/pb_ds/assoc_container.hpp> #include<ext/pb_ds/tree_policy.hpp> using namespace std; using namespace __gnu_pbds; #define int long long #define TR tree<int,null_type,less<int>,rb_tree_tag,tree_order_statistics_node_update> typedef pair<int,int> PII; const int N=3e6+10,M=2e5+10; const int INF=1e18; // const int mod=998244353; const int mod=1e9+7; mt19937_64 rng(chrono::steady_clock::now().time_since_epoch().count()); void solve(){ int n; cin>>n; vector dp(n+1,vector<int>(2*n+1)); vector<int> a(n+1); for(int i=1;i<=n;i++)cin>>a[i]; for(int i=1;i<=n;i++){ int sl=0,sr=0; for(int j=0;j<=2*n;j++){ if(i-j>=1)sl+=a[i-j]; if(i+j<=n)sr+=a[i+j]; dp[i][j]=max(sl,sr); } } for(int j=1;j<=2*n;j++){ for(int i=1;i<=n;i++){ if(i+1<=n)dp[i][j]=max(dp[i][j],dp[i+1][j-1]); if(i-1>=1)dp[i][j]=max(dp[i][j],dp[i-1][j-1]); } } int ans=0; for(int i=1;i<=n;i++){ int c=0; for(int j=1;j<=2*n;j++)c^=j*dp[i][j]; ans^=(c+i); } cout<<ans<<"\n"; } signed main(){ cin.tie(0)->sync_with_stdio(0); int T=1; // cin>>T; while(T--)solve(); return 0; } ``` # E - Boomerang(树的直径)    * 假设对于确定的$k$最终结束的点集为$V(r,t)$,那么选取的出发点一定是直径的中点,这样离所有叶子节点的最大值最小 * 假设我们求出所有$t$对应的直径$D$,那么对于当前的$k$如果能覆盖掉对应的点集$V(r,t)$,需要满足$2k(t-t_0) \geq D$,随着$k$增大,满足条件的$t$减少,可以双指针求解答案 * 现在问题是如何求解每个$V(r,t)$的直径 * 假设一棵树,原本的直径为$u,v$,那么新加一个点$x$,直径如果变化,只会变成$u,x$或者$v,x$,因此我们需要一个可以查询树上两点距离的数据结构(LCA或者树剖) * $t$每增加$1$,点集扩散一层,相当于是对于深度相同的节点同时处理 * 复杂度$O(n \log n)$ ```cpp #include<bits/stdc++.h> #include<ext/pb_ds/assoc_container.hpp> #include<ext/pb_ds/tree_policy.hpp> using namespace std; using namespace __gnu_pbds; #define int long long #define TR tree<int,null_type,less<int>,rb_tree_tag,tree_order_statistics_node_update> typedef pair<int,int> PII; const int N=2e5+10,M=2e5+10; const int INF=1e18; // const int mod=998244353; const int mod=1e9+7; mt19937_64 rng(chrono::steady_clock::now().time_since_epoch().count()); int par[N][30],dep[N],n,r,t0,dis[N<<1]; vector<int> adj[N],g[N<<1]; void dfs(int u,int fa){ par[u][0]=fa; dep[u]=dep[fa]+1; for(int i=1;i<=__lg(dep[u]);i++)par[u][i]=par[par[u][i-1]][i-1]; for(auto v:adj[u]){ if(v==fa)continue; dfs(v,u); } } int lca(int x,int y){ if(dep[x]<dep[y])swap(x,y); while(dep[x]>dep[y])x=par[x][__lg(dep[x]-dep[y])]; if(x==y)return x; for(int i=25;i>=0;i--){ if(par[x][i]!=par[y][i])x=par[x][i],y=par[y][i]; } return par[x][0]; } int getdis(int x,int y){ return dep[x]+dep[y]-2*dep[lca(x,y)]; } void solve(){ cin>>n; for(int i=1;i<n;i++){ int u,v; cin>>u>>v; adj[u].push_back(v); adj[v].push_back(u); } cin>>r>>t0; dfs(r,0); for(int i=1;i<=n;i++){ g[dep[i]-1].push_back(i); } int d=0,x=r,y=r; for(int i=1;i<=2*n;i++){ for(auto v:g[i]){ int d1=getdis(x,v),d2=getdis(y,v); if(d1>d){ d=d1; y=v; } if(d2>d){ d=d2; x=v; } } dis[i]=d; } for(int k=1,t=n;k<=n;k++){ while((t-t0)*k*2>=dis[t])t--; while((t-t0)*k*2<dis[t])t++; cout<<t<<" "; } } signed main(){ cin.tie(0)->sync_with_stdio(0); int T=1; // cin>>T; while(T--)solve(); return 0; } ``` # G - Pack(EXGCD+整除分块)  线性同余方程好题,做完会对线性同余方程和整除分块有更深的理解 * $ax+by=k$可以用$exgcd$求出一组解 * 假设知道选择$x$个$A$个物品和$y$个$B$物品,那么答案就是$n+m-(x+y)\min(\lfloor \frac{n}{x} \rfloor,\lfloor \frac{m}{y} \rfloor)$ * 注意到后面的一坨是整除分块的形式 * 定义$x$为最小非负整数解,$y=\frac{k-ax}{b}$为对应的最大解,增长速率$dx=\frac{b}{\gcd(a,b)},dy=\frac{a}{\gcd(a,b)}$ * 通解$X=x+t\cdot dx,Y=y-t\cdot dy$ * 因为$y$此时为最大值,$x,y$均为非负整数,因此$t \in [0,\frac{y}{dy}]$ * 考虑先枚举$\lfloor \frac{n}{x} \rfloor$ * 假设当前整除分块的范围为$X=[l,r]$,那么此时的值$v=\lfloor \frac{n}{l} \rfloor$,那么另一边满足$Y \leq \lfloor \frac{m}{v} \rfloor=limit$的条件,此时$\frac{m}{Y} \geq v$ * 因此有$y-t\cdot dy \leq limit \rightarrow t \geq \lceil \frac{y-limit}{dy}\rceil$ * 对于$X$的范围,有$l \leq x+t \cdot dx \leq r \rightarrow t \geq \lceil\frac{l-x}{dx}\rceil \land t \leq \lfloor \frac{r-x}{dx} \rfloor$ * 对上面所有式子求出满足t的范围$[L,R]$,如果$dx-dy \geq 0$,t选择最大值即$R$否则为$L$ * 做完上面的反过来枚举一遍$\lfloor \frac{m}{y} \rfloor$ ```cpp #include<bits/stdc++.h> #include<ext/pb_ds/assoc_container.hpp> #include<ext/pb_ds/tree_policy.hpp> using namespace std; using namespace __gnu_pbds; #define int long long #define TR tree<int,null_type,less<int>,rb_tree_tag,tree_order_statistics_node_update> typedef pair<int,int> PII; const int N=2e5+10,M=2e5+10; const int INF=1e18; // const int mod=998244353; const int mod=1e9+7; mt19937_64 rng(chrono::steady_clock::now().time_since_epoch().count()); int exgcd(int a,int b,int &x,int &y){ if(!b){ x=1,y=0; return a; } int d=exgcd(b,a%b,y,x); y-=a/b*x; return d; } void solve(){ int n,m,a,b,k; cin>>n>>m>>a>>b>>k; int x,y; int g=exgcd(a,b,x,y); if(k%g!=0){ cout<<n+m<<"\n"; return; } x*=k/g,y*=k/g; int dx=b/g,dy=a/g; int ans=0; x=(x%dx+dx)%dx; y=(k-a*x)/b; int t=y/dy; auto work=[&](int n,int m,int dx,int dy,int x,int y){ for(int l=1,r;l<=n;l=r+1){ r=n/(n/l); int v=n/l; int lim=m/v; // y-t*dy<=lim ==> t>=(y-lim)/dy // l<=x+t*dx<=r ==> t>=(l-x)/dx && t<=(r-x)/dx if(x>r)continue; int L=max({0ll,(y-lim+dy-1)/dy,(l-x+dx-1)/dx}); int R=min(t,(r-x)/dx); if(L<=R){ ans=max(ans,(x+L*dx+y-L*dy)*v); ans=max(ans,(x+R*dx+y-R*dy)*v); } } }; if(a*x>k){ cout<<n+m<<"\n"; return; } work(n,m,dx,dy,x,y); // cout<<ans<<"\n"; y=(y%dy+dy)%dy; x=(k-b*y)/a; work(m,n,dy,dx,y,x); cout<<n+m-ans<<"\n"; } signed main(){ cin.tie(0)->sync_with_stdio(0); int T=1; cin>>T; while(T--)solve(); return 0; } ```