B 枚举倍数 模拟 调和级数算时间复杂度

一开始以为有某种式子能O(1)算出来
但其实a>=2时每次改变的倍数,最多 O ( l o g n ) O(logn) O(logn)
a=1直接全按掉, O ( 1 ) O(1) O(1)
cpp
void solve(){
int n,m;cin>>n>>m;
/*
想复杂了
限定n<2e5 直接枚举就可以
nlogn
*/
int cnt=n;
vector<int>st(n+1,0);
forr(i,1,m){
int ans=0,a;cin>>a;
if(cnt&&!st[a]){
if(a==1){// 防止退化为O(n)
cout<<cnt<<endl;
cnt=0;
}
else{
for(int j=a;j<=n;j+=a){// logn
if(st[j])continue;
else ans++,st[j]=1,cnt--;
}
if(ans)cout<<ans<<endl;
else cout<<"the lights are already on!"<<endl;
}
}else{
cout<<"the lights are already on!"<<endl;
}
}
}
D 思维 贪心

一开始没注意到字典序最小
贪心来想前面的L越多越好,尽量左右横跳
整体方向是向右到头再回来
cpp
void solve(){
int n;cin>>n;
vector<int>a(n+2,0);
int sm=0;
forr(i,0,n){
cin>>a[i];
sm+=a[i];
}
// 注意还要求字典序最小
int pos=0;
string ans;
// 从右推向左
vector<int>pre(n+1,0);// 和前一个位置有多少次交换
pre[n]=a[n];
reforr(i,0,n-1){
pre[i]=a[i]-pre[i+1];
if((i>0&&pre[i]<=0)||(i==0&&pre[i]!=0))return cout<<"Impossible"<<endl,void();
}
forr(i,1,n){
forr(j,1,pre[i]-1){
ans+="RL";// 左右横跳
}
ans+='R';// 向后走
}
forr(i,1,n){
ans+='L';// 回来
}
cout<<ans<<endl;
// while (ans.size()<sm)
// {
// // forr(i,0,n)cout<<a[i]<<' ';cout<<endl;
// if(a[pos+1]){
// ans+='R';
// a[++pos]--;
// continue;
// }else if(pos>0&&a[pos-1]){
// ans+='L';
// a[--pos]--;
// }else{
// return cout<<"Impossible"<<endl,void();
// }
// }
// // cout<<pos<<endl;
// if(pos!=0)return cout<<"Impossible"<<endl,void();
// cout<<ans<<endl;
}
L 基环树 树形dp

cpp
const int N = 1e5+10,M=1e5;
const double PI=acos(-1);
const long long mod =998244353, inf = 2e18 ;
vector<int>g[N];
// 并查集找环
int fa[N],vis[N],cnt[N][2];// cnt[][1奶龙/0不是]
int findf(int x){
return fa[x]=(fa[x]==x?x:findf(fa[x]));
}
int dfs(int now,int f){// 树形dp找答案
vis[now]=1;
cnt[now][0]=0;
cnt[now][1]=1;
for(auto x:g[now]){
if(x==f||vis[x])continue;
dfs(x,now);
cnt[now][0]+=max(cnt[x][0],cnt[x][1]);
cnt[now][1]+=cnt[x][0];
}
vis[now]=0;// 回复现场
return cnt[now][0];
}
void solve(){
/*
自环 自己说自己是奶龙 必然是说假话的其他生物
*/
int n;cin>>n;
forr(i,1,n)fa[i]=i;
vector<int>a(n+1);
vector<pii>cir;
forr(i,1,n){
cin>>a[i];
g[a[i]].push_back(i);
int fx=findf(a[i]),fy=findf(i);
if(fx!=fy)fa[fy]=fx;
else cir.push_back({a[i],i});
}
int ans=0;
for(auto [u,v]:cir){
ans+=max(dfs(u,0),dfs(v,0));
}
cout<<ans<<endl;
}
G 贪心(找每次操作的收益比率) 二分查找
贪心 让每个增加都能获得最大收益
每次增加前后 商品金额比率
p = 100 + ( t + 1 ) l 100 + t l = 1 + l 100 + t l = 1 + x ′ p=\frac{100+(t+1)l}{100+tl}=1+\frac{l}{100+tl}=1+x' p=100+tl100+(t+1)l=1+100+tll=1+x′
发现t越大 p越小
t = l − 100 x ′ x ′ l t=\frac{l-100x'}{x'l} t=x′ll−100x′ 向下取整 让p尽量比较大
令 x = 1 x ′ x={1\over {x'}} x=x′1, t = x − 100 l t=x-{{100}\over l} t=x−l100
cpp
int n,k;
int L[N],t[N];
/*
一开始想用dfs 但是每个点从k中分配 枚举每个城市的t 复杂度爆炸
void dfs(int now,int lst){
}
*/
int cal(double x){// 计算给定比率 需要的t总和
int sm=0;
forr(i,1,n){
t[i]=max(0.0,floor(x-100/(L[i]*1.0)));
sm+=t[i];
}
return sm;
}
void solve(){
cin>>n>>k;
forr(i,1,n)cin>>L[i];
double l=0,r=1e6,bestp=l;
while (r-l>eps)
{
// cout<<l<<' '<<r<<endl;
double mid=(l+r)/2;
if(cal(mid)<=k)l=mid,bestp=mid;
else r=mid;
}
int used=cal(bestp);
/* 用优先队列会损失精度
priority_queue<pair<double,int>>q;
forr(i,1,n){
q.push({(100+(t[i]+1)*L[i])/(100+t[i]*L[i]),i});
}
while (used<k)
{
auto [pn,id]=q.top();q.pop();
t[id]++;
q.push({(100.0+(t[id]+1)*L[id]*1.0)/(100.0+t[id]*L[id]*1.0),id});
used++;
}
*/
while(used<k){
// 线性查找最优比率
int id=1;
double mx=(100.0+(t[id]+1)*L[id]*1.0)/(100.0+t[id]*L[id]*1.0);
forr(i,2,n){
double now=(100.0+(t[i]+1)*L[i]*1.0)/(100.0+t[i]*L[i]*1.0);
if(now>mx){
mx=now,id=i;
}
}
t[id]++;
used++;
}
double ans=1;
forr(i,1,n)ans*=(1+1.0*(t[i]*1.0*L[i])/100.0);
cout<<fixed<<setprecision(8)<<ans<<endl;
}
