Educational Codeforces Round 75 (Rated for Div. 2)

A. Broken Keyboard (CF 1251 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++) {
        char s[508];
        scanf("%s",s);
        set<char> qwq;
        int len=strlen(s);
        for(int i=0;i<len;++i){
            if (s[i]==s[i+1]) ++i;
            else qwq.insert(s[i]); 
        }
        for(auto i:qwq) putchar(i);
        puts("");
    }
    return 0;
}


B. Binary Palindromes (CF 1251 B)

题目大意

给了\(n\)\(01\)串,可以任意交换任意对串中任意的数字任意多次,问最多能弄出多少个回文串。

解题思路

我们发现\(01\)串长度是奇数的话一定可以自身交换成回文串,而如果是偶数的话,如果是偶数个\(1\)\(0\)也可以自身交换成回文串,如果是奇数个\(0\)\(1\)则不行,记这种串为\(s\)。由于奇数长度的串可以与\(s\)串进行交换使得\(s\)变成回文串。故如果有奇数串则全部都可以变成回文串,如果没有奇数串,但有偶数个\(s\)串,它们俩俩交换也可以是其都变成回文串,故有偶数个\(s\)串则全部都可以变成回文串,有奇数个的话就只有一个不能变成回文串。

神奇的代码
#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 ii = 1; ii <= kase; ii++) {
        int n;
        read(n);
        int cnt[2]={0};
        int aa=0;
        for(int i=0;i<n;++i){
            char s[58];
            scanf("%s",s);
            int len=strlen(s);
            int qwq[2]={0};
            for(int j=0;j<len;++j)
                ++qwq[s[j]-'0'];
            if (len&1){
                if (qwq[0]&1) ++cnt[0];
                else ++cnt[1];
            }
            else if (qwq[0]&1) ++aa;
        }
        aa=aa&1;
        aa=max(aa-cnt[0]-cnt[1],0);
        int ans=n-aa;
        write(ans,'\n');
    }
    return 0;
}


C. Minimize The Integer (CF 1251 C)

题目大意

给了一串数字,相邻奇偶性不同的可以交换位置,问最小能交换出的数字是多少。

解题思路

奇偶性相同的数字不能交换,那么它们的相对位置不能发现变化,而不同的可以随便变。那么我们把奇数的单独拎出来,偶数的单独拎出来,两个指针分别指着奇数组和偶数组,然后每次取奇偶中最小的放回即可。

神奇的代码
#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++) {
        char s[400005];
        scanf("%s",s);
        int len=strlen(s);
        vector<int> cnt[2];
        for(int i=0;i<len;++i){
            int qwq=s[i]-'0';
            cnt[qwq&1].push_back(qwq);
        }
        auto l=cnt[0].begin();
        auto r=cnt[1].begin();
        while(len--){
            if (l!=cnt[0].end()&&r!=cnt[1].end()){
                if (*l<*r){
                    putchar(*l+'0');
                    ++l;
                }
                else{
                    putchar(*r+'0');
                    ++r;
                }
            }else if (l!=cnt[0].end()){
                putchar(*l+'0');
                l++;
            }else{
                putchar(*r+'0');
                ++r;
            }
        }
        puts("");
    }
    return 0;
}


D. Salary Changing (CF 1251 D)

题目大意

\(n\)个人发工资,每人有一个发工资的钱数的范围,要求确定一种发工资方案,使得所有人所得的工资的中位数最大,且发的工资总数不超过\(s\)

解题思路

很容易想到可以二分中位数\(mid\),然后对于工资范围\([l_i,r_i]\),如果\(r_i<mid\),这种人肯定给\(l_i\),而对于\(l_i\geq mid\),这种人也肯定发\(l_i\),至于\(l_i<mid\leq r_i\),我们需要中位数为\(mid\),就要有\(m=\frac{n+1}{2}\)个人的工资大于等于\(mid\),如果此时这种人数小于\(m\)的话,还差\(cnt\)个,我们给\(l_i\)最大的\(cnt\)个人发\(mid\)工资,剩下的都发\(l_i\)即可。如果大于则全部发\(l_i\),然后\(mid\)增大继续找。

神奇的代码
#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(LL mid,vector<pair<LL,LL>> sol,int n,LL s){
    int cnt=0;
    vector<LL> qwq;
    for(auto i:sol){
        if (i.second<mid) s-=i.first;
        else if (i.first>=mid) ++cnt,s-=i.first;
        else qwq.push_back(i.first);
    }
    int m=(n+1)>>1;
    if (cnt>m) return 1;
    if (s<0) return 0;
    if (qwq.size()+cnt<m) return 0;
    sort(qwq.begin(),qwq.end());
    int cou=qwq.size()-(m-cnt);
    for(int i=0;i<cou;++i)
        s-=qwq[i];
    s-=(m-cnt)*mid;
    if (s<0) return 0;
    return 1;
}

int main(void) {
    int kase; read(kase);
    for (int i = 1; i <= kase; i++) {
        int n;
        LL s;
        read(n);
        read(s);
        vector<pair<LL,LL>> sol;
        for(int i=1;i<=n;++i){
            LL l,r;
            read(l);
            read(r);
            sol.push_back(make_pair(l,r));
        }
        LL l=0,r=1e9+7;
        LL ans=0;
        while(l<r){
            LL mid=(l+r)>>1;
            if (check(mid,sol,n,s)) ans=mid,l=mid+1;
            else r=mid;
        }
        write(ans,'\n');
    }
    return 0;
}


E1 E2. Voting (Easy/Hard Version) (CF 1251 E1 E2)

题目大意

拉选票,有\(n\)个人,第\(i\)个人需要用\(p_i\)金钱贿赂才会给你投票,或者看见有大于等于\(m_i\)个人给你投票的话他也会给你投票。现在你需要所有人都给你投票,问最小需要多少钱。

解题思路

分别考虑枚举金钱,贿赂多少人后无果。但考虑到知道多少人给你投票的角度的话似乎可解,于是我们将人通过\(m_i\)分组,对\(m_i\)倒序枚举,对于每组来说有一个\(cur=m_i\),此时\(m_j<cur\)的人都已经投票给你了,记为\(sum[i-1]\),再加上自己已经贿赂了\(py\)个人,如果\(sum[i-1]+py\geq cur\)的,那这组人就归降于您,否则就需要再在\(m_j\geq cur\)的人中贿赂直到\(sum[i-1]+py=cur\)或都贿赂完,我们自然是贪心的找\(p_i\)最小的那些人贿赂,用优先队列维护即可。

神奇的代码
#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++) {
        int n;
        read(n);
        int cnt[n+1]={0};
        vector<int> money[n+1];
        for(int p,m,i=1;i<=n;++i){
            read(m);
            read(p);
            cnt[m]++;
            money[m].push_back(p);
        }
        priority_queue<int,vector<int>,greater<int>> qwq;
        LL ans=0;
        int by=0;
        for(int i=1;i<=n;++i) cnt[i]+=cnt[i-1];
        for(int i=n-1;i>0;--i){
            if (money[i].empty()) continue;
            for(int j:money[i]) qwq.push(j);
            if (cnt[i-1]+by>=i) continue;
            while(!qwq.empty()&&cnt[i-1]+by<i){
                ans+=qwq.top();
                qwq.pop();
                ++by;
            }  
        }
        write(ans,'\n');
    }
    return 0;
}


F. Red-White Fence (CF 1251 F)

题目大意

\(n\)块白栅栏\(k\)块红栅栏有各自高度,要求选出一个红栅栏和若干个白栅栏,排成一排,要求红栅栏高度最高,红栅栏左边的白栅栏(可以没有)高度严格递增,右边的白栅栏(可以没有)高度严格递减,且它们的周长为\(Q\),有\(q\)次周长询问,分别回答方案数。

解题思路

记红栅栏高度为\(h\),选了\(cnt\)个白栅栏,这栅栏的周长为\(4\times h\times (cnt+1)\)
由于要严格递增或递减,对于长度相同有若干个的白栅栏只会用到两个,于是白栅栏就分成两种类型,一种是高度出现一次的,另一种是出现两次(及以上都归为两次)的。设红栅栏高度为\(h\),则高度小于\(h\)的白栅栏中,高度出现一次的有\(cnt_1\)个,出现两次的有\(cnt_2\)个,要选\(w\)个白栅栏。然后我们考虑方案数。
如果\(cnt_2\)为零,方案数即为\(C^{w}_{cnt_1}\times \sum\limits_{i=0}^{w}C^{i}_{w}=C^{w}_{cnt_1}\times 2^{w}\),但如果不为\(0\),对于这些数我们要考虑选\(1\)个还是选\(2\)个,选\(1\)个还要考虑放左边还是放右边。我们可以这样处理,把这\(cnt_2\)个出现两次的这些数的个数翻倍,一个表示把它放左边的,一个表示把它放右边的,那么对于这个数就有三种选择的情况,分别对应上面的三种。所以最终的方案数即为\(\sum\limits_{i=0}^{w}2^{i}\times C^{i}_{cnt_1}\times C^{w-i}_{cnt_2}\),这是个卷积形式,我们对于每一个红栅栏,构造多项式\(f(x)=\sum\limits_{i}2^{i}\times C^{i}_{cnt_1}x^{i}\)\(g(x)=\sum\limits_{i}C^{i}_{cnt_2}x^{i}\),用\(NTT\)加速\(f*g\),则这个红栅栏对答案的贡献为\(x^{w}\)的系数。预处理答案后\(O(1)\)回答。总时间复杂度为\(O(k*n\log_2 n+q)\)
随便找了个NTT模板

神奇的代码
#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 mod=998244353;

const int maxn=3e5+8;

int N,M,n,k,q,wi,bl;

LL a[maxn*3],b[maxn*3],c[maxn*3],g,jie[maxn],jieinv[maxn],white[maxn],red[6],ans[5*maxn];

int cnt_white[maxn],cnt_black[maxn];

LL qpower(LL x, LL y){
    LL res=1;
    while (y){
        if (y&1) (res*=x)%=mod;
        (x*=x)%=mod;
        y>>=1;
    }
    return res;
}

LL inv(LL x){return qpower(x, mod - 2);}

void NTT(LL *arr,int size,int type){
    int rev[maxn*3];
    rev[0]=0;
    for(int i=1;i<size;++i)
        rev[i]=(rev[i>>1]>>1)|((i&1)?(size>>1):0);
    for(int i=0;i<size;++i)
        if (rev[i]>i) swap(arr[i],arr[rev[i]]);
    for(int len=2;len<=size;len<<=1){
        LL wn=qpower(g,(mod-1)/len);
        if(type==-1) wn=inv(wn);
        for(int i=0;i<size;i+=len){
            LL w=1;
            for(int j=0;j<(len>>1);++j,w=w*wn%mod){
                LL tmp1=arr[i+j],tmp2=arr[i+(len>>1)+j]*w%mod;
                arr[i+j]=tmp1+tmp2;
                arr[i+j+(len>>1)]=tmp1-tmp2;
                if(arr[i+j]>=mod) arr[i+j]-=mod;
                if(arr[i+j+(len>>1)]<0) arr[i+j+(len>>1)]+=mod;
            }
        }
    }
    if(type==-1) {
        LL t=inv(size);
        for(int i=0;i<size;++i)
            arr[i]=arr[i]*t%mod;
    }
}

LL C(int m,int n){
    if (m>n) return 0;
    if (m<0||n<0) return 0;
    return jie[n]*jieinv[m]%mod*jieinv[n-m]%mod;
}

int main(void) {
    read(n);
    read(k);
    for(int u,i=1;i<=n;++i){
        read(u);
        if (cnt_white[u]<2) white[++wi]=u,++cnt_white[u];
    }
    for(int u,i=1;i<=k;++i){
        read(u);
        if (cnt_black[u]==0) red[++bl]=u,++cnt_black[u];
    }
    sort(white+1,white+1+wi);
    sort(red+1,red+1+bl);
    jie[0]=jie[1]=jieinv[1]=jieinv[0]=1;
    for(int i=2;i<=n;++i){
        jie[i]=jie[i-1]*i%mod;
        jieinv[i]=inv(jie[i]);
    }
    for(int i=1;i<=bl;++i){
        int pos=lower_bound(white+1,white+1+wi,red[i])-white;
        N=M=0;
        for(int j=1;j<pos;++j){
            if (white[j]==white[j+1]) N+=2,++j;
            else ++M;
        }
        for(int j=0;j<=M;++j) a[j]=C(j,M)*qpower(2,j)%mod;
        for(int j=0;j<=N;++j) b[j]=C(j,N);
        ++M;
        ++N;
        int sz=1;
        g=3;
        while(sz<N+M) sz<<=1;
        NTT(a,sz,1);
        NTT(b,sz,1);
        for(int i=0;i<sz;++i)
            c[i]=a[i]*b[i]%mod;
        NTT(c,sz,-1);
        for(int j=0;j<max(pos,0);++j) ans[(red[i]<<1)+((j+1)<<1)]=(ans[(red[i]<<1)+((j+1)<<1)]+c[j])%mod;
        for(int j=0;j<sz;++j) a[j]=0;
        for(int j=0;j<sz;++j) b[j]=0;
        for(int j=0;j<sz;++j) c[j]=0;
    }
    read(q);
    for(int Q,i=1;i<=q;++i){
        read(Q);
        write(ans[Q],'\n');
    }
    return 0;
}


posted @ 2020-02-06 21:13  ~Lanly~  阅读(201)  评论(0编辑  收藏  举报