2024.10.26 2024 CCPC哈尔滨站
Solved:6/13
Penalty:635
Rank:72
Rank(ucup):170
打到后面困了(而且不会 L 心态爆炸)睡觉去了,不然还能多做个 E 题(
被 L 单防了啊。。
M. Weird Ceiling
定义 \(f(n,k)\) 如下:设 \(d\) 为 \(n\) 小于等于 \(k\) 的最大约数,则 \(f(n,k) = \frac nd\)。求 \(\sum_{k=1}^n f(n,k)\)。
将所有约数排序,然后枚举 \(d\) 即可。复杂度 \(O(\sqrt n)\)。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
void solve(){
int n;
cin>>n;
vector<int> d;
for(int i=1;i*i<=n;++i)if(!(n%i)){
d.push_back(i);
if(i!=n/i)d.push_back(n/i);
}
sort(d.begin(),d.end());
ll ans=0;
for(int i=0;i<d.size()-1;++i){
ans+=1ll*(d[i+1]-d[i])*(n/d[i]);
}
ans+=1;
cout<<ans<<'\n';
}
int main(){
ios::sync_with_stdio(0);cin.tie(0);
int T;
cin>>T;
while(T--)solve();
}
C. Giving Directions in Harbin
简单模拟,没啥好说的。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=11;
string d[N];
int a[N];
void solve(){
int n;
cin>>n;
for(int i=1;i<=n;++i)cin>>d[i]>>a[i];
vector<string> ans;
vector<int> st;
auto num=[](string x)->int {
if(x=="S")return 0;
if(x=="E")return 1;
if(x=="N")return 2;
if(x=="W")return 3;
};
auto turn=[&](string x,string y)->void {
int t=num(x)-num(y);
if(abs(t)>abs(t+4))t+=4;
if(abs(t)>abs(t-4))t-=4;
if(t>0){
for(int i=0;i<t;++i)ans.push_back("R"),st.push_back(0);
}
else{
for(int i=0;i<-t;++i)ans.push_back("L"),st.push_back(0);
}
};
ans.push_back("Z"),st.push_back(a[1]);
for(int i=2;i<=n;++i){
turn(d[i-1],d[i]);
ans.push_back("Z"),st.push_back(a[i]);
}
cout<<ans.size()<<' '<<d[1]<<'\n';
for(int i=0;i<ans.size();++i){
if(ans[i]=="Z")cout<<ans[i]<<' '<<st[i]<<'\n';
else cout<<ans[i]<<'\n';
}
}
int main(){
ios::sync_with_stdio(0);cin.tie(0);
int T;
cin>>T;
while(T--)solve();
}
G. Welcome to Join the Online Meeting!
有 \(n\) 个人,其中 \(m\) 对人互相认识。现在要让这 \(n\) 个人开一个会议,一开始只有一个人在会中,每个人可以把他认识的人拉进会议。有 \(k\) 个人在干活不能拉人。问能否把所有人都拉进会议,若能要给出方案。
选一个不在干活的人开始,然后bfs即可。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+5;
int n,m,k,x,y;
bool b[N],vis[N];
vector<int> e[N];
void adde(int x,int y){
e[x].push_back(y);
}
int main(){
cin>>n>>m>>k;
for(int i=1;i<=k;++i)cin>>x,b[x]=1;
for(int i=1;i<=m;++i)cin>>x>>y,adde(x,y),adde(y,x);
queue<int> q;
for(int i=1;i<=n;++i)if(!b[i]){q.push(i),vis[i]=1;break;}
vector<vector<int>> ans;
while(!q.empty()){
int u=q.front();
q.pop();
if(b[u])continue;
vector<int> c;
c.push_back(u);
for(int v:e[u])if(!vis[v])c.push_back(v),q.push(v),vis[v]=1;
if(c.size()>1)ans.push_back(c);
}
int cnt=0;
for(int i=1;i<=n;++i)if(!vis[i])++cnt;
if(cnt){cout<<"No\n";return 0;}
cout<<"Yes\n";
cout<<ans.size()<<'\n';
for(auto c:ans){
cout<<c[0]<<' '<<c.size()-1<<' ';
for(int i=1;i<c.size();++i)cout<<c[i]<<' ';
cout<<'\n';
}
}
K. Farm Management
有 \(n\) 种作物,第 \(i\) 种作物可以种 \(l_i\) 到 \(r_i\) 个单位,单位收益为 \(w_i\)。
你一共可以种 \(m\) 个单位的作物,且你可以选择至多一种作物解除限制。
求最大收益。
贪心。
先把所有作物按收益从大到小排序,枚举解除限制的作物(假设是第 \(i\) 种)。
两种情况,一种是完全不种第 \(i\) 种作物,一种是所有余量都种第 \(i\) 种作物。
第二种情况显然只能 \(i=1\),第一种情况需要枚举 \(i\) 并预处理 \((r_i-l_i)\) 和 \((r_i-l_i)w_i\) 的前缀和。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+5;
int n;
ll m;
struct node{
int l,r,w;
bool operator<(const node& p)const{
return w<p.w;
}
}a[N];
ll s[N],sum[N];
int main(){
cin>>n>>m;
for(int i=1;i<=n;++i)cin>>a[i].w>>a[i].l>>a[i].r;
sort(a+1,a+n+1);
ll now=0,sl=0;
for(int i=1;i<=n;++i)now+=1ll*a[i].w*a[i].l,sl+=a[i].l;
ll ans=now+1ll*a[n].w*(m-sl);
for(int i=1;i<=n;++i){
s[i]=s[i-1]+a[n-i+1].r-a[n-i+1].l;
sum[i]=sum[i-1]+1ll*a[n-i+1].w*(a[n-i+1].r-a[n-i+1].l);
}
for(int i=1;i<=n;++i){
int pos=upper_bound(s+1,s+n+1,m-sl+a[i].l)-s;
ans=max(ans,now-1ll*a[i].w*a[i].l+sum[pos-1]+1ll*a[n-pos+1].w*(m-sl+a[i].l-s[pos-1]));
}
cout<<ans<<'\n';
}
J. New Energy Vehicle
\(n\) 种汽油,\(m\) 个加油站,每个加油站只能加一种油,每种油都是一单位能走一公里,求最远能走多少公里。\(n,m\leq 10^5\)。
贪心,优先用下次出现最早的那种油。
set<pair<int,int>>
维护(下次出现位置,编号)即可。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N=1e5+5;
int n,m,a[N],c[N],x[N],t[N],fir[N],lst[N],nxt[N];
ll solve(){
cin>>n>>m;
for(int i=1;i<=n;++i)cin>>a[i],c[i]=a[i];
for(int i=1;i<=m;++i)cin>>x[i]>>t[i];
for(int i=1;i<=n;++i)fir[i]=m+1,lst[i]=0;
for(int i=1;i<=m;++i){
if(fir[t[i]]>m)fir[t[i]]=i;
if(lst[t[i]])nxt[lst[t[i]]]=i;
lst[t[i]]=i;
}
for(int i=1;i<=n;++i)if(lst[i])nxt[lst[i]]=m+1;
set<pii> s;
for(int i=1;i<=n;++i)s.insert(pii(fir[i],i));
ll sum=0;
for(int i=1;i<=m;++i){
int r=x[i]-x[i-1];
while(!s.empty()&&c[s.begin()->second]<=r){
int k=s.begin()->second;
sum+=c[k],r-=c[k],c[k]=0;
s.erase(s.begin());
}
if(s.empty()&&r>0)return sum;
c[s.begin()->second]-=r,sum+=r;
c[t[i]]=a[t[i]];
if(s.find(pii(i,t[i]))!=s.end())s.erase(pii(i,t[i]));
s.insert(pii(nxt[i],t[i]));
}
for(int i=1;i<=n;++i)sum+=c[i];
return sum;
}
int main(){
ios::sync_with_stdio(0);cin.tie(0);
int T;
cin>>T;
while(T--)cout<<solve()<<'\n';
}
A. Build a Computer
构造一个有限状态自动机,使其能且只能识别\([L,R]\)范围内的所有二进制串(无前导零)。
\(1\leq L\leq R\leq 10^6\),不能超过 100 个节点。
直接造 trie 树节点数多达 2e6,肯定不行。
注意到很多子树都是满二叉树,我们可以用一条01重边的链把这些满二叉树全都缩掉,即如果这个节点是满二叉树就直接连到链上。这样可以证明总点数不超过 \(5\log n\),正好在 100 的范围内。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N=105;
int L,R,c[N][2],tot,rt,m;
void dfs(int &x,int L,int R,int l,int r,int k){
if(l==L&&r==R){
m=max(m,k),x=-k;
return;
}
x=++tot;
int mid=(l+r)>>1;
if(L<=mid)dfs(c[x][0],L,min(R,mid),l,mid,k-1);
if(R>mid)dfs(c[x][1],max(L,mid+1),R,mid+1,r,k-1);
}
vector<pii> e[N];
void adde(int x,int y,int z){
e[x].push_back(pii(y,z));
}
bool del[N];
int id[N];
int ID(int x){return x>0?id[x]:-x;}
int main(){
cin>>L>>R;
dfs(rt,L,R,0,(1<<__lg(R)+1)-1,__lg(R)+2);
int cnt=m;
for(int i=m;i>1;--i)adde(i,i-1,0),adde(i,i-1,1);
int r=rt;
while(c[r][0])del[c[r][0]]=1,r=c[r][0];
for(int i=1;i<=tot;++i)if(!del[i])id[i]=++cnt;
r=rt;
while(r)adde(ID(rt),ID(c[r][1]),1),r=c[r][0];
for(int i=2;i<=tot;++i)if(!del[i]){
if(c[i][0]&&!del[c[i][0]])adde(id[i],ID(c[i][0]),0);
if(c[i][1])adde(id[i],ID(c[i][1]),1);
}
cout<<cnt<<'\n';
for(int i=1;i<=cnt;++i){
cout<<e[i].size()<<' ';
for(auto [x,y]:e[i])cout<<x<<' '<<y<<' ';
cout<<'\n';
}
}