B 数学 分解质因数
k n m o d n = 0 k^n\mod n=0 knmodn=0,k只要包含n的所有质因数就好
若 n = p 1 t 1 , k n = p 1 n , 必有 n ≥ t 1 = log p 1 n n=p_1^{t_1},k^n=p_1^n,必有n\geq t_1=\log_{p_1}n n=p1t1,kn=p1n,必有n≥t1=logp1n
cpp
void solve(){
int n;cin>>n;
int tp=n;
map<int,int>m;
forr(i,2,sqrt(n)){
int fg=0;
while(tp%i==0){
fg=1;
tp/=i;
m[i]++;
}
}
m[tp]++;
int ans=1;
for(auto [p,x]:m){
ans*=p;
}
cout<<ans<<endl;
}
C 贪心 排序 STL
题意:给一系列数组,每个数组逆序后从前到后拼接数组,每种数只输出最前面的一个,让输出字典序最小
直接考虑逆序数组,最先想到的就是每次把数组字典序小的放前面
直接排序,如果像121999和122345,应该122345放前面,但是排序后121999在前面,小的数会把大的数拉到前面去
所以每次选最优数组,不同数组比较应该剔除前面出现过的数(已经输出的数),同一个数组相邻重复的数作用相当于合为一个数
cpp
void solve(){
int n;cin>>n;
vector<vint>a(n+1);
forr(i,1,n){
int l;cin>>l;
int prex=-1;
forr(j,1,l){
int x;cin>>x;
if(x==prex)continue;//相邻重复 不会有贡献
prex=x;
a[i].push_back(x);
}
reverse(a[i].begin(),a[i].end());// 数组越后面的 在ans里越靠前
}
vector<int>ans;
map<int,int>vis;
priority_queue<vint,vector<vint>,greater<vint>>q;
forr(i,1,n)q.push(a[i]);
while (q.size())
{
vint qv=q.top();q.pop();
for(auto x:qv){
if(!vis[x])ans.push_back(x),vis[x]=1;
}
priority_queue<vint,vector<vint>,greater<vint>>tp_q;//在每个数组里剔除小的/已经放入ans的数
while (q.size())
{
qv=q.top();q.pop();
vint newv;
for(auto x:qv)if(!vis[x])newv.push_back(x);
tp_q.push(newv);
}
q=tp_q;//priority_queue可以直接赋值
}
for(auto x:ans)cout<<x<<' ';cout<<endl;
/*以下是一开始的wa代码 没有剔除已经放过了数 会影响之后的选择
sort(a.begin()+1,a.end());
// forr(i,1,n){for(auto x:a[i])cout<<x<<' ';cout<<endl;}
vector<int>fg(n+1,0);
map<int,int>pos,jud;
forr(i,1,n){
int l=a[i].size();
forr(j,0,l-1)
if(!pos.count(a[i][j])||jud[a[i][j]]>j){
pos[a[i][j]]=i;
jud[a[i][j]]=j;
}
}
for(auto [num,p]:pos){
// cout<<num<<' '<<p<<endl;
if(fg[p])continue;
for(auto x:a[p]){
ans.push_back(x);
fg[p]=1;
}
}
forr(i,1,n){
if(fg[i])continue;
for(auto x:a[i]){
ans.push_back(x);
}
}
set<int>vis;
// cout<<"ans";
for(auto x:ans){
if(!vis.count(x)){
vis.insert(x);
cout<<x<<' ';
}
// cout<<x<<' ';
}
cout<<endl;
*/
}
D 分治

题意:一个排列数组每个连续三元组,中间的数不是三元组的最大值,这个数组是酷的。每次可以选择不满足条件的三元组删去两端任一数。
由题意可知,酷数组每个连续三元组最大值位于两端。
让一个数组变酷,至少要把最大值mx一边的数全删去,mx位于边上(mx一定可以进行这样的操作)
剩下另一边的数成为一个新的数组,性质相同,可以做和原数组相同的处理,最小到三元组
cpp
void solve(){
int n;cin>>n;
vector<int>a(n+1),pos(n+1);
forr(i,1,n){
cin>>a[i];
pos[a[i]]=i;
}
//st表找区间最大值
vector<vint>st(n+1,vector<int>((int)log2(n)+1));
forr(i,1,n)st[i][0]=a[i];
for(int j=1;(1<<j)<=n;j++){
for(int i=1;i+(1<<j)-1<=n;i++){
st[i][j]=max(st[i][j-1],st[i+(1<<(j-1))][j-1]);
}
}
auto findmx=[&](int l,int r)->int{
int len=log2(r-l+1);
int mx=max(st[l][len],st[r-(1<<len)+1][len]);
return pos[mx];
};
auto div=[&](auto&&div,int l,int r)->int{
if(r-l+1<3)return 0;
int x= findmx(l,r);
return min(r-x+div(div,l,x-1),x-l+div(div,x+1,r));// 把最大值单边删去
};
cout<<div(div,1,n)<<endl;
/* 一开始想用LIS
vector<int>dp;
dp.push_back(a[1]);
forr(i,2,n){
if(a[i]==mx)break;
if(dp.back()<a[i]){
dp.push_back(a[i]);
}else{
int pos=upper_bound(dp.begin(),dp.end(),a[i])-dp.begin();
dp[pos]=a[i];
}
}
int ans1=dp.size();
forr(i,1,n){
if(dp[0]==a[i])break;
if(dp[0]<a[i]){
ans1++;
break;
}
}
dp.clear();
dp.push_back(a[1]);
forr(i,2,n){
if(dp.back()>a[i]){
dp.push_back(a[i]);
}else{
int pos=upper_bound(dp.begin(),dp.end(),a[i],greater<int>())-dp.begin();
dp[pos]=a[i];
}
}
int ans2=dp.size();
reforr(i,1,n){
if(dp[ans2-1]==a[i])break;
if(dp[ans2-1]<a[i]){
ans2++;
break;
}
}cout<<"ans";
cout<<n-max(ans1,ans2)<<endl;
*/
}
还有一种笛卡尔树的做法 有空再学
