zoukankan      html  css  js  c++  java
  • AMPPZ-2015 (MIPT Workshop Open 1)

    A. Album of Numbers

    设$cnt[i]$表示数字$i$的个数,则$ans=frac{sum_{i} i imes cnt[i]prod_{j>i}(cnt[j]+1)}{prod_{i}(cnt[i]+1)-1}$。

    不妨忽略分母的减$1$,那么只需要维护出答案以及$cnt+1$的乘积即可得到最终答案。

    用线段树维护每个区间内的答案以及$cnt+1$的乘积,注意到乘积很大时对答案的影响变化很小,所以可以直接对$10^{100}$取$min$。

    时间复杂度$O(nlog n)$。

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    const int N=555555;
    const double inf=1e100;
    int m,n,i,a[N],b[N];char op[N][9];
    double s[N],p[N];
    void build(int x,int a,int b){
      p[x]=1;
      if(a==b)return;
      int mid=(a+b)>>1;
      build(x<<1,a,mid);
      build(x<<1|1,mid+1,b);
    }
    void change(int x,int a,int b,int c,int o){
      if(a==b){
        p[x]+=o;
        s[x]=::a[a]*(p[x]-1)/p[x];
        return;
      }
      int mid=(a+b)>>1;
      if(c<=mid)change(x<<1,a,mid,c,o);else change(x<<1|1,mid+1,b,c,o);
      p[x]=min(p[x<<1]*p[x<<1|1],inf);
      s[x]=s[x<<1]+s[x<<1|1]/p[x<<1];
    }
    int main(){
      scanf("%d",&m);
      for(i=1;i<=m;i++){
        scanf("%s%d",op[i],&b[i]);
        a[i]=b[i];
      }
      sort(a+1,a+m+1);
      for(i=1;i<=m;i++)if(i==1||a[i]!=a[i-1])a[++n]=a[i];
      build(1,1,n);
      for(i=1;i<=m;i++){
        change(1,1,n,lower_bound(a+1,a+n+1,b[i])-a,op[i][0]=='+'?1:-1);
        printf("%.15f
    ",s[1]*(1.0+1.0/(p[1]-1)));
      }
    }
    

      

    B. Well Off

    对$x[i]$和$-x[i]$建图,若$a+b>0$,则连边$a>-b$,$b>-a$,拓扑排序判断是否存在环。

    #include<cstdio>
    const int N=200010,M=1000010;
    int n,m,i,g[N],v[M],nxt[M],ed,d[N],q[N],h,t;
    inline void add(int x,int y){v[++ed]=y;nxt[ed]=g[x];g[x]=ed;d[y]++;}
    inline int read(){
      char s[9];
      int x;
      scanf("%s%d",s,&x);
      x--;
      x<<=1;
      if(s[0]=='-')x++;
      return x;
    }
    int main(){
      scanf("%d%d",&n,&m);
      while(m--){
        int x=read();
        int y=read();
        add(x,y^1);
        add(y,x^1);
      }
      n*=2;
      for(h=1,i=0;i<n;i++)if(!d[i])q[++t]=i;
      while(h<=t){
        int x=q[h++];
        for(i=g[x];i;i=nxt[i])if(!(--d[v[i]]))q[++t]=v[i];
      }
      puts(t<n?"NIE":"TAK");
    }
    

      

    C. Accurate Shots (8Mb TL!)

    若$m>sqrt{n}$,则可以直接枚举$m$的所有倍数暴力判断。

    否则考虑折半爆搜,枚举左半边对$m$的余数,就知道右半边对$m$的余数,分别枚举出所有解即可。

    时间复杂度$O(sqrt{n})$。

    D. Prom

    排序后双指针。

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    const int N=250010;
    int n,m,lim,i,j,k,a[N],b[N];long long ans;
    int main(){
      scanf("%d%d%d",&n,&m,&lim);
      for(i=1;i<=n;i++)scanf("%d",&a[i]);
      for(i=1;i<=m;i++)scanf("%d",&b[i]);
      sort(a+1,a+n+1);
      sort(b+1,b+m+1);
      for(i=j=k=1;i<=n;i++){
        while(j<=m&&b[j]<=a[i]+lim)j++;
        while(k<=m&&b[k]<a[i]-lim)k++;
        ans+=j-k;
      }
      printf("%lld",ans);
    }
    

      

    E. Impressive Graphs

    在$O(nklog n)$的时间里维护出杨氏图表的前$k$行,则最终个数即为前$k$行的元素个数。

    输出方案部分留坑。

    F. Pen

    用栈进行括号匹配,栈为空时往左补充,最后将栈中未配对左括号配上右括号。

    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    using namespace std;
    const int N=1000010;
    int n,i,t,cb;char a[N],q[N],b[N];
    void NO(){
      puts("NIE");
      exit(0);
    }
    int main(){
      scanf("%s",a+1);
      n=strlen(a+1);
      for(i=1;i<=n;i++){
        if(a[i]=='('||a[i]=='{'||a[i]=='['){
          q[++t]=a[i];
        }
        if(a[i]==')'){
          if(t){
            if(q[t]=='(')t--;
            else NO();
          }else{
            b[++cb]='(';
          }
        }
        if(a[i]=='}'){
          if(t){
            if(q[t]=='{')t--;
            else NO();
          }else{
            b[++cb]='{';
          }
        }
        if(a[i]==']'){
          if(t){
            if(q[t]=='[')t--;
            else NO();
          }else{
            b[++cb]='[';
          }
        }
      }
      for(i=cb;i;i--)putchar(b[i]);
      for(i=1;i<=n;i++)putchar(a[i]);
      for(i=t;i;i--){
        if(q[i]=='(')putchar(')');
        if(q[i]=='[')putchar(']');
        if(q[i]=='{')putchar('}');
      }
    }
    

      

    G. Board Game

    将颜色分治,每次对于两边的颜色分别求出凸包,然后双指针求闵可夫斯基和的凸包。

    时间复杂度$O(nlog n)$。

    H. Scouts

    $f[i][j]$表示仅考虑$[i,j]$区间的答案,则

    $f[i][j]=min(max(f[i][k-1],f[k+1][j])+a[k]),ileq kleq j$

    维护出$max$的分界点后用一堆线段树维护即可。

    时间复杂度$O(n^2log n)$。

    #include<cstdio>
    typedef long long ll;
    const int N=2005;
    const ll inf=1LL<<60;
    int M,n,i,j,a[N],g;
    ll f[N][N];
    inline void up(ll&a,ll b){if(a>b)a=b;}
    struct ZKW{
    ll v[4100];
    inline void ask(int x,int y,ll&t){
      if(x>y)return;
      for(x+=M-1,y+=M+1;x^y^1;x>>=1,y>>=1){
        if(~x&1)up(t,v[x^1]);
        if(y&1)up(t,v[y^1]);
      }
    }
    inline void ins(int x,ll y){for(x+=M;x;x>>=1)up(v[x],y);}
    inline void build(){for(int i=1;i<=n+M;i++)v[i]=inf;}
    }T1[N],T2[N];
    inline void add(int x,int y){
      if(x>1)T1[y].ins(x-1,f[x][y]+a[x-1]);
      if(y<n)T2[x].ins(y+1,f[x][y]+a[y+1]);
    }
    int main(){
      scanf("%d",&n);
      for(M=1;M<n+2;M<<=1);
      for(i=1;i<=n;i++)scanf("%d",&a[i]),T1[i].build(),T2[i].build();
      for(i=1;i<=n;i++)add(i,i-1);
      for(i=1;i<=n;i++)f[i][i]=a[i],add(i,i);
      for(i=n;i;i--)for(g=i,j=i+1;j<=n;j++){
        while(g<=j&&f[i][g-1]<f[g+1][j])g++;
        f[i][j]=inf;
        T1[j].ask(i,g-1,f[i][j]);
        T2[i].ask(g,j,f[i][j]);
        add(i,j);
      }
      printf("%lld",f[1][n]);
    }
    

      

    I. Insects

    最大权闭合子图。

    #include<cstdio>
    const int N=2010,inf=1e9;
    struct E{int t,f;E*nxt,*pair;}*g[N],*d[N],pool[500000],*cur=pool;
    int n,m,i,j,k,x,y,S,T,h[N],gap[N],ans;
    inline void add(int s,int t,int f){
      E*p=cur++;p->t=t;p->f=f;p->nxt=g[s];g[s]=p;
      p=cur++;p->t=s;p->f=0;p->nxt=g[t];g[t]=p;
      g[s]->pair=g[t];g[t]->pair=g[s];
    }
    inline int min(int a,int b){return a<b?a:b;}
    int sap(int v,int flow){
      if(v==T)return flow;
      int rec=0;
      for(E*p=d[v];p;p=p->nxt)if(h[v]==h[p->t]+1&&p->f){
        int ret=sap(p->t,min(p->f,flow-rec));
        p->f-=ret;p->pair->f+=ret;d[v]=p;
        if((rec+=ret)==flow)return flow;
      }
      if(!(--gap[h[v]]))h[S]=T;
      gap[++h[v]]++;
      d[v]=g[v];
      return rec;
    }
    int main(){
      int _p,_ca,_ck,_ct;
      scanf("%d%d%d%d%d",&n,&_p,&_ca,&_ck,&_ct);
      S=256*3+n+1;
      T=S+1;
      ans=_p*n;
      for(i=1;i<=n;i++){
        add(S,i,_p);
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);x++,y++,z++;
        add(i,x+n,inf);
        add(i,y+n+256,inf);
        add(i,z+n+512,inf);
      }
      for(i=1;i<=256;i++){
        add(i+n,T,_ca);
        add(i+n+256,T,_ck);
        add(i+n+512,T,_ct);
      }
      for(gap[0]=T,i=1;i<=T;i++)d[i]=g[i];
      while(h[S]<T)ans-=sap(S,inf);
      printf("%d",ans);
    }
    

      

    J. Caves

    设$f[S][i]$表示走过了$S$集合,目前位于$i$的最优期望花费,转移带环,但是可以将不带环的部分的DP值排序后计算环的代价。

    时间复杂度$O(2^nn^2)$。

    K. Blocks

    从$n$到$1$填数,那么若放在两侧则能被看到,否则看不到。

    设$f[i][j]$表示$i$个数从两侧总计能看到$j$个的方案数,可以预处理,则

    $ans=f[n][l+p-1]C(l+p-2,l-1)$

    时间复杂度$O(n(l+p)+m)$。

    #include<cstdio>
    const int N=50010,K=210,P=1000000007;
    int Case,n,A,B,i,j,f[N][K+5],ans,C[K][K];
    inline void up(int&a,int b){a=a+b<P?a+b:a+b-P;}
    int main(){
      f[1][1]=1;
      for(i=1;i<N;i++)for(j=1;j<K;j++)if(f[i][j]){
        //inside
        up(f[i+1][j],1LL*(i-1)*f[i][j]%P);
        //outside
        up(f[i+1][j+1],f[i][j]);
      }
      for(C[0][0]=i=1;i<K;i++)for(C[i][0]=j=1;j<=i;j++)C[i][j]=(C[i-1][j-1]+C[i-1][j])%P;
      scanf("%d",&Case);
      while(Case--){
        scanf("%d%d%d",&n,&A,&B);
        ans=1LL*f[n][A+B-1]*C[A+B-2][A-1]%P;
        printf("%d
    ",ans);
      }
    }
    

      

    L. Postman

    固定起点之后按DFS序走最优,两遍树形DP求出每个点作为起点的答案即可。

    时间复杂度$O(n)$。

    #include<cstdio>
    typedef long long ll;
    const int N=1000010;
    int n,i,x,y,g[N],v[N<<1],nxt[N<<1],ed,size[N];
    ll f[N],h[N],s[N],ans;
    inline void add(int x,int y){v[++ed]=y;nxt[ed]=g[x];g[x]=ed;}
    void dfs(int x,int y){
      for(int i=g[x];i;i=nxt[i]){
        int u=v[i];
        if(u==y)continue;
        dfs(u,x);
        f[x]+=f[u]+1LL*size[u]*(size[x]*2+1);
        size[x]+=size[u];
      }
      size[x]++;
    }
    void dfs2(int x,int y){
      if(y){
        h[x]=s[y]-f[x]-1LL*size[x]*((n-size[x]-1)*2+1);
        h[x]+=n-size[x];
      }
      s[x]=f[x]+h[x]+1LL*(n-size[x])*(size[x]-1)*2;
      if(s[x]<ans)ans=s[x];
      for(int i=g[x];i;i=nxt[i]){
        int u=v[i];
        if(u==y)continue;
        dfs2(u,x);
      }
    }
    int main(){
      scanf("%d",&n);
      for(i=1;i<n;i++)scanf("%d%d",&x,&y),add(x,y),add(y,x);
      dfs(1,0);
      ans=f[1];
      dfs2(1,0);
      printf("%lld",ans);
    }
    

      

  • 相关阅读:
    Perl的Open函数
    较详细的介绍JNI
    Java多线程单元测试
    sleep函数的简单原理
    Struts与Servlet的冲突
    Ant学习记录
    JDK转码工具
    Throwable
    Entity Framework系列文章导航
    多核时代 .NET Framework 4 中的并行编程1概述
  • 原文地址:https://www.cnblogs.com/clrs97/p/8870818.html
Copyright © 2011-2022 走看看