1. 程式人生 > >AtCoder Regular Contest 098

AtCoder Regular Contest 098

實現 中一 最大數 num mem 最小數 答案 當我 gist

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^3\log_2n)\)的,但實際上是遠小於\(O(n^2\log_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]];
}

AtCoder Regular Contest 098