Educational Codeforces Round 80 (Rated for Div. 2)

A. Deadline (CF 1288 A)

题目大意

给定\(n,d\),问是否存在自然数\(x\),使得\(x+\left \lceil \dfrac{d}{x+1} \right \rceil \leq n\)

解题思路

直接暴力。对于用了不等式的不会去证明其正确性qwq整除与上下取正什么的。

神奇的代码
#include <bits/stdc++.h>
#define MIN(a,b) ((((a)<(b)?(a):(b))))
#define MAX(a,b) ((((a)>(b)?(a):(b))))
#define ABS(a) ((((a)>0?(a):-(a))))
using namespace std;
typedef long long LL;
typedef vector<int> VI;
typedef pair<int,int> PII;
typedef vector<PII> VPII;
typedef vector<LL> VL;
typedef pair<LL,LL> PLL;
typedef vector<PLL> VPLL;

template <typename T>
void read(T &x) {
    int s = 0, c = getchar();
    x = 0;
    while (isspace(c)) c = getchar();
    if (c == 45) s = 1, c = getchar();
    while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
    if (s) x = -x;
}

template <typename T>
void write(T x, char c = ' ') {
    int b[40], l = 0;
    if (x < 0) putchar(45), x = -x;
    while (x > 0) b[l++] = x % 10, x /= 10;
    if (!l) putchar(48);
    while (l) putchar(b[--l] | 48);
    putchar(c);
}

bool check(int n,int d){
    for(int i=1;i<MIN(1e5,n);++i){
        if (i+(int)ceil(d*1.0/(i+1))<=n) return true;
    }
    return false;
}

int main(void) {
    int kase; read(kase);
    for (int i = 1; i <= kase; i++) {
        // printf("Case #%d: ", i);
        int n,d;
        read(n);
        read(d);
        if (n>=d||check(n,d)) puts("YES");
        else puts("NO");        
    }
    return 0;
}


B. Yet Another Meme Problem (CF 1288 B)

题目大意

给定\(A,B\),问有对少个\((a,b)\),其中\(a \in [1,A],b \in [1,B]\),使得\(a\times b+a+b=a\times 10^{f(b)}+b\),其中\(f(x)\)表示\(x\)的位数。

解题思路

我们把式子化简,即为\(b+1=10^{f(b)}\),很显然b只能为\(9,99,999\)这样的类型,我们设\(b+1\)\(cnt\)位,那答案就是\(cnt\times A\)

神奇的代码
#include <bits/stdc++.h>
#define MIN(a,b) ((((a)<(b)?(a):(b))))
#define MAX(a,b) ((((a)>(b)?(a):(b))))
#define ABS(a) ((((a)>0?(a):-(a))))
using namespace std;
typedef long long LL;
typedef vector<int> VI;
typedef pair<int,int> PII;
typedef vector<PII> VPII;
typedef vector<LL> VL;
typedef pair<LL,LL> PLL;
typedef vector<PLL> VPLL;

template <typename T>
void read(T &x) {
    int s = 0, c = getchar();
    x = 0;
    while (isspace(c)) c = getchar();
    if (c == 45) s = 1, c = getchar();
    while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
    if (s) x = -x;
}

template <typename T>
void write(T x, char c = ' ') {
    int b[40], l = 0;
    if (x < 0) putchar(45), x = -x;
    while (x > 0) b[l++] = x % 10, x /= 10;
    if (!l) putchar(48);
    while (l) putchar(b[--l] | 48);
    putchar(c);
}

int main(void) {
    int kase; read(kase);
    for (int i = 1; i <= kase; i++) {
        LL A,B;
        read(A);
        read(B);
        LL qwq=9;
        int tmp=0;
        while(qwq<=B){
            ++tmp;
            qwq=qwq*10+9;
        }
        LL ans=tmp*A;
        printf("%lld\n",ans);
    }
    return 0;
}


C. Two Arrays (CF 1288 C)

题目大意

给定\(n,m\),要求构造两个数组\(a,b\),记为\((a,b)\),满足\(a,b\)数组有\(m\)个元素,且\(\forall i \in [1,m],1\leq a_i\le b_i \leq n\),问有多少个符合要求的\((a,b)\)

解题思路

我们设\(dp0[i][j]\)表示当前第\(i\)位放\(j\)且不严格递增的方案数,则\(dp0[i][j]=\sum\limits_{k=1}^{j}dp0[i-1][k]\),相应的再设个不严格递减的方案数\(dp1[i][j]\),最终答案就是\(\sum\limits_{i=1}^{n}\sum\limits_{j=i}^{n}dp0[m][i]\times dp1[m][j]\)
对应为代码注释部分。

我们把\(b\)数组翻转过来,接到\(a\)后面,我们就得到了一个长度为\(2m\)的不严格递增的数组\(c\),这样问题就是我们能够构造出多少个长度为\(2m\)的不严格递增的数组,当然也可以按照上面来次动态规划可以解决。不过可以证明,答案就是

\[C^{n-1}_{2m+n-1} \]

我们记\(cnt_i\)表示数\(i\)在数组\(c\)出现的次数,则有\(\sum\limits_{i=1}^{n}cnt_i=2\times m\),答案就是该方程组的非负整数解组的个数。这是个经典的排列组合问题,由于用隔板法要求解为正整数,而这里\(cnt_i\)可以为\(0\),那么我们就等式左右两边都加一个\(n\),并令\(CNT_i=cnt_i+1\),这样方程\(\sum\limits_{i=1}^{n}CNT_i=2\times m\)的解都是正整数,根据隔板法(假设有\(2m\)个球,插\(n-1\)个板把求分为\(n\)组,每组的个数即为\(cnt_i\),排序)答案就是\(C^{n-1}_{2m+n-1}\)

还有个证明方法就是我们构造一个\(d\)数组,其中\(d_i=c_i+i\),则\(2\leq d_i \leq 2m+n\),可以证明每个\(d\)数组恰与一个\(c\)数组对应并不会证明,注意到\(d\)数组数字各异,所以\(d\)数组共有\(C_{2m+n-1}^{2m}=C_{2m+n-1}^{n-1}\)

神奇的代码
#include <bits/stdc++.h>
#define MIN(a,b) ((((a)<(b)?(a):(b))))
#define MAX(a,b) ((((a)>(b)?(a):(b))))
#define ABS(a) ((((a)>0?(a):-(a))))
using namespace std;
typedef long long LL;
typedef vector<int> VI;
typedef pair<int,int> PII;
typedef vector<PII> VPII;
typedef vector<LL> VL;
typedef pair<LL,LL> PLL;
typedef vector<PLL> VPLL;

template <typename T>
void read(T &x) {
    int s = 0, c = getchar();
    x = 0;
    while (isspace(c)) c = getchar();
    if (c == 45) s = 1, c = getchar();
    while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
    if (s) x = -x;
}

template <typename T>
void write(T x, char c = ' ') {
    int b[40], l = 0;
    if (x < 0) putchar(45), x = -x;
    while (x > 0) b[l++] = x % 10, x /= 10;
    if (!l) putchar(48);
    while (l) putchar(b[--l] | 48);
    putchar(c);
}

const LL mo=1e9+7;

LL kuai(LL a,LL b){
    LL qwq=1;
    while(b){
        if (b&1) qwq=qwq*a%mo;
        a=a*a%mo;
        b>>=1;
    }
    return qwq;
}

LL C(int n,int m){
    LL qwq=1,tmp=1;
    for(int i=1;i<=n;++i) qwq=qwq*i%mo; 
    for(int i=1;i<=m;++i) tmp=tmp*i%mo; 
    qwq=qwq*kuai(tmp,mo-2)%mo;
    tmp=1;
    for(int i=1;i<=n-m;++i) tmp=tmp*i%mo; 
    qwq=qwq*kuai(tmp,mo-2)%mo;
    return qwq;
}

int main(void) {
    int n,m;
    read(n);
    read(m);
    LL ans=C(2*m+n-1,n-1);
    /* LL dp0[m+1][n+1],dp1[m+1][n+1];
    for(int i=1;i<=n;++i) dp0[1][i]=dp1[1][i]=1;
    for(int i=2;i<=m;++i){
        LL sum=0;
        for(int j=1;j<=n;++j){
            sum=(sum+dp0[i-1][j])%mo;
            dp0[i][j]=sum;
        }
    }
    for(int i=2;i<=m;++i){
        LL sum=0;
        for(int j=n;j>=1;--j){
            sum=(sum+dp1[i-1][j])%mo;
            dp1[i][j]=sum;
        }
    }
    LL ans=0;
    for(int i=1;i<=n;++i)
        for(int j=i;j<=n;++j)
            ans=(ans+dp0[m][i]*dp1[m][j]%mo)%mo; */
    printf("%lld\n",ans);
    return 0;
}


D. Minimax Problem (CF 1288 D)

题目大意

给定\(n\)个数组\(a_i\),每个数组有\(m\)个数,要求选择两组数组\(i,j\),最大化\(\min\limits_{1\leq k \leq m} (\max (a_{ik},a_{jk}))\)。输出\(i,j\)。注意:\(1\leq n \leq 3\times 10^5,1\leq m \leq 8\)

解题思路

我暴力就\(O(n^2m)\)能怎么优化捏……但\(m\)是出奇的小。
注意到\(min\)的值可行性具有单调性,我们考虑如何快速判定它的可行性。
我们先二分最小值\(qwq\),枚举了一组,我们要看能否存在另一组,把该组小于\(qwq\)的值变成大于等于\(qwq\)。那么对于一组数组的某一位,如果它大于等于就设为\(1\),否则为\(0\),这样我们就把某一组\(hash\)成一个数字\(num_i\),那么对于一组\(i\),对应了一个数字\(num_i\),如果还有另一组\(j\)对应的数字\(num_j\)两者\(|\)一下得到的数字是\(num_i|num_j\)\(2^m-1\)的话,这就说明这两组结合起来,它的最小值是大于等于\(qwq\),而数字\(num_j\)也就只有\(2^m-1\leq 255\)

于是我们就二分最小值,然后枚举一组,再枚举各位大于\(qwq\)的所有情况即可。时间复杂度\(O(n\times 2^m\log_2\max(a_{ij}))\)

神奇的代码
#include <bits/stdc++.h>
#define MIN(a,b) ((((a)<(b)?(a):(b))))
#define MAX(a,b) ((((a)>(b)?(a):(b))))
#define ABS(a) ((((a)>0?(a):-(a))))
using namespace std;
typedef long long LL;
typedef vector<int> VI;
typedef pair<int,int> PII;
typedef vector<PII> VPII;
typedef vector<LL> VL;
typedef pair<LL,LL> PLL;
typedef vector<PLL> VPLL;

template <typename T>
void read(T &x) {
    int s = 0, c = getchar();
    x = 0;
    while (isspace(c)) c = getchar();
    if (c == 45) s = 1, c = getchar();
    while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
    if (s) x = -x;
}

template <typename T>
void write(T x, char c = ' ') {
    int b[40], l = 0;
    if (x < 0) putchar(45), x = -x;
    while (x > 0) b[l++] = x % 10, x /= 10;
    if (!l) putchar(48);
    while (l) putchar(b[--l] | 48);
    putchar(c);
}

const int N=3e5+8;

int n,m,x,y;

pair<bool,int> sign[260];

int a[N][10],v[N];

bool check(int val){
    memset(sign,0,sizeof(sign));
    for(int i=0;i<n;++i){
        int qwq=0;
        for(int j=0;j<m;++j)
            if (a[i][j]>=val) qwq|=(1<<j);
        sign[qwq]=make_pair(true,i);
        v[i]=qwq;
    }
    if (sign[(1<<m)-1].first==true){
        x=y=sign[(1<<m)-1].second+1;
        return true;
    }
    for(int i=0;i<n;++i)
        for(int j=0;j<(1<<m);++j){
            if (!sign[j].first) continue;
            int qwq=v[i]|j;
            if (qwq!=(1<<m)-1) continue;
            x=i+1;
            y=sign[j].second+1;
            return true;
        }
    return false;
}

int main(void) {
    read(n);
    read(m);
    int l=1e9+7,r=0;
    for(int i=0;i<n;++i)
        for(int j=0;j<m;++j){
            read(a[i][j]);
            l=MIN(l,a[i][j]);
            r=MAX(r,a[i][j]);
        }  
    x=y=0;
    ++r;
    while(l<r){
        int mid=(l+r)>>1;
        if (check(mid)) l=mid+1;
        else r=mid;
    }
    printf("%d %d\n",x,y);
    return 0;
}


E. Messenger Simulator (CF 1288 E)

题目大意

你有\(n(n\leq 3\times 10^5)\)个好友(这说明你与世界总人口的万分之一的人认识/滑稽),初始标号\(1-n\)排列,然后你依次收到了\(m\)条短信,第\(i\)条短信是来自是\(a_i\),当收到第\(a_i\)个好友发来的信息时,它会飞到列表的第一个并且在它前面的其他人都会后退一位,当然如果本身在第一位就什么事都没发生,除了你收到了条短信。问每个人位置最小和最大分别是多少。

解题思路

对于某个人\(i\)来说,如果它发过消息那么位置最小就是\(1\),没有发过消息则是\(i\),我们只用解决位置最大是多少。

对于某个人\(i\),它第一次出现的时候,由于只有序号比它大的才会对它的位置有影响,那么它此时已经后退了\(b\)位,位居\(i+b\)位,这里\(b\)是在\(i\)前面,比\(j\)大且互不相同的数的个数。而当\(i\)第二次出现的时候,\(i\)后退的\(B\)位,这里\(B\)是在\(i\)第一次出现之后第二次出现之前的数互不相同的个数,之后的处理类似。注意到如果我们在数列里前面加上\(n,n-1,n-2,n-3,...,1\)来初始化局面,那么在这之后对于\(i\)出现的情况的处理都类似\(i\)第二次出现了。

那么现在问题就转换成了统计区间内互不相同的个数。这个用\(Fenwick\ Tree\)或者\(Segment\ Tree\)来维护就好了。维护位置对个数的贡献,从左到右遍历,遇到第二次出现的就统计前后两次出现之间的位置对答案的贡献,然后把第一次出现的位置对答案的贡献置为\(0\),第二次出现的位置对答案的贡献置为\(1\),就能统计区间互不相同的个数了。

神奇的代码
#include <bits/stdc++.h>
#define MIN(a,b) ((((a)<(b)?(a):(b))))
#define MAX(a,b) ((((a)>(b)?(a):(b))))
#define ABS(a) ((((a)>0?(a):-(a))))
using namespace std;
typedef long long LL;
typedef vector<int> VI;
typedef pair<int,int> PII;
typedef vector<PII> VPII;
typedef vector<LL> VL;
typedef pair<LL,LL> PLL;
typedef vector<PLL> VPLL;

template <typename T>
void read(T &x) {
    int s = 0, c = getchar();
    x = 0;
    while (isspace(c)) c = getchar();
    if (c == 45) s = 1, c = getchar();
    while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
    if (s) x = -x;
}

template <typename T>
void write(T x, char c = ' ') {
    int b[40], l = 0;
    if (x < 0) putchar(45), x = -x;
    while (x > 0) b[l++] = x % 10, x /= 10;
    if (!l) putchar(48);
    while (l) putchar(b[--l] | 48);
    putchar(c);
}

const int N=6e5+8;

struct BIT_TREE{
    int cnt[N];

    int lowbit(int x){
        return (x&(-x));
    }

    void updata(int pos,int tot,int val){
        for(int i=pos;i<=tot;i+=lowbit(i))
            cnt[i]+=val;
    }

    int sum(int pos){
        int qwq=0;
        for(int i=pos;i>=1;i-=lowbit(i))
            qwq+=cnt[i];
        return qwq;
    }

    int query(int l,int r){
        if (l>r) return 0;
        return sum(r)-sum(l-1);
    }
}BIT;

int main(void) {
    int n,m;
    read(n);
    read(m);
    int v[n+m+5];
    for(int i=1;i<=n;++i)
        v[i]=n-i+1;
    for(int i=1;i<=m;++i)
        read(v[i+n]);
    int sign[n+m+5]={0};
    int pos[n+5][2]={0};
    for(int i=1;i<=n;++i) pos[i][0]=pos[i][1]=i;
    for(int i=1;i<=n+m;++i){
        if (sign[v[i]]==0){
            sign[v[i]]=i;
            BIT.updata(i,n+m,1);
        }
        else{
            int qwq=BIT.query(sign[v[i]]+1,i-1)+1;
            pos[v[i]][0]=1;
            pos[v[i]][1]=MAX(pos[v[i]][1],qwq);
            BIT.updata(sign[v[i]],n+m,-1);
            BIT.updata(i,n+m,1);
            sign[v[i]]=i;
        }
    }
    for(int i=1;i<=n;++i) pos[i][1]=max(pos[i][1],BIT.query(sign[i]+1,n+m)+1);
    for(int i=1;i<=n;++i)
        printf("%d %d\n",pos[i][0],pos[i][1]);
    return 0;
}


F. Red-Blue Graph (CF 1288 F)

题目大意

给定一个二分图,注意这里可能有重边,然后对边染色,染成红色费用\(r\),染成蓝色费用\(b\),对于一个点,如果连接它的红色边数量严格大于蓝色边,则这个点被染成红色;如果连接它的蓝色边数量严格大于红色边,则这个点被染成蓝色;否则它不被染色。现在给定点染色的要求\(URB\),对应未染色,红色,蓝色,球给定一组边染色的方案,使得点被染成给定要求,且费用最小。

解题思路

玄学构图+费用流

神奇的代码
qwq


posted @ 2020-01-15 15:06  ~Lanly~  阅读(262)  评论(0编辑  收藏  举报