\(E. Don’t Really Like How The Story Ends\)
\(dfs\)的时候如果一个点没有子节点,可以回溯,在父节点上找后续节点。如果找到了一个更大的节点,那么必须要与当前目标连接,然后回溯回来的时候如果目标值比当前的值更小说明仍有后续节点可以用,必须还在这个节点接后续节点。
int ned=2,ans=0;
vector<int>G[N];
void dfs(int u,int n){
if(u>n)return;
for (auto v:G[u]){
if(v<ned)continue;
while(v>=ned){
if(v==ned){
ned++;
dfs(ned-1,n);
}else{
ned++;
ans++;
dfs(ned-1,n);
}
}
}
}
void solve(){
int n=read(),m=read();
ned=2,ans=0;
for(int i=1;i<=n;i++){
G[i].clear();
}
for(int i=1;i<=m;i++){
int x=read(),y=read();
G[y].push_back(x);
G[x].push_back(y);
}
G[1].push_back(n+1);
for(int i=1;i<=n;i++){
sort(G[i].begin(),G[i].end());
}
dfs(1,n);
cout<<ans<<'\n';
}
\(I. Monster Hunter\)
考虑二分答案,那么就是找当前的次数如何分配能把数列尽量删掉。贪心策略是这样的:如果有 \(3\) ,对于血量大于 \(3\) 的奇数先填进去一个 \(3\) 把它变成偶数,如果 \(3\) 还有多,对于所有大于 \(6\) 的数尽量把所有的 \(6\) 的倍数的区域填掉(以填 \(2\) 个 \(3\) 进去的方式),这时候如果还有多的 \(3\),血量只有 \(1,2,4\) 这三种情况,贪心把血量最多的填掉,这样溢出是最少的。
int pre[N][4],n,m,a[N],h[N],th[N];
bool cmp(int x,int y){
return x>y;
}
bool check(int x) {
int cnt1=x/n*pre[n][1]+pre[x%n][1];
int cnt2=x/n*pre[n][2]+pre[x%n][2];
int cnt3=x/n*pre[n][3]+pre[x%n][3];
for(int i=1;i<=m;i++){
th[i]=h[i];
}
for(int i=1;i<=m;i++){
if(th[i]>=3&&th[i]%2==1&&cnt3>=1){
th[i]-=3;
cnt3--;
}
if(cnt3<=0)break;
}
for(int i=1;i<=m;i++){
if(th[i]>=6&&cnt3>=2){
int tmp=min(th[i]/6,cnt3/2);
th[i]-=6*tmp;
cnt3-=2*tmp;
}
if(cnt3<=0)break;
}
if(cnt3>0){
sort(th+1,th+1+m,cmp);
for(int i=1;i<=m;i++){
if(th[i]>=3&&cnt3>=1){
th[i]-=3;
cnt3--;
}
if(cnt3<=0)break;
}
sort(th+1,th+1+m,cmp);
for(int i=1;i<=m;i++){
if(th[i]>0&&cnt3>=1){
th[i]=0;
cnt3--;
}
if(cnt3<=0)break;
}
}
for(int i=1;i<=m;i++){
if(th[i]>=2&&cnt2>=1){
int tmp=min(th[i]/2,cnt2);
th[i]-=2*tmp;
cnt2-=tmp;
}
if(cnt2<=0)break;
}
if(cnt2>0){
sort(th+1,th+1+m,cmp);
for(int i=1;i<=m;i++){
if(th[i]>0&&cnt2>=1){
th[i]=0;
cnt2--;
}
if(cnt2<=0)break;
}
}
int had=0;
for(int i=1;i<=m;i++){
had+=max(0ll,th[i]);
}
return had<=cnt1;
}
int bs1(int l, int r){ //左偏二分
while (l < r){
int mid = (l + r) >> 1;
if (check(mid)) r = mid;
else l = mid + 1;
}
return l;
}
void solve(){
n=read();
for(int i=1;i<=n;i++){
a[i]=read();
for(int j=1;j<=3;j++){
pre[i][j]=pre[i-1][j];
}
pre[i][a[i]]++;
}
m=read();
for(int i=1;i<=m;i++){
h[i]=read();
}
cout<<bs1(1,100000000000000)<<'\n';
}
\(J. Ants\)
碰撞可以看作为穿过。那么显然的在几个(可能0)完整周期(周期就是每个蚂蚁走完两个全程)后,墙会破。每个周期两个墙的被撞次数都为 \(n\) 次。直接计算完整周期,对于剩余部分模拟。模拟的时候用优先队列储存可达时间,每次取两个队列中更早到达的处理。
void solve(){
int n=read(),x=read(),y=read();
vector<int>a(n+1),b(n+1);
for(int i=1;i<=n;i++){
a[i]=read();
}
for(int i=1;i<=n;i++){
b[i]=read();
}
int ans=min(x,y)/n*((mod-7)*2+2);
int bc=min(x,y)/n*n;
x-=bc;
y-=bc;
priority_queue<int,vector<int>,greater<int> >ql,qr;
for(int i=1;i<=n;i++){
if(b[i])qr.push(ans+(mod-7)-a[i]+1);
else ql.push(ans+a[i]);
}
while(ql.size()||qr.size()){
if(ql.size()){
int t=ql.top();
ql.pop();
ans=max(ans,t);
if(x){
qr.push(t+(mod-7)+1);
x--;
}
}
if(qr.size()){
int t=qr.top();
qr.pop();
ans=max(ans,t);
if(y){
ql.push(t+(mod-7)+1);
y--;
}
}
}
cout<<ans<<'\n';
}
- Programming Provincial Collegiate Sichuan Contestprogramming provincial collegiate sichuan programming collegiate provincial contest programming collegiate provincial shandong programming collegiate provincial guangdong programming provincial collegiate shandong programming collegiate provincial counting programming provincial collegiate sponsored 2023 programming collegiate provincial 题解programming collegiate provincial programming collegiate jiangsu contest