zoukankan      html  css  js  c++  java
  • Gym 101889:2017Latin American Regional Programming Contest(寒假自训第14场)

    昨天00.35的CF,4点才上床,今天打的昏沉沉的,WA了无数发。 题目还是满漂亮的。 尚有几题待补。

    C .Complete Naebbirac's sequence

    题意:给定N个数,他们在1到K之间,现在1到K的出现次数的不完全相同的,现在让你进行一次操作,使得他们相同。 操作是加一个数到集合; 或者删去一个数; 或同时一个数,删一个数。

    思路:枚举三种情况即可。

    #include<bits/stdc++.h>
    #define ll long long
    #define rep(i,a,b) for(int i=a;i<=b;i++)
    using namespace std;
    const int maxn=200010;
    int a[maxn],num[maxn],N,M;
    bool checkdel()
    {
        int cnt=0,x=0;
        if((N-1)%M==0) {
            rep(i,1,M) if(num[i]==(N-1)/M+1) x=i;
            else if(num[i]==(N-1)/M) cnt++;
        }
        else return false;
        if((cnt==M-1)&&x) {
            printf("-%d
    ",x);
            return true;
        }
        return false;
    }
    bool checkadd()
    {
        bool F=true; int x=0,cnt=0;
        if((N+1)%M!=0) return false;
        rep(i,1,M) if(num[i]==(N+1)/M) cnt++;
        else if(num[i]==(N+1)/M-1)x=i;
        if((cnt==M-1)&&x){
            printf("+%d
    ",x);
            return true;
        }
        return false;
    }
    bool checkadc()
    {
        bool F=true; int cnt=0,x=0,y=0;
        if(N%M!=0) return false;
        rep(i,1,M) {
            if(num[i]==N/M) cnt++;
            else if(num[i]>N/M) x=i;
            else if(num[i]<N/M) y=i;
        }
        if(cnt==M-2&num[x]==num[y]+2) {
            printf("-%d +%d
    ",x,y); return true;
        }
        return false;
    }
    int main()
    {
        scanf("%d%d",&M,&N);
        rep(i,1,N) scanf("%d",&a[i]),num[a[i]]++;
        if(checkadd()) return 0;
        if(checkadc()) return 0;
        if(checkdel()) return 0;
        puts("*");
        return 0;
    }

    D .Daunting device

    题意:N个格子排出一排,开始格子颜色都是1;现在有M个操作: N,M<1e5

               或,把区间[L,R]颜色改为c;   或,查询一共有多少格子颜色为c。  最后求颜色最多的数量。

    数据是随机的,且强制在线。

    思路:ODT裸题。维护相同颜色的区间。注意split的时候先split(R+1),再split(L);因为这个我wa20了;

             在这里有写。 大概就是会改变指针啥的,我对iterator不了解,所以不知道,记下来好了。

    开始想的分块也能做,但是我感觉还带个log。N*sqrt*log,数据水的话没准可以。

    #include<bits/stdc++.h>
    #define ll long long
    #define rep(i,a,b) for(int i=a;i<=b;i++)
    using namespace std;
    struct in{
        int L,R,v;
        in(){}
        in(int LL,int RR,int vv):L(LL),R(RR),v(vv){}
        friend bool operator<(in a,in b){
            if(a.L==b.L) return a.R<b.R;
            return a.L<b.L;
        }
    };
    set<in>s;
    #define IT set<in>::iterator
    int num[100010],ans;
    IT split(int pos)
    {
        IT it=s.lower_bound(in(pos,-2,-2));
        if(it!=s.end()&&(*it).L==pos) return it;
        it--; //if(pos>it->R) return s.end();
        int l=(*it).L,r=(*it).R,v=(*it).v;
        s.erase(it);
        s.insert(in(l,pos-1,v));
        return s.insert(in(pos,r,v)).first;
    }
    void Assign(int L,int R,int X)
    {
        IT it2=split(R+1),it1=split(L);
        for(IT it=it1;it!=it2;it++) {
            num[(*it).v]-=((*it).R-(*it).L+1);
        }
        s.erase(it1,it2);
        s.insert(in(L,R,X));
        num[X]+=R-L+1;
    }
    int main()
    {
        int N,L,C,P,X,A,B,S;
        scanf("%d%d%d",&L,&C,&N);
        s.insert(in(0,L-1,1)); num[1]=L;
        rep(i,1,N){
            scanf("%d%d%d%d",&P,&X,&A,&B); A%=L; B%=L;
            S=num[P]%L;
            int l=(A+1LL*S*S%L)%L,r=(A+1LL*(S+B)*(S+B)%L)%L;
            Assign(min(l,r),max(l,r),X);
        }
        rep(i,1,C) ans=max(ans,num[i]);
        printf("%d
    ",ans);
        return 0;
    }

    E .Enigma

    题意:给定一个字符串c[]和整数N,,或者是'?',或者是数字, '?'表示你可以替换为数字。 让你求字典序最小的c,满足他是N的倍数, |c|,N<1000;无解输出"*";

    思路:显然是DP,dp[i][j]表示前i位%N是否可以为j; 开始时,dp[0][0]=1; 然后做背包,最后倒着来,就可以得到最小字典序了。O(N^2);

    注意第一位不能为0;

    #include<bits/stdc++.h>
    #define ll long long
    #define rep(i,a,b) for(int i=a;i<=b;i++)
    #define rep2(i,a,b) for(int i=a;i>=b;i--)
    using namespace std;
    const int maxn=1010;
    char c[maxn]; int dp[maxn][maxn],N,L,fac[maxn];
    int main()
    {
        scanf("%s%d",c+1,&N);
        L=strlen(c+1);
        fac[0]=1%N; rep(i,1,L) fac[i]=fac[i-1]*10%N;
        dp[L+1][0]=1;
        rep2(i,L,1){
            if(c[i]=='?'){
                rep(k,0,9)
                 rep(j,0,N-1)
                  dp[i][(j+k*fac[L-i])%N]|=dp[i+1][j];
            }
            else {
               rep(j,0,N-1)
                 dp[i][(j+(c[i]-'0')*fac[L-i])%N]|=dp[i+1][j];
            }
        }
        if(!dp[1][0]) puts("*");
        else{
            int now=0;
            rep(i,1,L){
                if(c[i]!='?'){
                    putchar(c[i]);
                    now=((now-(c[i]-'0')*fac[L-i])%N+N)%N;
                }
                else{
                    rep(j,0,9){
                    if(i==1&&j==0) continue;
                      if(dp[i+1][((now-j*fac[L-i])%N+N)%N]) {
                        printf("%d",j);
                        now=((now-j*fac[L-i])%N+N)%N;
                        break;
                     }
                    }
                }
            }
        }
        return 0;
    }

    F .Fundraising

    题意:现在有N个慈善家,每个人有ai和bi属性,以及准备捐的款ci。有矛盾的慈善家不能同时出场,两个人有矛盾,当前仅当(ai-aj)*(bi-bj)<0;问怎么安排出场捐款最大。

    思路:典型的一维排序,一维用线段树或者树状数组更新DP值。

    注意有两个人的a和b完全相同的情况,我们把ci小的那个人删去。

    #include<bits/stdc++.h>
    #define ll long long
    #define rep(i,a,b) for(int i=a;i<=b;i++)
    using namespace std;
    const int maxn=1000010;
    struct in{
        int b,f;ll  d;
        friend bool operator <(in w,in v){
            if(w.b!=v.b) return w.b<v.b;
            return w.f>v.f;
        }
    }s[maxn];
    ll Mx[maxn],res[maxn],ans; int b[maxn],tot;
    void update(int Now,int L,int R,int pos,ll val)
    {
        if(L==R){Mx[Now]=max(Mx[Now],val); return ;}
        int Mid=(L+R)>>1;
        if(pos<=Mid) update(Now<<1,L,Mid,pos,val);
        else update(Now<<1|1,Mid+1,R,pos,val);
        Mx[Now]=max(Mx[Now<<1|1],Mx[Now<<1]);
    }
    ll query(int Now,int L,int R,int l,int r)
    {
        if(l<=L&&r>=R) return Mx[Now];
        int Mid=(L+R)>>1; ll res=0;
        if(l<=Mid) res=max(res,query(Now<<1,L,Mid,l,r));
        if(r>Mid) res=max(res,query(Now<<1|1,Mid+1,R,l,r));
        return res;
    }
    int main()
    {
        int N; scanf("%d",&N);
        rep(i,1,N){
            scanf("%d%d%lld",&s[i].b,&s[i].f,&s[i].d);
            b[++tot]=s[i].f;
        }
        sort(b+1,b+tot+1); tot=unique(b+1,b+tot+1)-(b+1);
        rep(i,1,N) s[i].f=lower_bound(b+1,b+tot+1,s[i].f)-b;
        sort(s+1,s+N+1);
        rep(i,1,N-1) {
            if(s[i].f==s[i+1].f&&s[i].b==s[i+1].b) s[i+1].d+=s[i].d,s[i].d=0;
        }
        sort(s+1,s+N+1);
        rep(i,1,N) {
            int j=i;
            while(j+1<=N&&s[j].b==s[j+1].b) j++;
            rep(k,i,j) {
                if(s[k].f>1)res[k]=query(1,1,tot,1,s[k].f-1);
                res[k]+=s[k].d;
                ans=max(ans,res[k]);
            }
            rep(k,i,j)
               update(1,1,tot,s[k].f,res[k]);
            i=j;
        }
        printf("%lld
    ",Mx[1]);
        return 0;
    }

    H .Hard choice

    by .pb 应该是个水题,懒得看了

    #include<bits/stdc++.h>
    #define ll long long
    using namespace std;
    int a,b,c,x,y,z;
    template<class T>
    inline void read(T&a){
        char c=getchar();
        for(a=0;(c<'0'||c>'9')&&c!='-';c=getchar());
        bool f=0;if(c=='-')f=1,c=getchar();
        for(;c>='0'&&c<='9';c=getchar())a=a*10+c-'0';
        if(f)a=-a;
    }
    int main(){
        read(a),read(b),read(c),read(x),read(y),read(z);
        printf("%d
    ",max(0,x-a)+max(y-b,0)+max(z-c,0)); 
        return 0;
    }

    I .Imperial roads

    题意:给定N点M边带权连通图,现在Q次询问,每次问在必选某条边的情况下,最小生成树的大小。

    思路:先建一个MST,大小为sum, 如果询问边已经在树上,那么答案是sum; 否则,ans=sum-len+maxedge(u->lca->v);

    即是用环里最大替换掉。。。老掉牙的基础题了,可以用树剖或者倍增求路径最大边权。

    #include<bits/stdc++.h>
    #define pii pair<int,int>
    #define rep(i,a,b) for(int i=a;i<=b;i++)
    #define rep2(i,a,b) for(int i=a;i>=b;i--)
    #define ll long long
    using namespace std;
    const int maxn=200010;
    struct in{
        int u,v,len;
        friend bool operator<(in a,in b){ return a.len<b.len; }
    }s[maxn];
    int fa[maxn],dep[maxn],f[maxn][21],Mx[maxn][21];
    int Laxt[maxn],Next[maxn<<1],To[maxn<<1],Len[maxn<<1],cnt;
    map<pii,int>mp;
    void add(int u,int v,int w){
        Next[++cnt]=Laxt[u]; Laxt[u]=cnt;To[cnt]=v;Len[cnt]=w;
    }
    int find(int x){
        if(x==fa[x]) return x;
        return fa[x]=find(fa[x]);
    }
    void dfs(int u,int pre)
    {
        f[u][0]=pre; dep[u]=dep[pre]+1;
        for(int i=Laxt[u];i;i=Next[i]){
            if(To[i]!=pre){
                dfs(To[i],u);
                Mx[To[i]][0]=Len[i];
            }
        }
    }
    int LCA(int u,int v)
    {
        if(dep[u]<dep[v]) swap(u,v);
        rep2(i,20,0) if(dep[f[u][i]]>=dep[v]) u=f[u][i];
        if(u==v) return u;
        rep2(i,20,0) if(f[u][i]!=f[v][i]) u=f[u][i],v=f[v][i];
        return f[u][0];
    }
    int query(int u,int v)
    {
        int Lca=LCA(u,v),res=0;
        rep2(i,20,0) if(dep[f[u][i]]>=dep[Lca]) {
            res=max(res,Mx[u][i]);u=f[u][i];
        }
        rep2(i,20,0) if(dep[f[v][i]]>=dep[Lca]) {
            res=max(res,Mx[v][i]);v=f[v][i];
        }
        return res;
    }
    int main()
    {
        int N,M,Q,u,v,sum=0;
        scanf("%d%d",&N,&M);
        rep(i,1,N) fa[i]=i;
        rep(i,1,M){
             scanf("%d%d%d",&s[i].u,&s[i].v,&s[i].len);
             if(s[i].u>s[i].v) swap(s[i].u,s[i].v);
             mp[pii(s[i].u,s[i].v)]=s[i].len;
        }
        sort(s+1,s+M+1);
        rep(i,1,M){
            int fu=find(s[i].u),fv=find(s[i].v);
            if(fu!=fv){
                mp[pii(s[i].u,s[i].v)]=0;
                fa[fu]=fv; add(s[i].u,s[i].v,s[i].len);
                add(s[i].v,s[i].u,s[i].len); sum+=s[i].len;
            }
        }
        dfs(1,0);
        rep(i,1,20)
         rep(j,1,N){
            f[j][i]=f[f[j][i-1]][i-1];
            Mx[j][i]=max(Mx[j][i-1],Mx[f[j][i-1]][i-1]);
        }
        scanf("%d",&Q);
        rep(i,1,Q){
            scanf("%d%d",&u,&v); if(u>v) swap(u,v);
            if(mp[pii(u,v)]==0) printf("%d
    ",sum);
            else printf("%d
    ",sum+mp[pii(u,v)]-query(u,v));
        }
        return 0;
    }

     

    J .Jumping frog

    题意:给定一个石头和水坑组成的环,顺时针依次是0到N-1;现在问对于K=[1,N-1],有多少个K满足,至少有一个点作为起点,每次顺时针跳到第K个点上,直到回到出发点,这个过程中不会跳到水坑中。

    思路:对于K,我们直到他遍历的点和起点的距离的N/gcd(N,K)的倍数。 所以我们可枚举N/gcd(N,K);这个数是N的因子,数量比较少,对于每个因子,我们去暴力找即可。

    还可以优化:如果k满足,那么k的倍数也满足。

    #include<bits/stdc++.h>
    #define rep(i,a,b) for(int i=a;i<=b;i++)
    using namespace std;
    const int maxn=100010;
    char c[maxn]; int fac[maxn],vis[maxn],tot,N,ans;
    bool judge(int bg,int L)
    {
        int Now=bg;
        while(true){
            if(c[Now]=='P') return false;
            Now=Now+L;
            if(Now>=N) Now-=N;
            if(Now==bg) break;
        }
        return true;
    }
    bool check(int L)
    {
        rep(i,0,L-1)
          if(judge(i,L)) return true;
        return false;
    }
    int main()
    {
        scanf("%s",c); N=strlen(c);
        rep(i,1,N-1) if(N%i==0) fac[++tot]=i;
        rep(i,1,tot) if(check(fac[i])) vis[fac[i]]=1;
        rep(i,1,N-1) ans+=vis[__gcd(i,N)];
        printf("%d
    ",ans);
        return 0;
    }
  • 相关阅读:
    【loj2639】[Tjoi2017]不勤劳的图书管理员
    【bzoj3514】Codechef MARCH14 GERALD07加强版
    BZOJ1002【FJOI2007】轮状病毒
    数论基础(附加例题)
    表达式总结
    背包
    hdu1027
    hdu1026
    hdu1025
    hdu1024
  • 原文地址:https://www.cnblogs.com/hua-dong/p/10392788.html
Copyright © 2011-2022 走看看