《Codeforces Round #661 (Div. 3)》

A:读错题导致难度飙升。

这里题意是任意两个下标满足条件都能换,那就是个水题了

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef long double ld;
typedef pair<LL,int> pii;
const int N = 1e5+5;
const int M = 1e6+5;
const LL Mod = 998244353;
#define rg register
#define pi acos(-1)
#define INF 1e8
#define INM INT_MIN
#define dbg(ax) cout << "now this num is " << ax << endl;
inline int read()
{
    int x = 0,f = 1;char c = getchar();
    while(c < '0' || c > '9'){if(c == '-') f = -1;c = getchar();}
    while(c >= '0' && c <= '9'){x = (x<<1)+(x<<3)+(c^48);c = getchar();}
    return x*f;
}
int a[55],vis[105];
int main()
{
    int ca;ca = read();
    while(ca--)
    {
        int n;n = read();
        memset(vis,0,sizeof(vis));
        for(int i = 1;i <= n;++i)
        {
            a[i] = read();
            vis[a[i]]++;
        }
        sort(a+1,a+n+1);
        int f = 0;
        for(int i = 1;i < n;++i)
        {
            if(vis[a[i]+1] != 0) continue;
            if(vis[a[i]] > 1){vis[a[i]]--;continue;}
            f = 1;
        }
        if(f) printf("NO\n");
        else printf("YES\n");
    }
  //  system("pause");
    return 0;
}
View Code

B:水题

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef long double ld;
typedef pair<LL,int> pii;
const int N = 1e3+5;
const int M = 1e6+5;
const LL Mod = 998244353;
#define rg register
#define pi acos(-1)
#define INF 1e18
#define INM INT_MIN
#define dbg(ax) cout << "now this num is " << ax << endl;
inline LL read()
{
    LL x = 0,f = 1;char c = getchar();
    while(c < '0' || c > '9'){if(c == '-') f = -1;c = getchar();}
    while(c >= '0' && c <= '9'){x = (x<<1)+(x<<3)+(c^48);c = getchar();}
    return x*f;
}
LL a[55],b[55];
int main()
{
    int ca;ca = read();
    while(ca--)
    {
        int n;n = read();
        LL mi1 = INF,mi2 = INF;
        for(int i = 1;i <= n;++i) a[i] = read(),mi1 = min(mi1,a[i]);
        for(int i = 1;i <= n;++i) b[i] = read(),mi2 = min(mi2,b[i]);
        LL ans = 0; 
        for(int i = 1;i <= n;++i)
        {
            int dis = a[i]-mi1;
            ans += dis;
            int ma = b[i]-mi2;
            if(ma > dis) ans += ma-dis;   
       //     dbg(ans);
        }
        printf("%lld\n",ans);
    }
   // system("pause");
    return 0;
}
View Code

C:一开始想到二分,但发现不满足二分性质。

但是这也为解题提供了一个思路,可以发现组合的权值最多只有[1,100]的可能性,那么取枚举这个权值即可

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef long double ld;
typedef pair<LL,int> pii;
const int N = 1e3+5;
const int M = 1e6+5;
const LL Mod = 998244353;
#define rg register
#define pi acos(-1)
#define INF 1e18
#define INM INT_MIN
#define dbg(ax) cout << "now this num is " << ax << endl;
inline int read()
{
    int x = 0,f = 1;char c = getchar();
    while(c < '0' || c > '9'){if(c == '-') f = -1;c = getchar();}
    while(c >= '0' && c <= '9'){x = (x<<1)+(x<<3)+(c^48);c = getchar();}
    return x*f;
}
int w[55],n,vis[105],num[105];
int check(int x)
{
    for(int i = 0;i <= 100;++i) num[i] = vis[i];
    int ans = 0;
    for(int i = 1;i <= n;++i)
    {
        if(w[i] > x) continue;
        if(x-w[i] == w[i])
        {
            if(num[w[i]] > 1) num[w[i]] -= 2,ans++;
        }
        else if(num[x-w[i]] > 0 && num[w[i]] > 0)
        {
            num[x-w[i]]--;
            num[w[i]]--;
            ans++;
        }
    }
    return ans;
}
int main()
{
    int ca;ca = read();
    while(ca--)
    {
        n = read();
        memset(vis,0,sizeof(vis));
        for(int i = 1;i <= n;++i) w[i] = read(),vis[w[i]]++;
        int ans = 0;
        for(int i = 1;i <= 100;++i) ans = max(ans,check(i));
        printf("%d\n",ans);
    }
  //  system("pause");
    return 0;
}
View Code

D:感觉我想的稍微麻烦了。。能AC就是好题解(逃

可以发现对于每个1,我们要将后面的没有被接入的第一个0接入当前序列。(0也是同理)

这样显然是可以满足最小的子序列数的。

那么对于这个后面的最小位置,我们可以用线段树维护这个位置。

然后一个位置被接入,就去更新线段树。因为是单点更新,复杂度nlogn,也挺快了。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef long double ld;
typedef pair<LL,int> pii;
const int N = 2e5+5;
const int M = 1e6+5;
const LL Mod = 998244353;
#define rg register
#define pi acos(-1)
#define INF 1e8
#define INM INT_MIN
#define dbg(ax) cout << "now this num is " << ax << endl;
inline int read()
{
    int x = 0,f = 1;char c = getchar();
    while(c < '0' || c > '9'){if(c == '-') f = -1;c = getchar();}
    while(c >= '0' && c <= '9'){x = (x<<1)+(x<<3)+(c^48);c = getchar();}
    return x*f;
}
int ans[N];//0,1
char s[N];
struct Node{int L,r,mi;}node[2][N<<2];
void build(int L,int r,int idx,int id)//id-0,id-1
{
    node[id][idx].L = L,node[id][idx].r = r;
    if(L == r)
    {
        if(s[L] == '0' && id == 0 || s[L] == '1' && id == 1) node[id][idx].mi = L;
        else node[id][idx].mi = INF;
        return ;
    }
    int mid = (L+r)>>1;
    build(L,mid,idx<<1,id);
    build(mid+1,r,idx<<1|1,id);
    node[id][idx].mi = min(node[id][idx<<1].mi,node[id][idx<<1|1].mi);
}
void update(int x,int idx,int id)
{
    if(node[id][idx].L == node[id][idx].r)
    {
        node[id][idx].mi = INF;
        return ;
    }
    int mid = (node[id][idx].L + node[id][idx].r)>>1;
    if(mid >= x) update(x,idx<<1,id);
    else update(x,idx<<1|1,id);
    node[id][idx].mi = min(node[id][idx<<1].mi,node[id][idx<<1|1].mi);
}
int query_mi(int L,int r,int idx,int id)
{
    if(node[id][idx].L >= L && node[id][idx].r <= r) return node[id][idx].mi;
    int mid = (node[id][idx].L+node[id][idx].r)>>1,mi = INF;
    if(mid >= L) mi = min(mi,query_mi(L,r,idx<<1,id));
    if(mid < r) mi = min(mi,query_mi(L,r,idx<<1|1,id));
    return mi;
}
int main()
{
    int ca;ca = read();
    while(ca--)
    {
        int n;n = read();
        scanf("%s",s+1);
        build(1,n,1,0);
        build(1,n,1,1);
        int ma = 0;
        memset(ans,0,sizeof(ans));
        for(int i = 1;i <= n;++i)
        {
            if(ans[i] == 0) ans[i] = ++ma;
            int tmp;
            if(s[i] == '0') tmp = query_mi(i+1,n,1,1);
            else tmp = query_mi(i+1,n,1,0);
            if(tmp == INF) continue;
            ans[tmp] = ans[i];
            if(s[tmp] == '0') update(tmp,1,0);
            else update(tmp,1,1);
        }
        printf("%d\n",ma);
        for(int i = 1;i <= n;++i) printf("%d%c",ans[i],i == n ? '\n' : ' ');
    }
    //system("pause");
    return 0;
}
View Code

E1:读错题导致难度飙升(梅开二度,老笨蛋了)

注意的是,这里是要使总(root-leaves)路径权值和小于s。

那么,我们可以去记录路径被经过的次数,然后乘上这个路径的w-w/2,这就是让它折半一次的能对总权值减少的贡献。

那么显然我们每次都选最大的,就能在最少的次数内满足。

这里这个被经过次数很显然就是子节点数。堆来维护最大值即可

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef long double ld;
typedef pair<LL,int> pii;
const int N = 1e5+5;
const int M = 1e6+5;
const LL Mod = 998244353;
#define rg register
#define pi acos(-1)
#define INF 1e18
#define INM INT_MIN
#define dbg(ax) cout << "now this num is " << ax << endl;
inline LL read()
{
    LL x = 0,f = 1;char c = getchar();
    while(c < '0' || c > '9'){if(c == '-') f = -1;c = getchar();}
    while(c >= '0' && c <= '9'){x = (x<<1)+(x<<3)+(c^48);c = getchar();}
    return x*f;
}
struct Node{int to;LL dis;};
vector<Node> G[N];
int ssize[N];
LL sum = 0;
struct Point{
    LL cost,w,cnt;
    bool operator < (const Point &a)const{
        return cost < a.cost;
    }
};
priority_queue<Point> Q;
LL cal(LL w,int cnt)
{
    return 1LL*cnt*(w-w/2);
}
void dfs(int u,int fa,LL dis)
{
    if(G[u].size() == 1) ssize[u] = 1;
    for(auto v : G[u])
    {
        if(v.to == fa) continue;
        dfs(v.to,u,v.dis);
        ssize[u] += ssize[v.to];
    }
    if(dis != 0) 
    {
        //printf("dis is %lld u is %d ssize is %d\n",dis,u,ssize[u]);
        sum += 1LL*ssize[u]*dis;
        Q.push(Point{cal(dis,ssize[u]),dis,ssize[u]});
    }
}
int main()
{
    int ca;ca = read();
    while(ca--)
    {
        int n;LL s;
        n = read(),s = read();
        for(rg int i = 1;i <= n;++i) G[i].clear(),ssize[i] = 0;
        for(rg int i = 1;i < n;++i)
        {
            int u,v;LL w;
            u = read(),v = read(),w = read();
            G[u].push_back(Node{v,w});
            G[v].push_back(Node{u,w});
        }
        while(!Q.empty()) Q.pop();
        sum = 0;
        dfs(1,0,0);
        int ans = 0;
        while(!Q.empty() && sum > s)
        {
            ans++;
            Point q = Q.top();
            Q.pop();
            sum -= q.cost;
            q.w /= 2;
            q.cost = cal(q.w,q.cnt);
            Q.push(q);
        }
        printf("%d\n",ans);
    }
   // system("pause");
    return 0;
}
View Code

E2:最小化花费了,可以用二分来。

因为c只有1,2,那么我们可以去维护两个堆。

一个维护1,一个维护2。开两个前缀和数组

用数组1来维护花费代价为i时能减少的最大权值。

数组2来维护花费代价为2*i时能减少的最大权值。

我们枚举1的花费,然后去二分最小的2代价使得sum-两个前缀和数组和 <= s

memset初始化数组T了,其实没必要,只要让第0个为0即可(改了之后快的飞起~~芜湖)

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef long double ld;
typedef pair<LL,int> pii;
const int N = 1e5+5;
const int M = 2e6+5;
const LL Mod = 998244353;
#define rg register
#define pi acos(-1)
#define INF 1e18
#define INM INT_MIN
#define dbg(ax) cout << "now this num is " << ax << endl;
inline LL read()
{
    LL x = 0,f = 1;char c = getchar();
    while(c < '0' || c > '9'){if(c == '-') f = -1;c = getchar();}
    while(c >= '0' && c <= '9'){x = (x<<1)+(x<<3)+(c^48);c = getchar();}
    return x*f;
}
struct Node{int to;LL dis,cost;};
vector<Node> G[N];
struct Point{
    int cnt;LL w,cost;
    bool operator < (const Point &a)const{
        return cost < a.cost;
    }
};
LL cal(LL w,int cnt)
{
    return 1LL*cnt*(w-w/2);
}
priority_queue<Point> Q1,Q2;
int ssize[N];
LL sum = 0,pre1[M],pre2[M],s;
void dfs(int u,int fa,LL dis,int c)
{
    if(G[u].size() == 1) ssize[u] = 1;
    for(auto v : G[u])
    {
        if(v.to == fa) continue;
        dfs(v.to,u,v.dis,v.cost);
        ssize[u] += ssize[v.to];
    }
    if(dis != 0)
    {
        sum += 1LL*ssize[u]*dis;
        if(c == 1) Q1.push(Point{ssize[u],dis,cal(dis,ssize[u])});
        else Q2.push(Point{ssize[u],dis,cal(dis,ssize[u])});
    }
}
bool check(LL tmp,int x)
{
    return (tmp-pre2[x]) <= s;
}
int main()
{
    int ca;ca = read();
    while(ca--)
    {
        int n;
        n = read(),s = read();
        for(int i = 1;i <= n;++i) G[i].clear(),ssize[i] = 0;
        for(rg int i = 1;i < n;++i)
        {
            int u,v,w,c;
            u = read(),v = read(),w = read(),c = read();
            G[u].push_back(Node{v,w,c});
            G[v].push_back(Node{u,w,c});
        }
        while(!Q1.empty()) Q1.pop();
        while(!Q2.empty()) Q2.pop();
        pre1[0] = pre2[0] = sum = 0;
        dfs(1,0,0,0);
        int cnt1 = 0,cnt2 = 0;
        while(!Q1.empty() && sum-pre1[cnt1] > s)
        {
            Point q = Q1.top();Q1.pop();
            ++cnt1;
            pre1[cnt1] = pre1[cnt1-1]+q.cost;
            q.w /= 2;
            q.cost = cal(q.w,q.cnt);
            if(q.cost != 0) Q1.push(q);
        }
        while(!Q2.empty() && sum-pre2[cnt2] > s)
        {
            Point q = Q2.top();Q2.pop();
            ++cnt2;
            pre2[cnt2] = pre2[cnt2-1]+q.cost;
            q.w /= 2;
            q.cost = cal(q.w,q.cnt);
            if(q.cost != 0) Q2.push(q);
        }
        LL ans = INF;
        for(int i = 0;i <= cnt1;++i)
        {
            LL ma = sum-pre1[i];
            int L = 0,r = cnt2,ta = 1e8;
            while(L <= r)
            {
                int mid = (L+r)>>1;
                if(check(ma,mid))
                {
                    ta = mid;
                    r = mid-1;
                } 
                else L = mid+1;
            }
            ans = min(ans,1LL*i+ta*2);
        }
        printf("%lld\n",ans);
    }
    //system("pause");
    return 0;
}
View Code

F:区间dp的思想

我们可以先处理出每个区间内部能嵌套的最多区间数,然后就找到了这个区间的最大权值。

既然要去找内部的嵌套,那么显然满足内部区间的长度比该区间小,所以我们可以按长度排序。

然后dp,因为已经按长度排序了,所以dp时肯定内部的权值已经处理好了。

具体的dp:

如果内部有个区间,且这个区间前面没有可以和它连接的,那么直接dp转移就行。

如果有相连的区间,我们可以让dp时一直传递值。

那么由于这里很特殊的一个地方,如果两个连续的区间,他们相接的地方重合,那么这两个区间是无法相连的。

所以我们对于当前区间,从p[i.]L-1]来转移,就不会重叠了

我们把满足条件的内部区间放入,然后当前位置满足由区间为右端点,就从左端点转移。

数据过大,先离散化。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef long double ld;
typedef pair<LL,int> pii;
const int N = 2e5+5;
const int M = 2e6+5;
const LL Mod = 998244353;
#define rg register
#define pi acos(-1)
#define INF 1e18
#define INM INT_MIN
#define dbg(ax) cout << "now this num is " << ax << endl;
inline int read()
{
    int x = 0,f = 1;char c = getchar();
    while(c < '0' || c > '9'){if(c == '-') f = -1;c = getchar();}
    while(c >= '0' && c <= '9'){x = (x<<1)+(x<<3)+(c^48);c = getchar();}
    return x*f;
}
struct Node{int L,r,len,id;}p[N];
bool cmp(Node a,Node b){return a.len < b.len;}
int a[N<<1],n,cnt;
LL dp[N],f[N];//到i号
vector<Node> G[N];
void slove()
{
    p[n+1] = {1,cnt+1,cnt+1,n+1};
    sort(p+1,p+n+2,cmp);
    for(int i = 1;i <= n+1;++i)
    {
        for(int j = p[i].L-1;j <= p[i].r;++j) G[j].clear(),dp[j] = 0;
        for(int j = 1;j < i;++j)
        {
            if(p[j].L >= p[i].L && p[j].r <= p[i].r) G[p[j].r].push_back(p[j]);
        }
        for(int j = p[i].L;j <= p[i].r;++j)
        {
            dp[j] = dp[j-1];//先继承
            for(auto t : G[j])//如果有线段的右边界为它
            {
                dp[j] = max(dp[j],dp[t.L-1]+f[t.id]);
            }
        }
        f[p[i].id] = dp[p[i].r]+1;
    }
    printf("%lld\n",f[n+1]-1);
}
int main()
{
    int ca;ca = read();
    while(ca--)
    {
        memset(f,0,sizeof(f));
        n = read();
        cnt = 0;
        for(int i = 1;i <= n;++i)
        {
            p[i].L = read(),p[i].r = read(),p[i].id = i;
            a[++cnt] = p[i].L,a[++cnt] = p[i].r;
        }
        sort(a+1,a+cnt+1);
        cnt = unique(a+1,a+cnt+1)-a-1;
        for(int i = 1;i <= n;++i)
        {
            p[i].L = lower_bound(a+1,a+cnt+1,p[i].L)-a;
            p[i].r = lower_bound(a+1,a+cnt+1,p[i].r)-a;
            p[i].len = p[i].r-p[i].L;
        }
        slove();
    }
    system("pause");
    return 0;
}

/*
4
5
1 5
2 3
2 5
3 5
2 2

*/
View Code

 

posted @ 2020-08-13 16:08  levill  阅读(108)  评论(0)    收藏  举报