• Ozon Tech Challenge 2020 (Div.1 + Div.2)


    Ozon Tech Challenge 2020 (Div.1 + Div.2)

    题目链接:https://codeforces.com/contest/1305

    A. Kuroni and the Gifts

    怎么做都能AC。

    B. Kuroni and Simple Strings

    题解:

    要么0次,要么1次。从最外层开始去除,一定是最优的。因为最左边的"("可以匹配右边所有的")",而最右边的")"可以匹配左边的"("。也就是两边的括号能匹配的更多,所以需要优先去除。暴力就能过。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    #define fi first
    #define se second
     
    typedef long long ll;
     
    const int INF=1e9+7;
    const int N=5050;
    int n,f[N],len;
    char s[N];
    bool dfs(set<int> &a){
        int l=len+1,r=0;
        for(int i=1;i<len;i++){
            if (f[i]==0 && s[i]=='('){
                l=i;
                f[i]=1;
                break;
            }
        }
        for(int i=len-1;i>=1;i--){
            if (f[i]==0 && s[i]==')'){
                r=i;
                f[i]=1;
                break;
            }
        }
        if (l>r)return 0;
        a.insert(l);
        a.insert(r);
        return 1;
    }
    void work(){
        scanf("%s",s+1);
        s[0]='a';
        len=strlen(s);
        vector<set<int>> ans;
        set<int>a;
        while(dfs(a));
        if (a.size()==0){printf("0
    ");return ;}
        printf("1
    ");
        printf("%d
    ",(int)a.size());
        for(auto x:a){
            printf("%d ",x);
        }
    }
    int main(){
    #ifndef ONLINE_JUDGE
        freopen("aa.in","r",stdin);
    #endif
        work();
     
    }
    

    C. Kuroni and Impossible Calculation

    题解

    千万不要把他当做数论去想。关键就在模数很小只有1000。当(n>m)时,那肯定存在((a[i]-a[j])\%m==0) 。要是能想到这里,这道题就没了。

    代码:

        #include<bits/stdc++.h>
        using namespace std;
        const int N = 1050;
        int a[N];
        int main(){
            int n,mo,ans=1;
            scanf("%d %d",&n,&mo);
            if (n>mo){cout<<0<<endl;return 0;}
            for(int i=1;i<=n;i++){
                scanf("%d",a+i);
                for(int  j=1;j<i;j++){
                    ans*=abs(a[j]-a[i])%mo;
                    ans%=mo;
                }
            }
            cout<<ans<<endl;
        }
    

    D. Kuroni and the Celebration

    题解

    我用的方法感觉比较玄学,一直WA来WA去。先随机选一个数为根,然后每次询问两个子节点,如果(lca)为子节点,那么实际的根肯定在子树里面。如果儿子都不是(lca),那么父亲就是根。这样做,有一个小问题,就是如果儿子是奇数的话,就会单出来一个,这样询问次数就可能就会超过(lfloorfrac{n}{2} floor)。那么就选择(size[x]approx[frac{n}{2}],且son[x]\%2==0的点为根(size[x]为x子树的大小,son[x]表示x的儿子的个数))

    还有就是每次先询问(size)更大的儿子。反正这些方法都比较玄学,要考虑会被什么数据卡,可麻烦了。

    正解其实是类似拓扑排序,每次找两个度为1的点记为(x,y),然后询问,如果$lca(x,y)==x (,那么)x(就是根,如果)x,y$都不是根那就删除这两点,同时更新与之想连的点的度。这样能保证每次删除两个点。最后要么在中途找到,要么只剩下一个点。也就做完啦,感觉很好写。

    代码一 玄学dfs

    #include<bits/stdc++.h>
    using namespace std;
    #define fi first
    #define se second
     
    typedef long long ll;
     
    const int INF=1e9+7;
    const int N=5050;
    int n,size[N],d[N],Tot,f[N];
    struct Edge{int x,y,s;};
    bool cmp(int x,int y){return size[x]>size[y];}
    struct Graph{
        int tot=0,last[N];
        Edge edges[N<<1];
        void addEdge(int x,int y){
            edges[++tot]={x,y,last[x]};
            last[x]=tot;
        }
        void dfs1(int x,int pre){
            size[x]=1;
            for(int i=last[x];i;i=edges[i].s){
                Edge &e=edges[i];
                if (e.y==pre)continue;
                dfs1(e.y,x);
                size[x]+=size[e.y];
            }
        }
        void dfs(int x,int pre){
            static int a[N];
            int lca,num=0;
            for(int i=last[x];i;i=edges[i].s){
                Edge &e=edges[i];
                if (e.y==pre)continue;
                a[++num]=e.y;
            }
            a[num+1]=x;
            sort(a+1,a+num+1,cmp);
            for(int i=1;i<=num;i+=2){
                //fflush(stdout);
                printf("? %d %d
    ",a[i],a[i+1]);
                Tot++;
                if (Tot>n/2)cout<<a[1000000];
                fflush(stdout); 
                scanf("%d",&lca);
                //fflush(stdout); 
                if (lca!=x){
                    dfs(lca,x);
                    return;
                }
            }
            printf("! %d
    ",x);
            return;
        }
    }G;
    void work(){
        scanf("%d",&n);
        for(int i=1;i<=n-1;i++){
            int x,y;
            scanf("%d %d",&x,&y);
            d[x]++; d[y]++;
            G.addEdge(x,y);
            G.addEdge(y,x);
        }
        int mx=0,xx=0,yy=1;
        G.dfs1(1,0);
        for(int i=1;i<=n;i++){
            if (d[i]%2==0) yy=i;
            if (size[i]<=(n+1)/2 && d[i]%2==0 && size[i]>mx){
                mx=size[i];
                xx=i;
            }
        }
        if (xx==0)xx=yy;
        G.dfs1(xx,0);
        G.dfs(xx,0);
    }
    int main(){
    #ifndef ONLINE_JUDGE
        //freopen("aa.in","r",stdin);
    #endif
        work();
     
    }
    

    代码二 拓扑序

        #include<bits/stdc++.h>
        using namespace std;
        const int N=1050;
        int n;
        struct Edge{int x,y,s;};
        struct Graph{
            int d[N],last[N],tot=0;
            Edge edges[N<<1];
            void addEdge(int x,int y){
                edges[++tot]=Edge{x,y,last[x]};
                last[x]=tot;
                d[x]++; d[y]++;
            }
            void addNode(queue<int> &Q, int x){
                for(int i=last[x];i;i=edges[i].s){
                    Edge &e=edges[i];
                    d[e.y]-=2;
                    if (d[e.y]==2){Q.push(e.y);d[e.y]=-1;}
                }
            }
            void solve(){
                queue<int>Q;
                for(int i=1;i<=n;i++){
                    if (d[i]==2){d[i]=-1;Q.push(i);}
                }
                while(!Q.empty()){
                    int x=Q.front(); Q.pop();
                    addNode(Q,x);
                    if (Q.empty()){
                        printf("! %d
    ",x);
                        fflush(stdout);
                        return;
                    }
                    int lca,y=Q.front(); Q.pop();
                    addNode(Q,y);
                    printf("? %d %d
    ",x,y);
                    fflush(stdout);
                    scanf("%d",&lca);
                    if (lca==x || lca==y){
                        printf("! %d
    ",lca);
                        return;
                    }
                }
            }
        }G;
        int main(){
            scanf("%d",&n);
            for(int i=1;i<=n-1;i++){
                int x,y;
                scanf("%d%d",&x,&y);
                G.addEdge(x,y);
                G.addEdge(y,x);
            }
            G.solve();
        }
    

    E. Kuroni and the Score Distribution

    题解

    我们先想怎样可以使得m很快被满足。因为序列单增,所以第(i)位,最多有(frac{i-1}{2})个数满足条件。即(a[1]+a[i-1]=a[2]+a[i-2]=...=a[frac{i-1}{2}]+a[i-frac{i-1}{2}])

    这个大概就是一个等差数列,于是就想着从(1)开始构建公差为(1)的等差数列。

    那么如果超过了呢。假如到(i)位还需要(x(x<frac {i-1}{2}))个三元组。那么只要令(a[i]=a[i-1]+a[i-2*x])即可。

    最后就是考虑剩下的不会再出现新的三元组。我们可以直接令(a[i]=INF(INF表示一个很大的数,如10^8)),然后之后每个数加上前面等差数列最大的那个值就行。假如前面枚举了五个数(1,2,3,4,5),那么之后就可以是(100000,100006,100012...)。然后就没了。一般构造题,方法都不只一种,没必要一定要构建一个最极限的方法。

    #include<bits/stdc++.h>
    using namespace std;
    const int N=10050;
    int ans[N];
    int main(){
        int n,m;
        scanf("%d%d",&n,&m);
        int Max=(n/2-1)*(n/2)+(n/2)*(n%2);
        if (m>Max){printf("-1
    ");return 0;}
        if (m==0){
            for(int i=n+1;i<=2*n;i++)printf("%d%c",i," 
    "[i==2*n]);
            return 0;
        }
        int num=0;
        ans[++num]=1;
        ans[++num]=2;
        for(int i=3;i<=n;i++){
            num++;
            int val=(i-1)/2;
            if (val<m){
                m-=val;
                ans[num]=ans[num-1]+ans[num-i+1];
            }else{
                ans[num]=ans[num-1]+ans[num-2*m];
                m=0;
                break;
            }
        }
        ans[num+1]=100000000;
        for(int i=num+2;i<=n;i++){
            ans[i]=ans[i-1]+ans[num]+1;
        }
        for(int i=1;i<=n;i++)printf("%d%c",ans[i]," 
    "[i==n]);
        
    

    F. Kuroni and the Punishment

    题解

    想了半天,首先最容易发现,每个奇数加1,是一种可行方案,(ansle n)

    之后想怎么枚举公约数,只要枚举质数就行了,但是显然没办法枚举太多。

    然后又想,应该很多点是不会变动的。因为平均每个变动不到一次((frac{ans}{n}<1))

    于是就想随遍抽取两个数,把他们的最大公约数作为所有数的公约数,重复100次。然后WA了,但是总感觉就是一个随机抽取。于是又想啊想。终于明白了。

    肯定至少有一半的数变动等于0或1。我们可以求变动为2的数最多为(k),则

    (2*kle nRightarrow kle frac{n}{2})。也就是说一半以上都是变动1或0。那我们随便抽一个数,它变动大于1的概率是(frac{1}{2}),我们抽取20次,一次都抽不到的概率则为(frac{1}{2^{20}}),这个概率几乎是不可能。也就是说抽20次肯定能抽到变动为0或1的数。那我们就把每次抽到的数(x,xpm1)进行质因数分解,再把这些质数当做公约数求一遍,取最小值即可。

    其实抽10次就已经差不多了。我试了两次,第一次过了,第二次WA在160个点左右。

    最后告诫大家最好不要用(rand()),因为它在codeforces上的范围是([0,32767])

    具体大家可以看看:https://www.cnblogs.com/RabbitHu/p/10390146.html

    代码

        #include<bits/stdc++.h>
        using namespace std;
        #define gcd __gcd
        //#define RAND_MAX 200050
        typedef long long ll;
        const int N=200050;
        set<ll> S;
        ll a[N],ans;
        int n;
        void dfs(ll x){
            ll len=(ll)(sqrt(x));
            for(ll i=2;i<=len;i++){
                if (x%i==0){
                    S.insert(i);
                    while(x%i==0)x/=i;
                }
            }
            if  (x>1)S.insert(x);
        }
        void check(ll x){
            ll tp=0;
            for(int i=1;i<=n;i++){
                if (a[i]<=x)
                    tp+=x-a[i];
                else 
                    tp+=min(a[i]%x,x-a[i]%x);
                if (tp>=ans)return;
         
            }
            ans=tp;
        }
        int main(){
            mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
            scanf("%d",&n);
            ll x,y;
            for(int i=1;i<=n;i++){
                scanf("%lld",&a[i]);
                if (a[i]&1)ans++;
            }
            for(int i=1;i<20;i++){
                x=rng()%n+1;
                dfs(a[x]);
                dfs(a[x]+1);
                if (a[x]>1)dfs(a[x]-1);
            }
            for(ll x:S)check(x);
            
            printf("%lld ",ans);
        }
    
  • 相关阅读:
    [bzoj1468]Tree(点分治)
    [bzoj1087]: [SCOI2005]互不侵犯King(状压dp)
    [hdu5628]Clarke and math(dirichlet卷积)
    [bzoj1036]:[ZJOI2008]树的统计Count(树链剖分)
    [bzoj1026][SCOI2009]windy数(前缀和+数位dp)
    洛谷 P1714 切蛋糕(dp+RMQ)
    [hdu3507] Print Article
    [bzoj1597]: [Usaco2008 Mar]土地购买
    php基础二
    php基础
  • 原文地址:https://www.cnblogs.com/mmmqqdd/p/12420301.html
Copyright © 2020-2023  润新知