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\n",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\n",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\n",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\n",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\n",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\n",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\n",sum);
        else printf("%d\n",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\n",ans);
    return 0;
}

 

posted @ 2019-02-17 21:38  nimphy  阅读(198)  评论(0编辑  收藏