zoukankan      html  css  js  c++  java
  • AtCoder Regular Contest 098

    AtCoder Regular Contest 098


    C - Attention

    题意

    给定一个只包含“E”,“W”字符串,可以花一的花费使他们互相转换。选定一个位置,使位置左边的字符都变成E,右边都变成W所需要的最小花费。

    分析

    这题纯粹是签到题,做两个前缀和然后直接加就可以了。

    #include <iostream>
    #include <cmath>
    #include <cstring>
    #include <cstdio>
    #include <algorithm>
    #include <queue>
    #define re register
    #define max(a,b) ((a)>(b)?(a):(b))
    #define min(a,b) ((a)<(b)?(a):(b))
    #define MAXN 300005
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    #define ms(arr) memset(arr, 0, sizeof(arr))
    const int inf = 0x3f3f3f3f;
    int s[MAXN],ans,n,w[MAXN],e[MAXN];
    char a[MAXN];
    int main() 
    {
    	cin>>n;
        for(re int i=1;i<=n;i++)
            cin>>a[i];
        for(re int i=1;i<=n;i++){
            if(a[i]=='W') w[i]=w[i-1]+1;
            else w[i]=w[i-1]; 
        }
        for(re int i=n;i>=1;i--){
            if(a[i]=='E') e[i]=e[i+1]+1;
            else e[i]=e[i+1];
        }
        ans=10000000;
        for(re int i=1;i<=n;i++)
            ans=min(ans,w[i]+e[i]-1);
        cout<<ans;
        return 0;
    }
    

    D - Xor Sum 2

    题意

    给你一个数列a,求出一共有多少个连续子序列,使$ a_l xor a_{l+1} xor ... xor a_{r-1} xor a_r=a_l+a_{l+1}+...+a_{r-1}+a_r$

    分析

    我们有一个很显然的结论,就是$ a_l xor a_{l+1} xor ... xor a_{r-1} xor a_r le a_l+a_{l+1}+...+a_{r-1}+a_r$

    那么我们可以发现答案具有二分性质,我们枚举左端点,然后二分寻找右端点最右是靠在哪里就可以了。

    实际上由于题目性质,我们也可以通过直接暴力寻找实现,可以证明对于每一个左端点最多向右伸展二十次。

    #include <iostream>
    #include <cmath>
    #include <string>
    #include <cstring>
    #include <cstdio>
    #include <algorithm>
    #define re register
    #define max(a,b) ((a)>(b)?(a):(b))
    #define min(a,b) ((a)<(b)?(a):(b))
    #define MAXN 4000007
    #define mo 19930726
    #define ll long long
    using namespace std;
    typedef unsigned long long ull;
    #define ms(arr) memset(arr, 0, sizeof(arr))
    const int inf = 0x3f3f3f3f;
    ll n,a[200001],sum[200001],sor[200001];
    int main()
    {
    	cin>>n;
    	for(re int i=1;i<=n;i++){
    		cin>>a[i];
    		sum[i]=sum[i-1]+a[i];
    		sor[i]=sor[i-1]^a[i];
    	}
    	ll ans=0;
    	for(re int i=1;i<=n;i++){
    		int l=i,r=n;
    		while(l<=r){
    			int mid=l+r>>1;
    			if(sum[mid]-sum[i-1]!=(sor[mid]^sor[i-1])) r=mid-1;
    			else l=mid+1;
    		}
    		ans+=l-i;
    	}
    	cout<<ans;
    }
    

    E - Range Minimum Queries

    题意

    给定一个数列,你每次可以从中选出一个长度为k的连续子序列,然后删掉其中最小的数。一共删除Q次,最小化删掉的所有数中,最大数与最小数的差。

    分析

    可以这么想,当我们确定了一个删除的最小的数之后,其余的删除的数肯定越小越好。

    我们可以枚举要删除的最小的数,然后比它更小的数全部设为不可删除,这样这个序列就被这些数分成了一段一段的。对于每一段,我们都尽量的删除最小的数,最后sort一下就可以求出了。

    复杂度看上去是(O(n^3log_2n))的,但实际上是远小于(O(n^2log_2n))的,故可以在时间允许范围内通过。

    #include <iostream>
    #include <cmath>
    #include <string>
    #include <cstring>
    #include <cstdio>
    #include <algorithm>
    #define re register
    #define max(a,b) ((a)>(b)?(a):(b))
    #define min(a,b) ((a)<(b)?(a):(b))
    #define MAXN 4000007
    #define mo 19930726
    using namespace std;
    typedef unsigned long long ull;
    #define ms(arr) memset(arr, 0, sizeof(arr))
    const int inf = 0x3f3f3f3f;
    int a[10001],b[10001],c[10001],tot;
    int n,k,q,ans=inf;
    inline void find(int l,int r)
    {
    	for(int i=l;i<=r;i++) b[i]=a[i];
    	sort(b+l,b+1+r);
    	for(int i=l;i<=r-k+1;i++) c[++tot]=b[i];
    }
    inline void solve(int x)
    {
    	int cut=1;tot=0;a[n+1]=-inf;
    	for(int i=1;i<=n+1;i++){
    		if(a[i]<x){
    			if(i-cut>=k) find(cut,i-1);
    			cut=i+1;
    		}
    	}
    	sort(c+1,c+tot+1);
    	if(tot<q) return;
    	ans=min(ans,c[q]-c[1]);
    }
    int main()
    {
    	cin>>n>>k>>q;
    	for(int i=1;i<=n;i++) cin>>a[i];
    	for(int i=1;i<=n;i++) solve(a[i]);
    	cout<<ans;
    }
    

    F - Donation

    题意

    大致好像是说,给出一个n个点,m条边的无向图,每个点有(a_i,b_i)两个权值。我们在到一个点的时候,可以捐献(b_i)的费用。同时我们需要保证。

    1.到达每一个点和离开每一个点时我们手中的费用要大于(a_i)
    2.初始时刻我们手中的费用要大于(a_s)

    求我们给所有点都捐献(b_i)所需要的最少的费用。

    分析

    注意我们经过一个点的时候只需要花费一次,而且可以在任意时刻花费。那么显然的是我们对于每个重复经过的点,使其在最后一次经过时缴纳费用是最优的。

    因此我们修改a的约束,建立数组c,使(c_i=max(a_i-b_i,0))表示我们在任意时刻经过了i这个点,手中一定至少要拥有(c_i)的钱数。

    我们可以贪心的想到先遍历(c_i)最大的点,那么删去这个点之后,我们就得到了若干个联通块。然后最优的方法明显是贡献了(c_i)之后进入了一个联通块之后就不再出来。

    然后我们一层一层的递归,用子树的根去连上一层的根。这样我们就可以构造一棵所有子树的(c_i)都要小于根节点的树。

    接下来可以直接DP,(f[x])表示x的子树符合条件的最小初始钱数,(s[x])表示x的子树的b值之和。

    #include <iostream>
    #include <cstdlib>
    #include <cmath>
    #include <string>
    #include <cstring>
    #include <cstdio>
    #include <algorithm>
    #define re register
    #define max(a,b) ((a)>(b)?(a):(b))
    #define min(a,b) ((a)<(b)?(a):(b))
    #define MAXN 100507
    #define mo 19930726
    #define ll long long
    using namespace std;
    typedef unsigned long long ull;
    #define ms(arr) memset(arr, 0, sizeof(arr))
    const int inf = 0x3f3f3f3f;
    int head[MAXN],fa[MAXN],a[MAXN],b[MAXN],c[MAXN],n,m,vis[MAXN],id[MAXN],cnt,num;
    ll s[MAXN],f[MAXN];
    struct po{
    	int nxt,to;
    }edge[MAXN<<1];
    vector<int> v[MAXN];
    int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
    inline bool cmp(int x,int y) {return c[x]<c[y];}
    inline void add_edge(int from,int to){
    	edge[++num].nxt=head[from];edge[num].to=to;head[from]=num;
    }
    void dfs(int u){
    	s[u]=b[u];
    	for(int i=head[u];i;i=edge[i].nxt){
    		int v=edge[i].to;
    		dfs(v);
    		s[u]+=s[v];
    	}
    	f[u]=s[u]+c[u];
    	for(int i=head[u];i;i=edge[i].nxt){
    		int v=edge[i].to;
    		f[u]=min(f[u],s[u]-s[v]+max(f[v],1ll*c[u]));
    	}
    }
    int main()
    {
    	cin>>n>>m;
    	for(int i=1;i<=n;i++){
    		cin>>a[i]>>b[i];
    		c[i]=max(0,a[i]-b[i]);
    		fa[i]=id[i]=i;
    	}
    	for(int i=1;i<=m;i++){
    		int x,y;
    		cin>>x>>y;
    		v[x].push_back(y); v[y].push_back(x);
    	}
    	sort(id+1,id+n+1,cmp);
    	for(int i=1;i<=n;i++){
    		int x=id[i];vis[x]=1;
    		for(int j=0;j<v[x].size();j++) {
    			int y=v[x][j];
    			if(!vis[y]) continue;
    			y=find(y);
    			if(x!=y){
    				fa[y]=x;add_edge(x,y);
    			}
    		}
    	}
    	dfs(id[n]);
    	cout<<f[id[n]];
    }
    
  • 相关阅读:
    jvm原理----------4.Java虚拟机何谓垃圾及垃圾回收算法
    jvm原理----------5.垃圾收集器及内存分配策略
    jvm原理----------6.创建对象及对象的访问定位
    mysql的sql语句的常用的优化方式
    jvm内存原理及调优(完全总结)
    dubbo的负载均衡与重试机制
    File类
    异常的真实应用
    字符串转换功能
    Object类介绍
  • 原文地址:https://www.cnblogs.com/victorique/p/9540356.html
Copyright © 2011-2022 走看看