zoukankan      html  css  js  c++  java
  • 2018 南京区域赛

    A - Adrien and Austin

    (n) 为奇数时先手必胜;为偶数时,若 (kleq1) ,则后手必胜,否则先手必胜。注意特判 (n=0)

    代码:

    #include <bits/stdc++.h>
    using namespace std;
    int main()
    {
        int n,k;
        scanf("%d%d",&n,&k);
        if(n==0)
        {
            printf("Austin
    ");
            return 0;
        }
        if(n%2==0&&k<=1)
            printf("Austin
    ");
        else
            printf("Adrien
    ");
        return 0;
    }
    

    I - Magic Potion

      一开始想的是二分图匹配,先进行一趟最大匹配,求出 (ans)。然后把匹配的点标记掉,再求一趟最大匹配,结果为 (res)。最终结果为:(ans+min(k,res))。但一直 (WA) 在第 (7) 个点。因为,第一遍求最大匹配时,最大匹配并非是唯一的,所以导致第二遍求的时候,就不一定是最大值。
      所以,改用最大流来求解。
    建图如下:(同时考虑人数和药水数量的限制)

    代码:

    #include <bits/stdc++.h>
    #define pb push_back
    using namespace std;
    const int inf=0x3f3f3f3f;
    const int N=1100;
    struct node
    {
        int to,val,rev;
    };
    vector<node>pic[N];
    queue<int>que;
    int layer[N],iter[N];
    int V,n,m,k;
    bool bfs()
    {
        for(int i=1;i<=V;i++) layer[i]=-1;
        while(!que.empty()) que.pop();
        que.push(1);
        layer[1]=0;
        while(!que.empty())
        {
            int now=que.front();
            que.pop();
            for(int i=0;i<pic[now].size();i++)
            {
                node tmp=pic[now][i];
                if(layer[tmp.to]<0&&tmp.val>0)
                {
                    layer[tmp.to]=layer[now]+1;
                    que.push(tmp.to);
                    if(tmp.to==V) return 1;
                }
            }
        }
        return 0;
    }
    int dfs(int v,int c)
    {
        if(v==V) return c;
        for(int &i=iter[v];i<pic[v].size();i++)
        {
            node &tmp=pic[v][i];
            if(layer[tmp.to]>layer[v]&&tmp.val>0)
            {
                int d=dfs(tmp.to,min(c,tmp.val));
                if(d>0)
                {
                    tmp.val-=d;
                    pic[tmp.to][tmp.rev].val+=d;
                    return d;
                }
            }
        }
        return 0;
    }
    int dinic()
    {
        int max_flow=0,f=0;
        while(bfs())
        {
            for(int i=1;i<=V;i++) iter[i]=0;
            while((f=dfs(1,inf))>0)
                max_flow+=f;
        }
        return max_flow;
    }
    void addedge(int v,int u,int w)
    {
        pic[v].pb(node{u,w,pic[u].size()});
        pic[u].pb(node{v,0,pic[v].size()-1});
    }
    int main()
    {
        int x,t;
        scanf("%d%d%d",&n,&m,&k);
        V=n+m+3;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&t);
            for(int j=1;j<=t;j++)
            {
                scanf("%d",&x);
                addedge(i+2,x+2+n,2);
            }
        }
        addedge(1,2,k);
        for(int i=3;i<=n+2;i++)
        {
            addedge(2,i,1);
            addedge(1,i,1);
        }
        for(int i=n+2+1;i<=2+n+m;i++) addedge(i,V,1);
        printf("%d
    ",dinic());
        return 0;
    }
    
    

    G - Pyramid

    (n^3) 打表,找规律:
    如下图:

    可以发现后面为等差数列,从后向前推。
    所以:

    [f[n]=1+sum_{i=1}^{n-1}{[4+sum_{j=1}^{i-1}{6+frac{(j-6)*(j-1)}{2}}]} ]

    一层一层拆开,利用公式:
    (1^2+2^2+...+n^2=frac{n*(n+1)*(2*n+1)}{6})

    (1^3+2^3+...+n^3=(1+2+3+...+n)^2)

    最后可以化简出:

    [f[n]=n+frac{1}{6}*[frac{n*(n-1)}{2}]^2+frac{n*(n-1)*(2*n-1)}{6}+frac{11*n*(n-1)}{12} ]

    代码:

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int mod=1e9+7;
    const ll inv2=500000004;
    const ll inv6=166666668;
    const ll inv12=83333334;
    ll f1(ll x)
    {
        return x*(x-1)%mod*inv2%mod;
    }
    ll f2(ll x)
    {
        ll res=(x-1)*x%mod*(2*x-1)%mod*inv6%mod;
        return res;
    }
    int main()
    {
        int t;
        ll n;
        scanf("%d",&t);
        while(t--)
        {
            scanf("%lld",&n);
            ll ans=n+f1(n)*f1(n)%mod*inv6%mod+f2(n)+11*n%mod*(n-1)%mod*inv12%mod;
            printf("%lld
    ",(ans%mod+mod)%mod);
        }
        return 0;
    }
    
    

    发现网上还有更加简单的公式。
    同样基于上述的表。(4) 次作差后差值恒定为 (1),说明递推式为:(f(n)=a*n^4+b*n^3+c*n^2+d*n+e),已知前 (5) 项,即可求出。

    [f[n]=frac{n^4+6*n^3+11*n^2+6*n}{24} ]

    下面基于差分来解决此题:

    首先,前 (7) 项为:1 5 15 35 70 126 210
    在前面加上 (f(0)=0)
    可以得到差分表:

    所以,根据图中的 ‘利用差分表求高级等差数列的前n项和’。
    可以推出:(全0行的前面行的第一个数为组合数前面的系数)
    (S(n)=0*C(n+1,1)+1*C(n+1,2)+3*C(n+1,3)+3*C(n+1,4)+1*C(n+1,5))
    (=frac{n*(n+1)*(n+2)*(n+3)*(n+4)}{120})

    作差可以求出 (f(n)=S(n)-S(n-1)=frac{n*(n+1)*(n+2)*(n+3)}{24})

    Problem J. Prime Game

    考虑每个质因子对于整体答案的贡献。
    (p) 个位置上的数,其包含的任意一个素因子,它原本应当产生的贡献有 ((n−p+1)⋅p)。但考虑到重复,记该素因子上一次出现的位置为 (q),则此时的贡献为:((p-q)*(n-p+1))。其他的就是质因子分解,筛出 (1e3) 以内的素数,共 (168) 个,用于素因子分解。

    代码:

    #include <bits/stdc++.h>
    #define pb push_back
    using namespace std;
    typedef long long ll;
    const int N=1e6+6;
    const int maxn=1e3+5;
    int prime[200],a[N],last[N];
    int cnt;
    bool vis[maxn];
    vector<int>fac[N];
    void euler()
    {
        cnt=0;
        for(int i=2;i<=1e3;i++)
        {
            if(!vis[i]) prime[++cnt]=i;
            for(int j=1;j<=cnt&&prime[j]*i<=1e3;j++)
            {
                vis[i*prime[j]]=1;
                if(i%prime[j]==0) break;
            }
        }
    }
    void divide(int num,int k)
    {
        for(int i=1;i<=cnt&&prime[i]*prime[i]<=num;i++)
        {
            if(num%prime[i]==0)
            {
                fac[k].pb(prime[i]);
                while(num%prime[i]==0) num/=prime[i];
            }
        }
        if(num>1) fac[k].pb(num);
    }
    int main()
    {
        euler();
        int n,a;
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a);
            divide(a,i);
        }
        ll ans=0;
        for(int i=1;i<=n;i++)
        {
            ll res=0;
            for(int j=0;j<fac[i].size();j++)
            {
                int t=fac[i][j];
                if(last[t]==0) res=1LL*i*(n-i+1);
                else res=1LL*(i-last[t])*(n-i+1);
                last[t]=i;
                ans+=res;
            }
        }
        printf("%lld
    ",ans);
        return 0;
    }
    
    

    Problem D. Country Meow

    最大值最小化问题,可以用三分枚举球心求解。

    最小球覆盖:
    模拟退火算法:

    #include <bits/stdc++.h>
    using namespace std;
    const double eps = 1e-8;
    const int inf = 0x3f3f3f3f;
    const double start_T = 1000;
    struct point3d
    {
        double x,y,z;
    }dat[150];
    int n;
    double dis(point3d a, point3d b)
    {
        return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)+(a.z-b.z)*(a.z-b.z));
    }
    double solve()
    {
        double step=start_T,ans=inf,mt;
        point3d z;
        z.x=z.y=z.z=0;
        int s=0;
        while(step>eps)
        {
            for (int i = 0; i < n; ++i)
            {
                if (dis(z,dat[s])<dis(z,dat[i])) s=i;
            }
            mt=dis(z,dat[s]);
            ans=min(ans,mt);
            z.x+=(dat[s].x-z.x)/start_T*step;
            z.y+=(dat[s].y-z.y)/start_T*step;
            z.z+=(dat[s].z-z.z)/start_T*step;
            step*=0.97;
        }
        return ans;
    }
    int main()
    {
        double ans;
        cin>>n;
        for (int i = 0; i < n; ++i)
            scanf("%lf%lf%lf",&dat[i].x,&dat[i].y,&dat[i].z);
        ans=solve();
        printf("%.8f
    ",ans);额
        return 0;
    }
    

    Problem K. Kangaroo Puzzle

    因为矩阵比较小,而可以进行的操作次数远远大于矩阵大小。所以,每次选定两个1进行合并,因为一个点走,其他的点也走。每次都让选定的第一个点走到第二个点的位置,进行若干次后,一定会成功追及。

    还可以采用随机化算法,生成随机数,进行 (50000) 次移动。

    代码:

    #include <bits/stdc++.h>
    using namespace std;
    char ss[30];
    char c[4]={'U','D','L','R'};
    int main()
    {
        int n,m;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
            scanf("%s",ss+1);
        for(int i=1;i<=50000;i++)
            printf("%c",c[rand()%4]);
        printf("
    ");
        return 0;
    }
    
    

    Problem M. Mediocre String Problem

    拓展 KMP+Manacher+差分
    对于从 (s) 中选出的字符串,可以分成两部分一部分与从 (t) 中选出的字符串恰好相反,一部分为回文串。
    对于回文串部分,可以用 (Manacher)(O(n)) 时间内求出每个位置的回文串长度。
    对于另一部分,先把 (s) 串反向,然后利用拓展KMP,求出反向后的串 (s^{'}) 的每个后缀与串 (t) 的最长公共前缀,就相当找到了原串 (s) 中与 (t) 部分相反的串。
    然后,就是把这两部分进行组合。
    对于回文串,需要求出每个位置可以是几个回文串的起始位置。利用差分解决,从后向前进行,在每个回文串的中心位置 (+1),在结束位置的下一个位置 (-1),每次累加。
    注意下标,一开始代码中 (num) 是以 (1) 为起始下标的,导致一直 (WA)(-1) 后就可以了。

    代码:

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 1e6 + 5;
    char ss[N], t[N], sr[N];
    char sn[N << 1];
    int sen[N << 1], num[N];
    int extend[N], nxt[N];
    int init()
    {
        int len = strlen(ss), cnt = 1;
        sn[0] = '$';
        sn[1] = '#';
        for (int i = 0; i < len;i++)
        {
            sn[++cnt] = ss[i];
            sn[++cnt] = '#';
        }
        sn[++cnt] = '';
        return cnt;
    }
    void  manacher()
    {
        int id, mx = 0;
        int len = init();//扩充到的最后一个位置坐标,而不是扩充后的长度
        //cout << "len=" << len << endl;
        for (int i = 1; i < len;i++)
        {
            sen[i] = mx > i ? min(sen[2 * id - i], mx - i) : 1;
            while(sn[i-sen[i]]==sn[i+sen[i]])
                sen[i]++;
            if(i+sen[i]>mx)
            {
                id = i;
                mx = i + sen[i];
            }
        }
        //差分记录:!!!!!!
        for (int i = len - 2; i >= 2;i--)
        {
            int t = i / 2;
            num[t-1]++;
            num[t - (sen[i] / 2)-1]--;
        }
        for (int i = len / 2 - 1; i >= 1; i--)
            num[i] += num[i + 1];
    }
    
    void getNext(char s[])//模式串
    {
        int ls=strlen(s),i=0;
        nxt[0]=ls;
        while(i<ls&&s[i]==s[i+1])
            i++;
        nxt[1]=i;
        int k=1;//已知的最大匹配的开始位置
        for(int i=2;i<ls;i++)
        {
            int len=k+nxt[k];//已知的最大匹配的结束位置
            nxt[i]=min(nxt[i-k],max(0,len-i));
            while(i+nxt[i]<ls&&s[nxt[i]]==s[i+nxt[i]])//如果已知的最大匹配不能满足要求,继续判断
                nxt[i]++;
            if(i+nxt[i]>k+nxt[k])//更新已知的最大匹配
                k=i;
        }
    }
    void getExtend(char sa[],char sb[])//主串,模式串
    {//与求next数组的步骤一样,不够是两个不同的字符串之间
        getNext(sb);
        int i=0,la=strlen(sa),lb=strlen(sb);
        while(sa[i]==sb[i]&&i<la&&i<lb)
            i++;
        extend[0]=i;
        int k=0;
        for(int i=1;i<la;i++)
        {
            int len=k+extend[k];
            extend[i]=min(nxt[i-k],max(0,len-i));
            while(i+extend[i]<la&&extend[i]<lb&&sb[extend[i]]==sa[i+extend[i]])//多了一个条件
                extend[i]++;
            if(i+extend[i]>k+extend[k])
                k=i;
        }
    }
    ll solve(int len)
    {//注意extend从0开始存
        ll ans = 0;
        for (int i = 0; i <len-1;i++)
            ans += 1LL*extend[len - i-1] * num[i+1];
        return ans;
    }
    int main()
    {
        scanf("%s", ss);
        scanf("%s", t);
        manacher();
        int len = strlen(ss);
        for (int i = 0; i < len;i++)//先翻转ss串
            sr[len - i - 1] = ss[i];
        getExtend(sr, t);
        printf("%lld
    ", solve(len));
        return 0;
    }
    
    

    Problem E. Eva and Euro coins

    归约。
    以下处理基于一个结论:一个位置的 (1) 可以在不跨越 (1) 的情况下,向左移动 (k) 个位置。
    对于连续的 (k)(1) 可以变换成 (0)
    那么就可以把连续的 (k)(0) 或者 (1) 消除。

    代码:

    #include <bits/stdc++.h>
    using namespace std;
    const int N=1e6+6;
    char s[N],t[N];
    int num[N][2];
    int n,k;
    void solve(char ss[])
    {
        int cnt=0;
        for(int i=1;i<=n;i++)
        {
            num[++cnt][0]=ss[i]-'0';
            num[cnt][1]=num[cnt-1][0]==ss[i]-'0'?num[cnt-1][1]+1:1;
            if(num[cnt][1]==k)
                cnt-=k;
        }
        for(int i=1;i<=n;i++)
        {
            if(i<=cnt) ss[i]=num[i][0]+'0';
            else ss[i]='0';
        }
        //cout<<ss+1<<endl;
    }
    int main()
    {
        scanf("%d%d",&n,&k);
        scanf("%s",s+1);
        scanf("%s",t+1);
        if(k==1)
        {
            printf("Yes
    ");
            return 0;
        }
        solve(s);
        solve(t);
        if(strcmp(s+1,t+1)==0)
            printf("Yes
    ");
        else
            printf("No
    ");
        return 0;
    }
    
    
  • 相关阅读:
    作业十一
    作业十
    作业九
    作业八
    作业七
    作业六
    作业五
    作业四
    eclipse+maven+web服务,实现对hdfs的目录浏览展示
    Eclipse+hadoop伪态式分布+API
  • 原文地址:https://www.cnblogs.com/1024-xzx/p/12957189.html
Copyright © 2011-2022 走看看