2020 CCPC Wannafly Winter Camp Day1 解题报告(部分)

B


简单题,已知原文和秘钥、加密得到密文的方法、密文和秘钥。可以轻松的解密出原文:只要用密文对应的数字减去秘钥对应的数字就可以了。


#include <bits/stdc++.h>

using namespace std;

#define ll long long
ll input(){
    ll x=0,f=0;char ch=getchar();
    while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return f? -x:x;
}

char getch(int x){
    if(x>25) return (char)(x-26+'A');
    else return (char)('a'+x);
}

int getid(char ch){
    if(ch>='a'&&ch<='z') return (int)(ch-'a');
    else return (int)(ch-'A'+26);
}

const int N=1e3+7;
const int mod=52;

char s[N][N];
int a[N][N];
int opr[N][2];
int n,m;

void antiopr(int x,int y){
    int lenx=strlen(s[x]),leny=strlen(s[y]);
    int tmpx[N];
    for(int i=0;i<lenx;i++)
        tmpx[i]=a[x][i];

    for(int i=0;i<leny;i++){
        a[y][i]=(a[y][i]-tmpx[i%lenx]+mod)%mod;
    }
}

int main(){
    n=input(),m=input();
    for(int i=1;i<=m;i++){
        opr[i][0]=input(),opr[i][1]=input();
    }

    for(int i=1;i<=n;i++){
        scanf("%s",s[i]);
        int len=strlen(s[i]);
        for(int j=0;j<len;j++){
            a[i][j]=getid(s[i][j]);
        }
    }

    for(int i=m;i>=1;i--){
        antiopr(opr[i][0],opr[i][1]);
    }
    for(int i=1;i<=n;i++){
        int len=strlen(s[i]);
        for(int j=0;j<len;j++){
            printf("%c",getch(a[i][j]));
        }printf("\n");
    }
}

F


二分答案\(K\)就行了,算小于等于\(K\)的乘积有多少个。但是问题是当数为0或者负数时需要讨论,比较麻烦。赛中没有调出来。


#include <bits/stdc++.h>

using namespace std;

#define ll long long
ll input(){
    ll x=0,f=0;char ch=getchar();
    while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return f? -x:x;
}

const int N=1e5+7;
const ll inf=1e12+7;

ll a[N],b[N];
ll n,m;
ll k;

ll check(ll x){
    ll cnt=0;
    for(int i=1;i<=n;i++){
        if(a[i]==0){
            if(x<=0) cnt+=m;
            continue;
        }
        ll tmp=x/a[i];
        if(tmp*a[i]<x){
            if((tmp+1)*a[i]>=x) tmp++;
            else tmp--;
        }

        if((tmp+1)*a[i]>=x)
            cnt+=m+1-(lower_bound(b+1,b+m+1,tmp)-b);
        else
            cnt+=upper_bound(b+1,b+m+1,tmp)-b-1;
    }
    return cnt>=k;
}

int main(){
    n=input(),m=input();
    k=input();

    for(int i=1;i<=n;i++)
        a[i]=input();
    for(int i=1;i<=m;i++)
        b[i]=input();

    sort(a+1,a+1+n);
    sort(b+1,b+1+m);

    ll l=-inf,r=inf,Ans=0;
    while(l<=r){
        ll mid=(l+r)/2;
        if(check(mid)) Ans=mid,l=mid+1;
        else r=mid-1;
    }
    cout<<Ans<<endl;
}

H


\(y\)要满足以下两个条件:

  1. \(y\)必须是\(k\)的倍数,因为我们要区分\(gcd(y,k)\)\(k\)
  2. 对于\([1, \lfloor \frac{n}{k} \rfloor]\)中的每一个质数\(p\),\(y\)必须是\(p\)的倍数,因为我们要区分\(p\)\(pk\)

所以答案就是\([1, \lfloor \frac{n}{k} \rfloor]\)中的每一个质数与k的乘积。


ss = []
def work():
    maxl = 500
    count = 0        
    check = [] 
    for x in range(0, maxl+1):
        check.append(0)
    for i in range(2, maxl):
        if check[i] == 0:
            ss.append(i)
            count += 1
        for j in range(0, count):
            if ss[j]*i > maxl:
                break
            check[ss[j]*i] = 1 
            if i%ss[j] == 0:
                break
    return count

cnt = work()
T = int(input())
for i in range(1,T+1):
    n,k = map(int, input().split())

    tmp = n//k;

    Ans = 1
    
    for i in range(0,cnt):
        if ss[i] > tmp :
            break
        Ans = Ans * ss[i]

    print(Ans*k)

I


补这个题花了我好大的功夫学了树套树和线段树的分离与合并。本题就是势能线段树套权值线段树,真的写出来还是有点代码量的,考场上肯定就直接分块刚了。

这个题暴力是可以过的,我自己在牛客上还拜托了去了现场的盆友在PTA(现场的数据和环境)上交都AC了。某知名选手翻车现场


暴力:

#include <bits/stdc++.h>

using namespace std;

#define ll long long
ll input(){
    ll x=0,f=0;char ch=getchar();
    while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return f? -x:x;
}

const int N=8e4+7;

int n,m;
int a[N],p[N];

int main(){
    n=input(),m=input();
    for(int i=1;i<=n;i++)
        a[i]=input();

    for(int j=1;j<=m;j++){
        int opr=input(),l=input(),r=input(),x=input();
        if(opr==1){
            for(int i=l;i<=r;i++)
                a[i]=min(x,a[i]);
        }else{
            for(int i=1;i<=n;i++) p[i]=0;
            for(int i=l;i<=r;i++) p[a[i]]++;
            int tmp=0;
            for(int i=1;i<=n;i++){
                tmp+=p[i];
                cout<<tmp<<endl;
                if(tmp>=x){
                    printf("%d: %d\n",j,i);
                    break;
                }
            }
        }
    }
}

树套树:

#include <bits/stdc++.h>

using namespace std;

#define ll long long
ll input(){
    ll x=0,f=0;char ch=getchar();
    while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return f? -x:x;
}

const int N=8e4+7,MN=N*20*20;

vector <int> roots;
int n,m,top,rec[MN];

namespace Value_Tree{
    
    #define ls(x) t[x].ch[0]
    #define rs(x) t[x].ch[1]

    struct node{
        int ch[2],s;
        void clear(){ch[0]=ch[1]=s=0;}
        node(){clear();}
    }t[MN];

    int size=0;

    int newnode(){return !top?++size:rec[top--];}
    void remove(int rt){rec[++top]=rt;t[rt].clear();}

    int insert(int rt,int l,int r,int pos,int x){
        if(!rt) rt=newnode();
        t[rt].s+=x;
        if(l==r) return rt;
        int mid=(l+r)>>1;
        if(pos<=mid) ls(rt)=insert(ls(rt),l,mid,pos,x);
        else rs(rt)=insert(rs(rt),mid+1,r,pos,x);
        return rt;
    }

    int insert(int rt,int val,int x=1){return insert(rt,1,n,val,x);}

    int merge(int l,int r){
        if(l==0&&r==0) return l;
        int rt=newnode();
        t[rt].s=t[l].s+t[r].s;
        ls(rt)=merge(ls(l),ls(r));
        rs(rt)=merge(rs(l),rs(r));
        return rt;
    }

    int merge2(int l,int r,int del=0){
        if(r==0) return l;
        if(l==0) l=newnode();
        t[l].s+=t[r].s;
        ls(l)=merge2(ls(l),ls(r),del);
        rs(l)=merge2(rs(l),rs(r),del);
        if(del) remove(r);
        if(!del&&t[l].s==0){remove(l);return 0;}
        return l;
    }

    void recycle(int rt){
        if(!rt) return;
        remove(rt);
        recycle(ls(rt)),
        recycle(rs(rt));
    }

    int query(int l,int r,int val){
        if(l==r) return l;
        int mid=(l+r)>>1;
        int lsum=0;
        for(auto rt:roots) lsum+=t[ls(rt)].s;
        if(lsum>=val){
            for(auto &rt:roots) rt=ls(rt);
            return query(l,mid,val);
        }else{
            for(auto &rt:roots) rt=rs(rt);
            return query(mid+1,r,val-lsum);
        }
    }
}

int a[N];

namespace seg{
    struct node{
        int mx,cnt,sc,lazy,rt;
        node(){mx=cnt=sc=rt=0;lazy=INT_MAX;}
    }t[N<<2];

    void put(int rt){
        if(t[rt<<1].mx>t[rt<<1|1].mx){
            t[rt].mx=t[rt<<1].mx;
            t[rt].sc=max(t[rt<<1|1].mx,t[rt<<1].sc);
            t[rt].cnt=t[rt<<1].cnt;
        }else if(t[rt<<1].mx<t[rt<<1|1].mx){
            t[rt].mx=t[rt<<1|1].mx;
            t[rt].sc=max(t[rt<<1].mx,t[rt<<1|1].sc);
            t[rt].cnt=t[rt<<1|1].cnt;
        }else{
            t[rt].mx=t[rt<<1].mx;
            t[rt].sc=max(t[rt<<1].sc,t[rt<<1|1].sc);
            t[rt].cnt=t[rt<<1].cnt+t[rt<<1|1].cnt;
        }
    }

    void build(int rt,int l,int r){
        if(l==r){
            t[rt].mx=a[l];
            t[rt].cnt=1;
            t[rt].rt=Value_Tree::insert(t[rt].rt,a[l]);
            return;
        }
        int mid=(l+r)>>1;
        build(rt<<1,l,mid);
        build(rt<<1|1,mid+1,r);
        t[rt].rt=Value_Tree::merge(t[rt<<1].rt,t[rt<<1|1].rt);
        put(rt);
    }

    void modify(int rt,int val){
        if(t[rt].mx<=val) return;
        Value_Tree::insert(t[rt].rt,t[rt].mx,-t[rt].cnt);
        Value_Tree::insert(t[rt].rt,val,t[rt].cnt);
        t[rt].mx=val;
        t[rt].lazy=val;
    }

    void pushdown(int rt){
        if(t[rt].lazy==INT_MAX) return;
        modify(rt<<1,t[rt].lazy);
        modify(rt<<1|1,t[rt].lazy);
        t[rt].lazy=INT_MAX;
    }

    int modify(int rt,int l,int r,int ql,int qr,int val){
        if(r<ql||qr<l||t[rt].mx<val) return 0;
        if(ql<=l&&r<=qr&&t[rt].sc<val){
            int root;
            root=Value_Tree::insert(0,t[rt].mx,-t[rt].cnt);
            root=Value_Tree::insert(root,val,t[rt].cnt);
            modify(rt,val);
            return root;
        }
        pushdown(rt);
        int mid=(l+r)>>1,root;
        root=Value_Tree::merge2(modify(rt<<1,l,mid,ql,qr,val),modify(rt<<1|1,mid+1,r,ql,qr,val),1);
        t[rt].rt=Value_Tree::merge2(t[rt].rt,root);
        put(rt);
        return root;
    }

    void query(int rt,int l,int r,int ql,int qr){
        if(ql<=l&&r<=qr){
            roots.push_back(t[rt].rt);
            return;
        }
        pushdown(rt);
        int mid=(l+r)>>1;
        if(ql<=mid) query(rt<<1,l,mid,ql,qr);
        if(mid<qr) query(rt<<1|1,mid+1,r,ql,qr);
    }
}

int main(){
    n=input(),m=input();
    for(int i=1;i<=n;i++) a[i]=input();
    seg::build(1,1,n);
    for(int i=1;i<=m;i++){
        int opr=input(),l=input(),r=input(),x=input();
        if(opr==1) Value_Tree::recycle(seg::modify(1,1,n,l,r,x));
        else{
            roots.clear();
            seg::query(1,1,n,l,r);
            printf("%d\n",Value_Tree::query(1,n,x));
        }
    }
}

A


把所有区间按照中点从小到大排序,我们就可以得到最优的序列。然后问题转化为了求一个序列的逆序对个数的期望。根据期望的可加性,我们可以枚举每一对位置,答案就是它们形成逆序对的概率加起来。对于一对随机变量\([l_1,r_1]\),\([l_2,r_2]\)左边右边大的期望为:
\[ \frac{\sum_{i=l_1}^{r_1}max(0,min(i,r_2)-l_2+1)}{(r_1-l_1+1)*(r_2-l_2+1)} \]

最后\(O(n^2)\)计算所有的答案即可。


#include <bits/stdc++.h>

using namespace std;

#define ll long long
ll input(){
    ll x=0,f=0;char ch=getchar();
    while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return f? -x:x;
}

#define PII pair<ll,ll>
#define fr first
#define sc second
#define mp make_pair

const int N=5e3+7;
const ll mod=998244353;

PII a[N];
int n;

bool cmp(PII x,PII y){
    return (x.fr+x.sc)<(y.fr+y.sc);
}

ll powmod(ll a,ll b){
    ll res=1;
    while(b){
        if(b&1) res=res*a%mod;
        a=a*a%mod;
        b>>=1ll;
    }
    return res;
}

PII cal(PII x,PII y){
    ll sum=(x.sc-x.fr+1)*(y.sc-y.fr+1)%mod;
    ll cnt=0;
    if(x.sc>y.sc)
        cnt=(cnt+((x.sc-y.sc)*(y.sc-y.fr+1)%mod+((y.sc-y.fr+1)*(y.sc-y.fr)/2)%mod)%mod)%mod;
    else if(x.sc>y.fr){
        ll len=x.sc-x.fr+1;
        if(x.fr>y.fr) cnt=(cnt+(len*(x.fr-y.fr)%mod+(len*(len-1)/2)%mod)%mod)%mod;
        else cnt=(cnt+((x.sc-y.fr+1)*(x.sc-y.fr)/2)%mod)%mod;
    }
    return mp(cnt,sum);
}

int main(){
    n=input();
    for(int i=1;i<=n;i++){
        a[i].fr=input(),a[i].sc=input();
    }
    sort(a+1,a+n+1,cmp);

    ll X=0,Y=1;

    for(int i=1;i<=n;i++){
        for(int j=i+1;j<=n;j++){
            PII tmp=cal(a[i],a[j]);
            X=(X*tmp.sc%mod+Y*tmp.fr%mod)%mod;
            Y=Y*tmp.sc%mod;
        }
    }
    printf("%lld\n",(X*powmod(Y,mod-2)%mod+mod)%mod);
}

猜你喜欢

转载自www.cnblogs.com/-aether/p/12285653.html