Codeforces Round #407 (Div. 1) 题解

19 篇文章 0 订阅
19 篇文章 0 订阅

A:

给定长度为 n 的数组a,定义函数 f ,对于1l<rn
f(l,r)=r1i=l|a[i]a[i+1]|(1)il
求所有 f 函数中的最大值
2n105,|ai|109

solution A:

a 中的元素差分后取绝对值,即Δa[i]=|a[i+1]a[i]|
f 函数的公式等价于,找到起点l和终点 r [l,r]中,
标号和 l 奇偶性相同的贡献为正,否则为负
那么终点r的奇偶性肯定和起点 l 相同
采用分类讨论的思想,先只考虑起点为奇数位置的情况
那么将{Δa}中所有偶数位置乘上 1 然后处理一遍前缀和
从左往右枚举元素,如果是奇数位置,找到它左边前缀和最小的偶数位置减一下就行了

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<algorithm>
#include<cmath>
#include<stack>
#define min(a,b) ((a) < (b) ? (a) : (b))
#define max(a,b) ((a) > (b) ? (a) : (b))
using namespace std;

typedef long long LL;
const int maxn = 1E5 + 10;

int n,A[maxn];
LL Ans,Min,B[maxn];

int main()
{
    #ifdef DMC
        freopen("DMC.txt","r",stdin);
    #endif

    cin >> n;
    for (int i = 1; i <= n; i++) scanf("%d",&A[i]);
    for (int i = 1; i < n; i++) A[i] = abs(A[i + 1] - A[i]);
    for (int i = 1; i < n; i++)
        B[i] = (i & 1) ? A[i] : -A[i],B[i] += B[i - 1];
    for (int i = 1; i < n; i++)
        if (i & 1) Ans = max(Ans,B[i] - Min);
        else Min = min(Min,B[i]);
    Min = 0;
    for (int i = 1; i < n; i++)
        B[i] = (i & 1) ? -A[i] : A[i],B[i] += B[i - 1];
    for (int i = 1; i < n; i++)
        if (i & 1) Min = min(Min,B[i]);
        else Ans = max(Ans,B[i] - Min);
    cout << Ans << endl;
    return 0;
}

B:

Igor想在自己的国家来一次旅行,他的国家可以抽象成一张 n 个点m条边的无向图
我们称一条旅行路线是好的,当这条路线经过了 m2 条边每条边两次
且剩下的 2 条边每条边恰好被经过一次
你需要统计对于这张图,一共可以有多少条好的路线存在
如果图中不存在任何一条好的路径,那么输出1
1n,m106 保证图中不存在重边,每个点最多存在一条自环边

solution B:

对于每一条合法的旅行路线,不妨将经过两次的 m2 条边都增加为两条边
这样一条合法的旅行路线就变成了一条经过每条边恰好一次的欧拉路径
欧拉路径存在的充要条件是,要么所有点都是偶点,要么恰好有两个奇点
那么对选择的边分类讨论
如果选择了两条普通的边,那么就造出了四个奇点,不合法
如果选择了两条普通边,且这两条边有一个公共点,那就是恰好两个奇点,合法
如果选择的边有一条是自环边,因为自环边选择时对点的度数并不影响,故可以忽略它
注意小心讨论解为 1 的情况,容易出事故

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<algorithm>
#include<cmath>
#include<stack>
using namespace std;

typedef long long LL;
const int maxn = 1E6 + 10;

int n,m,N,M,du[maxn],vis[maxn],D[maxn];
LL Ans;

queue <int> Q;
vector <int> v[maxn];

int main()
{
    #ifdef DMC
        freopen("DMC.txt","r",stdin);
    #endif

    cin >> n >> m;
    for (int i = 1; i <= m; i++)
    {
        int x,y; scanf("%d%d",&x,&y);
        v[x].push_back(y); v[y].push_back(x);
        ++D[x]; ++D[y];
        if (x == y) ++M;
        else ++N,++du[x],++du[y];
    }
    for (int i = 1; i <= n; i++)
        if (D[i]) {vis[i] = 1; Q.push(i); break;}
    while (!Q.empty())
    {
        int k = Q.front(); Q.pop();
        for (int i = 0; i < v[k].size(); i++)
        {
            int to = v[k][i];
            if (vis[to]) continue;
            vis[to] = 1; Q.push(to);
        }
    }
    for (int i = 1; i <= n; i++)
        if (!vis[i] && D[i]) {cout << 0 << endl; return 0;}
    for (int i = 1; i <= n; i++)
        Ans += 1LL * du[i] * (du[i] - 1) / 2LL;
    Ans += 1LL * N * M;
    Ans += 1LL * M * (M - 1) / 2LL;
    cout << Ans << endl;
    return 0;
}

C:

Sasha和Kolya想要喝可乐。现在,他们有 k 种不同的可乐
其中,第i种可乐的 CO2 含量为 ai1000
他们想要调出一杯 CO2 含量恰好为 n1000 的可乐
假设两杯可乐混合时 CO2 含量累加,水的体积也累加
如,浓度为 3001000 5001000 的可乐混合后浓度为 300+5001000+1000=4001000
每种可乐都有无限杯,且它们的初始体积都是相同的
询问为了调出目标可乐最少需要多少杯已有的可乐,如果无解输出 1
0ai,n10001k106

solution C:

以下为了讨论方便,自动忽略没有用的分母 1000
假设目标为 n 对于每种可乐,令bi=ain
那么对于一种成功调配的方案,一定有 bi=0
且对于每一种合法调配的方案,显然能找到一种添加可乐的顺序
使得任意时刻,当前可乐的浓度的绝对值总是不超过 1000
证明很简单,由于 |bi|1000
只需要每次只加一种符号的可乐,当临界的时候换一种符号就行
因为绝对值的限制,不可能换了符号后马上溢出的
所以只需要储存绝对值不超过 1000 的状态,然后一个BFS就行了

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<stack>
#include<bitset>
#include<ext/pb_ds/priority_queue.hpp>
using namespace std;

const int N = 1000;
const int maxn = 2001;

int n,m,tp,stk[maxn],dis[maxn];
bool vis[maxn];

queue <int> Q;

int main()
{
    #ifdef DMC
        freopen("DMC.txt","r",stdin);
    #endif

    cin >> n >> m;
    while (m--)
    {
        int x; scanf("%d",&x); x -= n;
        if (vis[x + N]) continue;
        vis[x + N] = 1; stk[++tp] = x;
    }
    Q.push(N);
    while (!Q.empty())
    {
        int k = Q.front(); Q.pop();
        for (int i = 1; i <= tp; i++)
        {
            int to = k + stk[i];
            if (to >= maxn || to < 0 || dis[to]) continue;
            if (to == N) {cout << dis[k] + 1 << endl; return 0;}
            dis[to] = dis[k] + 1; Q.push(to);
        }
    }
    cout << -1 << endl;
    return 0;
}

D:

Roma想要在自己的家乡进行一次旅行,他的家乡可以看做一个笛卡尔坐标系
在这里,每条主要街道都可以表示成一条与 x 轴或者y轴平行的直线
Roma想要走遍家乡的所有主要街道,但不幸的是,Roma忘了这些街道的具体坐标
为了帮助Roma,他的叔叔Anton决定给他提供一些援助
具体的,Roma每次可以给出一个坐标为整数的点,
然后Anton会告诉他,离这个点最近的那条直线到这个点的距离是多少
由于某些原因,Anton最多只能提供 3105 次援助
请你在有限的询问内,确定坐标系上每一条直线的具体坐标
保证每条直线的坐标的绝对值不超过 108 ,所以你的询问的坐标也不能超过这个限制
假设一共有 n 条与y轴平行的直线, m 条与x轴平行的直线
保证 1n,m104

solution D:

显然所有主要街道都与直线 y=x 相交,那么考虑每个在 y=x 上的点
定义函数 Query(x,y) 返回询问点 (x,y) 的答案
显然,若 Query(x,x)=0 ,则必有直线经过点 (x,x)
那么,先找出所有有直线经过,且在直线 y=x 上的点
定义函数 Solve(l,r) 能找到所有坐标属于 [l,r] 的点
mid=l+r2d=Query(mid,mid)
那么递归 Solve(l,mid1),Solve(mid+1,r) 即可
否则,距离 mid d 处必定存在我们想要的点
所以递归Solve(l,midd),Solve(mid+d,r)即可
注意到,每次调用函数,要么我们找到了一个点,要么我们确定了其中一个端点是这样的点
所以函数调用的次数是 O(n)
现在找到了所有有直线经过的类似 (k,k) 的点
考虑如何确定经过它的直线是 y=k 还是 x=k
假设是 y=k ,那么我们随机查询一个点 Query(k,h)
如果它的返回值非0,那么肯定不是这条直线了,否则,我们有 104 的概率随机到直线 x=h
多随机几次就稳了~

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<algorithm>
#include<cmath>
#include<stack>
using namespace std;

typedef unsigned int u32;
const u32 g = 16;
const u32 Mod = 200000001;
const int maxn = 1E4 + 10;
const int INF = 100000000;

int n,m,tp,lx[maxn],ly[maxn],stk[maxn * 2];

int N = 1,M = 2,X[10],Y[10];

inline int Query(int x,int y)
{
    printf("0 %d %d\n",x,y); fflush(stdout);
    int ret; scanf("%d",&ret); return ret;
    //int ret = INF;
    //for (int i = 1; i <= N; i++) ret = min(ret,abs(x - X[i]));
    //for (int i = 1; i <= M; i++) ret = min(ret,abs(y - Y[i]));
    //return ret;
}

inline void Dfs(int l,int r)
{
    if (l > r) return;
    int mid = l + r >> 1;
    int ret = Query(mid,mid);
    if (ret == 0) stk[++tp] = mid,++ret;
    Dfs(l,mid - ret); Dfs(mid + ret,r);
}

inline int Get_rand()
{
    return ((u32)(rand()) << g) % Mod - INF;
}

inline bool Check_x(int k)
{
    for (int i = 0; i < 3; i++)
    {
        int pos = Get_rand();
        int ret = Query(k,pos);
        if (ret != 0) return 0;
    }
    return 1;
}

inline bool Check_y(int k)
{
    for (int i = 0; i < 3; i++)
    {
        int pos = Get_rand();
        int ret = Query(pos,k);
        if (ret != 0) return 0;
    }
    return 1;
}

int main()
{
    #ifdef DMC
        freopen("DMC.txt","r",stdin);
    #endif

    //X[1] = 2; Y[1] = 0; Y[2] = -3;
    srand(1004535809); Dfs(-INF,INF);
    for (int i = 1; i <= tp; i++)
    {
        if (Check_x(stk[i])) lx[++n] = stk[i];
        if (Check_y(stk[i])) ly[++m] = stk[i];
    }
    printf("1 %d %d\n",n,m);
    for (int i = 1; i < n; i++) printf("%d ",lx[i]);
    cout << lx[n] << endl;
    for (int i = 1; i < m; i++) printf("%d ",ly[i]);
    cout << ly[m] << endl;
    fflush(stdout);
    return 0;
}

E:

n 个战士排成一行,第i个战士有一个熟练度 ai
现在需要这些战士组成一些队伍,每个队伍必须恰好 5 人,设这五人的编号分别为i,j,k,l,p
需满足 1i<j<k<l<pn,aiaj=ak=alap
我们称中间的三位战士为玩家,边上的两位为助手
由于某些原因,某个时刻,一个战士不能在任何分组里充当玩家的角色
某个时刻,一个本来不能在任何分组里充当玩家的战士又变得可以了
不过,任何时刻,一个战士总是可以在任何分组里充当助手的角色
现在有 m 次修改,每次修改一个战士的状态,
每次修改后你需要回答当前所有战士能组成的队伍的数量
1n,m105,1ai109 答案对 109+7 取模

solution E:

首先将所有战士的熟练度离散化
预处理 Lefti 为战士 i 左侧熟练度不大于他的战士的数量
类似地预处理Righti
先不考虑修改操作,考虑如何算出初始答案
对于每个战士 i ,当他作为三位玩家中间的那一位的时候
对答案的贡献为Leftk{k<i,ak=ai}Righth{i<h,ai=ah}
加上修改的话,因为所有战士总是能当助手的,
所以只需要考虑熟练度相同的战士充当玩家的组数就行了
对于每一类熟练度的战士,用一棵splay维护他们组成的序列
根据这些战士在这个序列中从左往右数的编号,给他们每人一个新的编号 idi
考虑战士 i 被修改为不能充当玩家的情况
对于所有让该战士充当中间玩家的方案,直接调用上面的公式就行了
对于所有让该战士充当最左边那个玩家的方案,枚举哪个战士站在最右边
此时方案数为Lefti(idkidi)Rightk{i<k}
两个 id 的差值就是能充当中间战士的战士数量
把求和的式子拆开,即 idkRightkidiRightk
于是,我们只需要在splay中维护 idkRightk Rightk 就行了
对于所有让该战士充当最右边玩家的方案的求法类似
于是我们每次只需要统计方案的差值,然后在对应的splay中插入/删除一个点就行了
但是splay内插入/删除一个节点好像是非常慢的。。。
也可能是我写的太丑了么。。。?
一个不错的优化,将所有的插入/删除操作改为对一个点标记为存在/不存在

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<algorithm>
#include<cmath>
#include<stack>
#include<ctime>
#define max(a,b) ((a) > (b) ? (a) : (b))
using namespace std;

const int maxn = 2E5 + 20;
typedef long long LL;
const LL mo = 1000000007;

int n,m,Ans,cur = 1,A[maxn],B[maxn],rt[maxn],ch[maxn][2],fa[maxn],siz[maxn],
    key[maxn],L[maxn],R[maxn],sl0[maxn],sl1[maxn],sr0[maxn],sr1[maxn],c[maxn]
    ,SL[maxn],SR[maxn],sum[maxn];
vector <int> v[maxn];

inline int Mul(const LL &x,const LL &y) {return x * y % mo;}
inline int Add(const int &x,const int &y) {return x + y < mo ? x + y : x + y - mo;}
inline int Dec(const int &x,const int &y) {return x - y >= 0 ? x - y : x - y + mo;}

inline int getint()
{
    char ch = getchar(); int ret = 0;
    while (ch < '0' || '9' < ch) ch = getchar();
    while ('0' <= ch && ch <= '9')
        ret = ret * 10 + ch - '0',ch = getchar();
    return ret;
}

inline void maintain(int x)
{
    int lc = ch[x][0],rc = ch[x][1],pos = siz[lc] + key[x];
    siz[x] = Add(key[x],Add(siz[lc],siz[rc]));
    sl0[x] = Add(key[x] ? L[x] : 0,Add(sl0[lc],sl0[rc]));
    sr0[x] = Add(key[x] ? R[x] : 0,Add(sr0[lc],sr0[rc]));
    sl1[x] = Add(Mul(key[x] ? L[x] : 0,pos),sl1[lc]);
    sl1[x] = Add(sl1[x],Add(sl1[rc],Mul(pos,sl0[rc])));
    sr1[x] = Add(Mul(key[x] ? R[x] : 0,pos),sr1[lc]);
    sr1[x] = Add(sr1[x],Add(sr1[rc],Mul(pos,sr0[rc])));
}

inline int Build(int col,int l,int r)
{
    if (l > r) return 0;
    int mid = l + r >> 1,ret = v[col][mid];
    ch[ret][0] = Build(col,l,mid - 1);
    ch[ret][1] = Build(col,mid + 1,r);
    if (ch[ret][0]) fa[ch[ret][0]] = ret;
    if (ch[ret][1]) fa[ch[ret][1]] = ret;
    maintain(ret); return ret;
}

inline void rotate(int x)
{
    int y = fa[x],z = fa[y];
    int d = ch[y][0] == x ? 0 : 1;
    ch[y][d] = ch[x][d^1]; maintain(y);
    ch[x][d^1] = y; maintain(x); fa[y] = x;
    fa[x] = z; if (ch[y][d]) fa[ch[y][d]] = y;
    if (z) ch[z][ch[z][1] == y] = x,maintain(z);
}

inline void splay(int x,int col)
{
    for (int y = fa[x]; y; rotate(x),y = fa[x])
        if (fa[y]) rotate((ch[y][0] == x) ^ (ch[fa[y]][0] == y) ? x : y);
    rt[col] = x;
}

void Pre_Work()
{
    for (int i = 1; i <= n; i++) A[i] = B[i] = getint();
    sort(B + 1,B + n + 1);
    for (int i = 2; i <= n; i++)
        if (B[i] != B[i - 1]) B[++cur] = B[i];
    for (int i = 1; i <= cur; i++) v[i].push_back(i);
    for (int i = 1; i <= n; i++)
    {
        A[i + n] = lower_bound(B + 1,B + cur + 1,A[i]) - B;
        v[A[i + n]].push_back(i + n); siz[i + n] = 1;
    }
    for (int i = n + 1; i <= 2 * n; i++)
    {
        for (int j = A[i]; j > 0; j -= j&-j) L[i] += c[j];
        for (int j = A[i]; j <= cur; j += j&-j) ++c[j];
    }
    memset(c,0,sizeof(c));
    for (int i = 2 * n; i > n; i--)
    {
        for (int j = A[i]; j > 0; j -= j&-j) R[i] += c[j];
        for (int j = A[i]; j <= cur; j += j&-j) ++c[j];
    }
    for (int i = n + 1; i <= 2 * n; i++) key[i] = 1;
    for (int i = 1; i <= cur; i++) rt[i] = Build(i,0,v[i].size() - 1);
    for (int i = n + 1; i <= 2 * n; i++)
    {
        SL[i] = sum[A[i]];
        sum[A[i]] = Add(sum[A[i]],L[i]);
    }
    memset(sum,0,sizeof(sum));
    for (int i = 2 * n; i > n; i--)
    {
        SR[i] = sum[A[i]];
        sum[A[i]] = Add(sum[A[i]],R[i]);
    }
    for (int i = n + 1; i <= 2 * n; i++)
        Ans = Add(Ans,Mul(SL[i],SR[i]));
}

int main()
{
    #ifdef DMC
        freopen("DMC.txt","r",stdin);
        //freopen("1.out","w",stdout);
    #endif

    n = getint(); Pre_Work(); m = getint();
    while (m--)
    {
        int typ = getint(),x = getint() + n;
        if (typ == 1)
        {
            splay(x,A[x]); int dl,dr,pos = siz[ch[x][0]] + 1;
            dl = Dec(Mul(pos - 1,sl0[ch[x][0]]),sl1[ch[x][0]]);
            dr = Add(sr1[ch[x][1]],Mul(pos,sr0[ch[x][1]]));
            dr = Dec(dr,Mul(pos + 1,sr0[ch[x][1]]));
            Ans = Dec(Ans,Mul(sl0[ch[x][0]],sr0[ch[x][1]]));
            Ans = Dec(Ans,Mul(dl,R[x])); Ans = Dec(Ans,Mul(dr,L[x]));
            key[x] ^= 1; maintain(x);
        }
        else
        {
            splay(x,A[x]); int dl,dr,pos = siz[ch[x][0]] + 1;
            dl = Dec(Mul(pos - 1,sl0[ch[x][0]]),sl1[ch[x][0]]);
            dr = Add(sr1[ch[x][1]],Mul(pos,sr0[ch[x][1]]));
            dr = Dec(dr,Mul(pos + 1,sr0[ch[x][1]]));
            Ans = Add(Ans,Mul(sl0[ch[x][0]],sr0[ch[x][1]]));
            Ans = Add(Ans,Mul(dl,R[x])); Ans = Add(Ans,Mul(dr,L[x]));
            key[x] ^= 1; maintain(x);
        }
        printf("%d\n",Ans);
    }
    //cerr << (double)(clock()) / CLOCKS_PER_SEC << endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值