910考试题解

时间限制: 1 Sec  内存限制: 128 MB

 

题解

       从看到题的那一刻就开始满脑子“枚举解”“特判”一堆乱七八糟的玩意,然后后来还真特判完了就开始枚举了……然而考了也不知道多长时间以后,看了一眼题,ax+by=c,我为什么只知道题面是个方程没注意是这个方程呢???明显是扩展欧几里德的那个式子……扩欧我还是会打的,但是这道题也教会了我“你只是会打而已”。

       尽管是扩欧也分类讨论得很细,这已经不是特判不特判的问题了,而是这题本来需要如此。每组输入时就把a变为非负数。ab都为0,则c=0有无穷解c!=0无解。a=b=1时解的个数就是c-1。a+b=c且b大于0时只有1,1一组解。除去上述情况,b>0时就需要扩欧,求出最小解之后要用到那个我从来没打过的递推原理统计解的个数……b=0时相当于看c=ax有无解,c是a的倍数就有无穷解否则无解。b<0时看c是否是gcd(a,b)的倍数,是则有无穷解否则无解。

       后来才想明白的有两个点。一是b<0为何看gcd(a,b)与c的关系,因为这样相当于m*gcd(a,b)-n*gcd(a,b)=c,只要c是gcd(a,b)的倍数就变成了m-n=c/gcd(a,b)的解,这样当然可以m与n同增同减有无穷解,否则就变成了两个整数之差是一个小数,因此无解。二是扩欧之后求解的个数的方法,不定方程的通解是x = x0 + (b/gcd)*t ; y = y0 – (a/gcd)*t,我们一般扩欧求出的是x0,应该据此求y0,因为y是递减的,这样可以根据y是否大于0方便地求出共有多少组解。

 

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
int ca,a,b,c,ans,tp,gcd;
int e_gcd(int x,int y,int &m,int &n)
{
    if(y==0)
    {
       m=1,n=0;
       return x;
    }
    int jg=e_gcd(y,x%y,m,n);
    int kk=m;
    m=n;
    n=kk-x/y*n;
    return jg;
}
int ny(int x,int y,int z)
{
    int m,n;
    gcd=e_gcd(x,y,m,n);
    if((z%gcd)!=0) return -1;
    y/=gcd;
    m*=z/gcd;
    if(y<0) y=-y;
    int ans=m%y;
    if(ans<=0) ans+=y;
    return ans;
}
int main()
{
    scanf("%d",&ca);
    for(int l=1;l<=ca;l++)
    {
       scanf("%d%d%d",&a,&b,&c);
       if(abs(a)<abs(b))  tp=a,a=b,b=tp;
       if(a<0)  a=-a,b=-b,c=-c;
       if(a==0&&b==0)
       {
          if(c==0)  printf("ZenMeZheMeDuo\n");
          else printf("0\n");
          continue;
       }//both zero
       if(a==b&&a==1)
       {
          if(c<=1)
          {
             printf("0\n");
             continue;
          }
          if(c-1>65535) printf("ZenMeZheMeDuo\n");
          else printf("%d\n",c-1);
          continue;
       }//a==b==1
       if(a+b==c&&b>=0)
       {
          printf("1\n");
          continue;   
       }//a+b==c&&a>=0&&b>=0
       ans=0;
       if(b>0)
       {
         tp=ny(a,b,c);
         if(tp==-1)
         {  
           printf("0\n");
           continue;
         }
         tp=(c-tp*a)/b;
         if(tp>0)  
         {
            ans=tp*gcd/a;
            if(ans*a/gcd!=tp) ans++;
         }
         if(ans<=65535) printf("%d\n",ans);
         else printf("ZenMeZheMeDuo\n");
         continue;
       }//e_gcd
       if(b==0)
       {
          if(c>=0&&(c%a)==0) printf("ZenMeZheMeDuo\n");
          else printf("0\n");
       }//a>0&&b==0  c=ax
       if(b<0) 
       {
         ny(a,b,c);
         if(c%gcd==0)  printf("ZenMeZheMeDuo\n");
         else printf("0\n");
       }
    }
    return 0;
}
fuction

 

时间限制: 1 Sec  内存限制: 128 MB

 

题解

      刚开始想了想树dp,感觉这个转移有点奇怪,然后就去想别的思路了。贪心显然没什么道理,最后打了个十分复杂的小范围状压,但是仿佛打挂了,没有拿分。

       这个树dp用到了一种很神奇的思路,对于每条边都考虑它对全局的贡献。f[i][j]表示以i为根的树中有j个黑点,则这棵子树以外有m-j个黑点,以此类推白点的个数也能确定出来,这条边对答案的贡献就是可知的。当用子节点求父节点的f时,枚举父节点有多少个黑点,又有多少个在这个子树里,像背包一样转移,转移之后再更新父节点的子树大小,时间复杂度是n^2的(然而我并没有兴趣证明~)。很久没有打过背包了,连枚举的顺序都没注意到,调了半天才想起来还有这回事。好多东西并不是难理解或者容易出错,只是忘掉了最基本的原理。

 

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int sj=2020;
int n,m,h[sj],e,fa[sj],a1,a2,a3,size[sj];
long long f[sj][sj],tp;
struct B
{
     int ne,v,w;
}b[sj<<1];
void add(int x,int y,int z)
{
     b[e].v=y,b[e].ne=h[x],b[e].w=z,h[x]=e++;
     b[e].v=x,b[e].ne=h[y],b[e].w=z,h[y]=e++;
}
void bj(long long &x,long long y)
{
     x=x>y?x:y;
}
void dfs(int x)
{
     size[x]=1;
     for(int i=h[x];i!=-1;i=b[i].ne)
       if(b[i].v!=fa[x])
       {
          fa[b[i].v]=x;
          dfs(b[i].v);
          a1=size[x]+size[b[i].v];
          if(a1>m) a1=m;
          for(int k=a1;k>=0;k--)
          {
            a2=k-size[x];
            if(a2<0) a2=0;
            for(int j=a2;j<=size[b[i].v]&&j<=k;j++)
            {
              tp=(long long)((long long)j*(m-j)+(long long)(size[b[i].v]-j)*(n-m-(size[b[i].v]-j)))*b[i].w;
              bj(f[x][k],f[x][k-j]+f[b[i].v][j]+tp);    
            }
          }
          size[x]+=size[b[i].v];
       }
}
int main()
{
    scanf("%d%d",&n,&m);
    memset(h,-1,sizeof(h));
    for(int i=1;i<n;i++)
    {
       scanf("%d%d%d",&a1,&a2,&a3);
       add(a1,a2,a3);
    }
    dfs(1);
    printf("%lld",f[1][m]);
    return 0;
}
coloration

 

posted @ 2017-09-22 15:01  moyiii  阅读(322)  评论(0编辑  收藏  举报