zoukankan      html  css  js  c++  java
  • 【题解】电子科技大学第十九届 ACM 程序设计竞赛

    第十四名 —— B站关注嘉然

    A

    动态规划

    (presum[i]) 为原序列位置 (i) 之前 (0)(1) 个数的差值

    (f[i]) 为最后一次在位置 (i) 操作,位置 (i) 之前 (0)(1) 个数的最大差值

    (f[i]=max{f[j]-presum[j]+presum[i-3]}),其中 (j<=i-3)

    (d[i]) 为最后一次在位置 (i) 操作,整个序列中 (0)(1) 个数的最大差值

    (d[i]=f[i]+presum[n]-presum[i])

    考虑所有最后一次操作可能位置的差值 (d[i]),以及一次也没有进行操作的差值 (presum[n])

    答案即为 (max{max{d[i]},presum[n]})

    #include <cstdio>
    #include <iostream>
    using namespace std;
    int n,m[100001],f[100001],presum[100001],premax[100001],ans;
    int solve(){
        int ret=0;
        for (int i=1;i<=n;i++) presum[i]=presum[i-1]+(m[i]==0?1:-1);
        for (int i=3;i<=n;i++){
            f[i]=premax[i-3]+presum[i-3];
            premax[i]=max(premax[i-1],f[i]-presum[i]);
        }
        for (int i=3;i<=n;i++){
            f[i]+=presum[n]-presum[i];
            ret=max(ret,f[i]);
        }
        ret=max(ret,presum[n]);
        return ret;
    }
    int main(){
        scanf("%d
    ",&n);
        for (int i=1;i<=n;i++){
            scanf("%c",&m[i]);
            m[i]-='0';
        }
        ans=solve();
        for (int i=1;i<=n;i++) m[i]=!m[i];
        ans=max(ans,solve());
        printf("%d",ans);
    }
    

    B

    模拟

    检测两个集合的关系,统计对应元素是否出现即可

    #include <cstdio>
    #include <set>
    using namespace std;
    int a[100001],b[100001];
    int p1=0,p2=0,p3=0;
    set<int> sa,sb;
    int main(){
        int n,m;
        scanf("%d",&n);
        for (int i=0;i<n;i++){
            scanf("%d",&a[i]);
            sa.insert(a[i]);
        }
        scanf("%d",&m);
        for (int i=0;i<m;i++){
            scanf("%d",&b[i]);
            sb.insert(b[i]);
        }
        for (int i=0;i<n;i++){
            if (sb.count(a[i])==0)
                p1=1;
        }
        for (int i=0;i<m;i++){
            if (sa.count(b[i])==0)
                p2=1;
            if (sa.count(b[i])==1)
                p3=1;
        }
        if (p1==0&&p2==0){
            printf("A equals B");
        }else{
            if (p1==1&&p2==0){
                printf("B is a proper subset of A");
            }else{
                if (p1==0&&p2==1){
                    printf("A is a proper subset of B");
                }else{
                    if (p3==0)
                        printf("A and B are disjoint");
                    else
                        printf("I am confused!");
                }
            }
        }
    }
    

    C

    数论

    用最小质因子 (2) 做桥梁即可将所有数联系起来,所以答案为 (max{2*(n以内的最大质数),n})

    #include <cstdio>
    typedef long long ll;
    const ll M=1000001;
    ll t,n,pri[M],a,b;
    ll ans[M];
    int main(){
        for (ll i=2;i<=M;i++) pri[i]=1;
        for (ll i=2;i<=M;i++){
            if (pri[i]){
                for (ll j=i*i;j<=M;j+=i)
                    pri[j]=0;
            }
        }
        b=2;
        for (ll i=3;i<=M;i++){
            if (pri[i]){
                a=i;
            }
            ans[i]=a*2;
        }
        scanf("%lld",&t);
        for (ll i=0;i<t;i++){
            scanf("%lld",&n);
            printf("%lld
    ",ans[n]>n?ans[n]:n);
        }
    }
    

    D

    字符串哈希

    对字符串做哈希后用 map 统计出现次数即可

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <map>
    #include <queue>
    #define ll long long
    using namespace std;
    struct P{
        int c1,c2;
        friend bool operator< (const P &a,const P &b){
            return a.c1!=b.c1?a.c1<b.c1:a.c2>b.c2;
        }
    }p[500001];
    int cnt;
    map<ll,int> s;
    priority_queue<P> q;
    ll calchash(char *s){
        int len=strlen(s);
        ll sum=0;
        for (int i=0;i<len;i++)
            sum+=131*sum+s[i];
        return sum;
    }
    char s1[500001][51],s2[500001][51];
    ll shash[500001];
    int main(){
        while(~scanf("%s %s",s1[cnt],s2[cnt])){
            shash[cnt]=calchash(s1[cnt]);
            if(s.count(shash[cnt])==0)
                s[shash[cnt]]=0;
            else
                s[shash[cnt]]++;
            p[cnt].c2=cnt;
            cnt++;
        }
        for (int i=0;i<cnt;i++){
            p[i].c1=s[shash[i]];
            q.push(p[i]);
        }
        while(!q.empty()){
            cout<<s1[q.top().c2]<<" "<<s2[q.top().c2]<<endl;
            q.pop();
        }
    }
    

    E

    离散化

    用优先队列做离散化之后模拟即可

    #include <cstdio>
    #include <queue>
    #include <iostream>
    using namespace std;
    typedef pair<int,int> P;
    const int N=200001;
    priority_queue<P,vector<P>,greater<P> > q1;
    priority_queue<int,vector<int>,greater<int> > q2;
    int n,s,t,x=1,ans=0;
    int main(){
        scanf("%d",&n);
        for (int i=0;i<n;i++){
            scanf("%d%d",&s,&t);
            q1.push(P(s,t));
        }
        while (!q1.empty()||!q2.empty()){
            if (q2.empty()) x=q1.top().first;
            while (!q1.empty()&&q1.top().first==x){
                P p=q1.top();q1.pop();
                q2.push(p.second);
            }
            while (!q2.empty()&&q2.top()<x) q2.pop();
            if (q2.top()>=x) {
                q2.pop();
                ans++;
                x++;
            }
        }
        printf("%d",ans);
    }
    

    F

    广度优先搜索

    #include <cstdio>
    #include <iostream>
    #include <queue>
    using namespace std;
    struct P{
        int x,y,len;
        P(int ax,int ay,int alen){
            x=ax,y=ay,len=alen;
        }
    };
    int n,x,y,a,b,ans,map[1003][1003];
    int dir[4][2]={{0,1},{1,0},{0,-1},{-1,0}};
    queue<P> q;
    int main(){
        scanf("%d%d%d",&x,&y,&n);
        x+=501,y+=501;
        for (int i=0;i<n;i++){
            scanf("%d%d",&a,&b);
            a+=501,b+=501;
            map[a][b]=1;
        }
        q.push(P(501,501,0));
        while (!q.empty()){
            P k=q.front();
            if (k.x==x&&k.y==y) ans=k.len;
            for (int i=0;i<4;i++){
                int ax=k.x+dir[i][0],ay=k.y+dir[i][1];
                if (0<=ax&&ax<1003&&0<=ay&&ay<1003&&map[ax][ay]==0){
                    map[ax][ay]=1;
                    q.push(P(ax,ay,k.len+1));
                }
            }
            q.pop();
        }
        printf("%d",ans);
    }
    

    J

    贪心

    去掉绝对值符号后,比较大的 (n/2) 个数符号取正,比较小的 (n/2) 个数符号取负,靠中间的两个数特殊处理一下即可

    #include <cstdio>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    ll n,h[100001],ans,ans2;
    int main(){
        scanf("%lld",&n);
        for (ll i=0;i<n;i++) scanf("%lld",&h[i]);
        sort(h,h+n);
        if (n%2==0){
            for (ll i=0;i<n/2;i++) ans-=h[i]*2;
            for (ll i=n/2;i<n;i++) ans+=h[i]*2;
            ans+=h[n/2-1]-h[n/2];
        }else{
            for (ll i=0;i<n/2;i++) ans-=h[i]*2;
            for (ll i=n/2;i<n;i++) ans+=h[i]*2;
            ans+=-h[n/2]-h[n/2+1];
            for (ll i=0;i<n/2+1;i++) ans2-=h[i]*2;
            for (ll i=n/2+1;i<n;i++) ans2+=h[i]*2;
            ans2+=h[n/2]+h[n/2-1];
            ans=max(ans,ans2);
        }
        printf("%lld",ans);
    }
    

    K

    图论

    维护每个节点 (x) 的子树大小 (siz[x]),所有子节点到节点 (x) 的路径和 (len[x])

    节点 (1) 连接到节点 (i) 时,形成一个基环外向树,设节点 (x) 的外向树大小为 (subsiz[x])

    基环上节点 (i) 的外向树大小为 (subsiz[i]=siz[i]),基环上其他节点 (j) 的外向树大小为 (subsiz[j]=siz[j]-siz[son[j]]),其中 (son[j]) 为基环上节点 (j) 的子节点

    考虑基环上每个节点 (x) 的外向树对答案的贡献为 (subsiz[x]*cirlen[x]+len[x]),其中 (cirlen[x]) 为节点 (x) 通过基环到达节点 (1) 的最短路径长度

    随着基环长度变化,基环上部分节点的 (cirlen[x]) 也会变化,但是考虑到会变化的区间是连续靠下方的半段,所以可以维护这一部分变化区间的 (subsiz[x]) 的和 (downsum)

    每次更新直接对答案加上对应的区间和 (downsum) 即可用 (O(1)) 的复杂度维护答案中的 (subsiz[x]*cirlen[x])

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    using namespace std;
    const int N=1e5+1;
    int n,u,v,siz[N],len[N],idxsta[N],dep,substa[N],downsum,ans,fans=1e9;
    int sz,head[N],vis[N];
    struct E{
        int next,to;
    }e[N*2];
    void insert(int a,int b){
        sz++;
        e[sz].next=head[a];
        head[a]=sz;
        e[sz].to=b;
    }
    void dfs(int x){
        vis[x]=1;
        siz[x]=1;
        len[x]=0;
        for (int i=head[x];i;i=e[i].next){
            int v=e[i].to;
            if (!vis[v]){
                dfs(v);
                siz[x]+=siz[v];
                len[x]+=len[v]+siz[v];
            }
        }
    }
    void solve(int x){
        dep++;
        vis[x]=1;
        idxsta[dep]=x;
        if (dep%2==1) downsum-=substa[dep/2+1];
        downsum+=siz[x];
        ans+=len[x];
        ans+=downsum;
        fans=min(ans,fans);
        ans-=downsum;
        ans-=len[x];
        downsum-=siz[x];
        if (dep%2==1) downsum+=substa[dep/2+1];
        for (int i=head[x];i;i=e[i].next){
            int v=e[i].to;
            if (!vis[v]){
                substa[dep]=siz[x]-siz[v];
                if (dep%2==1) downsum-=substa[dep/2+1];
                downsum+=substa[dep];
                ans+=len[x]-len[v]-siz[v];
                ans+=downsum;
                solve(v);
                ans-=downsum;
                ans-=len[x]-len[v]-siz[v];
                downsum-=substa[dep];
                if (dep%2==1) downsum+=substa[dep/2+1];
            }
        }
        dep--;
    }
    int main(){
        scanf("%d",&n);
        for (int i=1;i<n;i++){
            scanf("%d%d",&u,&v);
            insert(u,v);
            insert(v,u);
        }
        dfs(1);
        memset(vis,0,sizeof(vis));
        solve(1);
        printf("%d",fans);
    }
    

    L

    回文串

    Manacher 结合前缀和,统计每种长度对应的字符串个数即可

    #include <cstdio>
    #include <algorithm>
    #include <cmath>
    using namespace std;
    typedef long long ll;
    const ll M=100001,K=1e9+7;
    ll n,k,d[M],mul[M],cnt[M],sum=0,ans=1;
    char s[M];
    ll binpow(ll a, ll b, ll m) {
      a %= m;
      ll res = 1;
      while (b > 0) {
        if (b & 1) res = res * a % m;
        a = a * a % m;
        b >>= 1;
      }
      return res;
    }
    int main(){
        scanf("%lld%lld
    ",&n,&k);
        scanf("%s",s);
        for (ll i = 0, l = 0, r = -1; i < n; i++) {
            ll k = (i > r) ? 1 : min(d[l + r - i], r - i);
            while (0 <= i - k && i + k < n && s[i - k] == s[i + k]) {
                k++;
            }
            d[i] = k--;
            if (i + k > r) {
                l = i - k;
                r = i + k;
            }
        }
        for (ll i=0;i<n;i++){
            d[i]=d[i]*2-1;
            cnt[d[i]]++;
        }
        for (ll i=n/2*2+1;i>=1;i-=2){
            sum+=cnt[i];
            if (k>sum){
                ans=ans*binpow(i,sum,K)%K;
                k-=sum;
            }else{
                ans=ans*binpow(i,k,K)%K;
                k=0;
            }
        }
        if (k>0) ans=-1;
        printf("%lld",ans);
    }
    

    N

    贪心

    选最小的 (k/2) 个坐标和最大的 (k/2) 个坐标即可

    #include <cstdio>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    ll n,k,x[100001],ans;
    int main(){
        scanf("%lld%lld",&n,&k);
        for (ll i=0;i<n;i++) scanf("%lld",&x[i]);
        sort(x,x+n);
        for (ll i=0;i<k/2;i++)
            ans-=(k-1-i*2)*x[i];
        for (ll i=0;i<k/2;i++)
            ans+=(k-1-i*2)*x[n-1-i];
        printf("%lld",ans);
    }
    
  • 相关阅读:
    知物由学 | 未来安全隐患:AI的软肋——故意欺骗神经网络
    IT和非IT人士:2分钟了解什么是区块链
    追踪掠食者:地下灰产如何撸死创业公司?
    机器学习、深度学习、和AI算法可以在网络安全中做什么?
    一文了解安卓APP逆向分析与保护机制
    如何做好iOS应用安全?这有一把行之有效的“三板斧”
    微服务化不同阶段 Kubernetes 的不同玩法
    从B站、爱奇艺、映客的IPO上市,看国内视频公司的内容审核现状
    知物由学 | 你的网络安全问题背后的真正原因
    感动到流泪!数据分析师的福音:跨视图粒度计算
  • 原文地址:https://www.cnblogs.com/algonote/p/14777782.html
Copyright © 2011-2022 走看看