Atcoder Beginner Contest 376

新猫
     Λ   Λ__
  /(*゚ー゚)/\
 /|  ̄U U ̄|\/
   |      |/

A.Candy Button \(\text{diff } 19\)

你按一次按钮就会得到一颗糖,如果这次按按钮和上次得到糖的间隔时间小于 \(C\) 则不会得到糖,给你若干按按钮的时间,问能得到多少糖

int n,c;
int a[1000001];
signed main(){
    cin>>n>>c;
    int tot=0;
    int last=-1;
    for(int i=1;i<=n;++i){
        cin>>a[i];
        if(last==-1 or a[i]-last>=c){
            tot++;
            last=a[i];
        }
    }
    cout<<tot<<endl;
}

B.Hands on Ring(Easy) \(\text{diff } 290\)

一个环上有 \(N\) 段,初始时左手在 \(1\),右手在 \(2\),手可以每次任意方向移动一步,但是左右手不能重叠,给你若干个操作,表示将一只手移到 \(p_i\)(另一只手不能动)的最小步数

罕见的把史放 T2 的 ABC

我选择分别对两个方向 dfs 一遍

int n,q;
int l=1,r=2;
inline int fixed(int x){
    while(x<1) x+=n;
    while(x>n) x-=n;
    return x;
}
int dfs(int now,bool isadd,bool isl,int step,int goal){
    if((isl and now==r) or (isl==false and now==l)) return 0x7fffffff;
    if(now==goal) return step;
    return dfs(fixed(now+(isadd?1:-1)),isadd,isl,step+1,goal);
}
char getch(){
    char ch=getchar();
    while(!(ch=='L' or ch=='R')) ch=getchar();
    return ch;
}
long long ans=0;
signed main(){
    cin>>n>>q;
    for(int i=1;i<=q;++i){
        char c=getch();int x;cin>>x;
        if(c=='L'){
            ans+=min(dfs(l,true,true,0,x),dfs(l,false,true,0,x));
            l=x;
        }
        else{
            ans+=min(dfs(r,true,false,0,x),dfs(r,false,false,0,x));
            r=x;
        }
    }
    cout<<ans<<endl;
}

C.Prepare Another Box \(\text{diff } 366\)

给你若干物品和盒子及其大小,一个盒子只能装一个大小不超过盒子大小的物品,并且你可以买若干个任意容量的盒子,问是否有办法使得所有玩具都装在盒子里,并且没有剩余的盒子,如果有,输出其最小花费

贪心地想,将所有容量从大到小排序,开双指针,如果能放就放,否则直接买,最后判断盒子是否用完即可

int n;
int a[1000001],b[1000001];
signed main(){
    cin>>n;
    for(int i=1;i<=n;++i){
        cin>>a[i];
    }
    for(int i=1;i<=n-1;++i){
        cin>>b[i];
    }
    sort(a+1,a+n+1,[](int x,int y){return x>y;});
    sort(b+1,b+n,[](int x,int y){return x>y;});
    int i=1,j=1,ans=0;
    while(i<=n){
        if(j>n-1){
            ans+=a[i];i++;
            continue;
        }
        if(a[i]<=b[j]){
            i++;j++;
            continue;
        }
        ans+=a[i];i++;
    }
    cout<<(j==n?ans:-1)<<endl;
}

D.Cycle \(\text{diff } 743\)

给定有向图,寻找包含 \(1\) 的最小环

\(N\le 2\times 10^5\)

好题

比较考察对 dij 的理解,包含 \(1\) 的最小环,可以看成找一条从 \(1\)\(1\) 的最短路

一般来说我们干这个的时候都是枚举起始点相邻的点,尝试断开连边跑最短路

但是可以发现,如果我们一开始将 \(dis_1\) 设为 \(inf\),然后对 \(1\) 跑最短路,也能有这个效果

那么你显然就不能用 \(dis_1\) 去做一开始的松弛了,否则你什么也松弛不到,因此你可以往优先队列里放一个 \(dis_1=0\),然后每次拿出优先队列里的值来松弛其他节点,这样也能做到断边的效果

#define int long long
int n,m;
vector<int>e[200001];
int dis[200001];
bool vis[200001];
struct node{
    int id,dis;
    bool operator <(const node&A)const{
        return dis>A.dis;
    }
};
priority_queue<node>q;
void dij(int s){
    memset(dis,0x3f,sizeof dis);
    q.push({s,0});
    while(!q.empty()){
        node u=q.top();q.pop();
        if(vis[u.id]) continue;
        vis[u.id]=true;
        for(int i:e[u.id]){
            if(dis[i]>u.dis+1){
                dis[i]=u.dis+1;
                q.push({i,dis[i]});
            }
        }
    }
}
signed main(){
    ios::sync_with_stdio(false);
    cin>>n>>m;
    for(int i=1;i<=m;++i){
        int x,y;cin>>x>>y;
        e[x].push_back(y);
    }
    dij(1);
    cout<<(dis[1]>=0x3f3f3f3f3f?-1:dis[1])<<endl;
}

E.Max×Sum \(\text{diff } 1063\)

给定两个长为 \(N\) 的数列 \(A,B\),从 \(N\) 中选 \(K\) 个数,最小化 \(\max\{A_i\}\times \sum\{B_i\}\) 的值

枚举 \(A_i\),钦定当前的 \(A_i\) 是最大值

就变成了从 \(A_j\) 不超过 \(A_i\)\(j\) 里求 \(b_j\) 的最小值

如果我们从小到大枚举 \(A_i\),则动态地每次维护 \(b_j\)\(K\) 小之和即可

可以用优先队列实现

#define int long long
int n,k;
int id[200001];
int a[200001],b[2000001];
priority_queue<int>q;
signed main(){
    ios::sync_with_stdio(false);
    int cases;cin>>cases;while(cases--){
        int ans=0x7fffffffffffffff;
        while(!q.empty()) q.pop();
        cin>>n>>k;
        for(int i=1;i<=n;++i){
            cin>>a[i];
        }
        for(int i=1;i<=n;++i){
            cin>>b[i];
        }
        iota(id+1,id+n+1,1);
        sort(id+1,id+n+1,[](int x,int y){return a[x]<a[y];});
        int tmp=0;
        for(int i=1;i<=n;++i){
            if(q.size()<k){
                tmp+=b[id[i]];q.push(b[id[i]]);
            }
            else if(q.top()>b[id[i]]){
                tmp+=b[id[i]]-q.top();
                q.pop();q.push(b[id[i]]);
            }
            if((int)q.size()<k) continue;
            ans=min(ans,a[id[i]]*tmp);
        }
        cout<<ans<<'\n';
    }
}

F.Hands on Ring(Hard) \(\text{diff } 2089\)

考虑 DP 实现

可以 DP 是因为,前一个操作已经帮我们确定了一个手的位置,因此我们可以设 \(f_{i,j}\) 表示进行到操作 \(i\) 的时候,另一只手的位置为 \(j\)

转移很水,直接分讨挪左手右手就行了,但是这个题的转移比 B 题沟史十倍

滚动数组压一下,或者直接压成一维也行

#include <bits/stdc++.h>
using namespace std;
const int inf=0x3f3f3f3f;
int n,q;
int dp[200001];
int old[200001];
void f(int s,int g,int x,int cnt){
	//从 s 到 g 的转移(cnt=f_{i-1})
	g=(g-s+n)%n;
	x=(x-s+n)%n;
	for(int j=0;j<2;++j){
		if(x<=g){
			int nt=(g+1)%n;
			if(j==1) nt=n-nt;
			dp[(nt+s)%n]=min(dp[(nt+s)%n],cnt+g+g+1-x);
		}
		else{
			int nt=x;
			if(j==1) nt=n-nt;
			dp[(nt+s)%n]=min(dp[(nt+s)%n],cnt+g);
		}
		g=n-g;
		x=n-x;
	}
};
int main(){
	ios::sync_with_stdio(false);
	cin>>n>>q;
	char lh='L';int lt=0;
	memset(dp,0x3f,sizeof dp);
	dp[1]=0;
	while(q--){
		char h;int t;
		cin>>h>>t;
		t--;
		swap(dp,old);
		memset(dp,0x3f,sizeof dp);
		for(int i=0;i<n;++i){
			if(old[i]!=inf){
				if(h==lh){
					f(lt,t,i,old[i]);
				}
				else{
					f(i,t,lt,old[i]);
				}
			}
		}
		lh=h;lt=t;
	}
	int ans=inf;
	for(int i=0;i<n;++i) ans=min(ans,dp[i]);
	cout<<ans<<endl;
}

posted @ 2024-10-20 12:04  HaneDaniko  阅读(53)  评论(2编辑  收藏  举报