2013 ACM-ICPC吉林通化全国邀请赛

2013 ACM-ICPC吉林通化全国邀请赛——题目重现

D题HDU4496-D -City【并查集+逆向思考】

  • 题目大意

n个点,m条边,按照输入边的顺序一条一条地删去,每删一条询问当前的连通分量数目。

  • 分析

它的逆向过程就是每加入一条边就将两个点所在的集合合并,用并查集来维护。

  • 代码

#include<iostream>
#include<cstring>
#include<cmath>
#include<cstdio>
using namespace std;
int n,m;
int pa[100005];
int ans[1000005];
struct Edge
{
      int x;
      int y;
}edge[1000005];
void Init()
{
      for(int i=0;i<n;i++)
        pa[i]=i;
}
int Findset(int i)
{
      if(pa[i]!=i)
        pa[i]=Findset(pa[i]);
        return pa[i];
}
void Union(int a,int b)
{
      pa[a]=b;
}
int main()
{
  while(scanf("%d%d",&n,&m)!=EOF)
  {
      Init();
      for(int i=1;i<=m;i++)
      {
          scanf("%d%d",&edge[i].x,&edge[i].y);
      }
      ans[m]=n;
      for(int i=m;i>=1;i--)
      {
          if(Findset(edge[i].x)!=Findset(edge[i].y))
          {
              ans[i-1]=ans[i]-1;
              Union(Findset(edge[i].x),Findset(edge[i].y));
          }
                    else
            ans[i-1]=ans[i];
      }
      for(int i=1;i<=m;i++)printf("%d\n",ans[i]);
  }

}

E题HDU4497-GCD and LCM 【数论 质因子】

  • 题目大意

给出G、L,问有多少对(x,y,z)满足

{gcd(x,y,z)=Glcm(x,y,z)=L

  • 分析

1.因为gcd、lcm都和素数有关,将这些数都化为素数成积的形式

x=pa11pa22...panny=pb11pb22...pbnnz=pc11pc22...pcnn

又有
{G=pm11pm22...pmnnL=pk11pk22...pknn

2.
由gcd和lcm性质可知
{mi=min{ai,bi,ci}ki=max{ai,bi,ci}

3.
现在就是排列组合的问题了, pi pj 之间是独立的分类计数,直接算分两种情况:
mi=ki:1miki:A33(kimi1)+2A33A22=A33(kimi)

一个技巧是令M= LG
M=Pk1m11Pk2m22...Pknmnn

这样就用算M的质因数,并省去了判断 mi 否等于 ki

  • 代码
#include<iostream>
#include<cstring>
#include<cmath>
#include<cstdio>
using namespace std;
#define MAXN 50000
int T;
int G,L;
int prime[100005];
int CNT;
int ans;
struct  P
{
      int m;
      int c;
}p[100005];
void Find_prime()
{
      int notprime[100005];
      memset(notprime,0,sizeof(notprime));
      int cnt=0;
      for(int i=2;i<=100000;i++)
      {
          if(!notprime[i])prime[++cnt]=i;
          for(int j=1;i*prime[j]<=100000;j++)
          {
              notprime[i*prime[j]]=1;
              if(i%prime[j]==0)break;
          }
      }
}
void FindP(int M)
{
      int i=1;
    CNT=0;
      while(M>1)
      {
          if(prime[i]*prime[i]>M)
          {
              CNT++;
              p[CNT].m=M;
              p[CNT].c=1;
              return ;
          }
          if(M%prime[i]==0)
          {
              CNT++;
              p[CNT].m=prime[i];
              p[CNT].c=0;
              while(M%prime[i]==0) {p[CNT].c++;M/=prime[i];}
          }
          i++;
      }
}
int main()
{
   Find_prime();
   int M;
   cin>>T;

   while(T--)
   {
      cin>>G>>L;
      if((L%G)!=0){cout<<0<<endl;continue;}
      M=L/G;
      FindP(M);
       ans=1;
      for(int i=1;i<=CNT;i++)
      {
          ans*=(6*p[i].c);
      }
      cout<<ans<<endl;
   }
}

H题HDU4597-Play Game【区间动规】

  • 题目大意

两排长度为n的数列,Alice和Bob轮流取数(足够聪明)
每次只能从4个端点中的一个取,问先取的人能取到的最大数是多少

  • 分析

我的方法好像不是简洁,有空再改。
f[x1][y1][x2][y2] 表示第一排从 x1 y1 ,第二排从 x2 y2 先取的人(Alice)所能取到的最大数$
状态确定了之后就是状态的转移方程了,类似于最长公共子序列问题(LCS),就不多说了。

  • 代码
#include<iostream>
#include<cstring>
#include<cmath>
#include<cstdio>
using namespace std;
int T;
int n;
int a[25];
int b[25];
int f[25][25][25][25];
int sum[25][25][25][25];
int sa[25];
int sb[25];
int max(int a,int b)
{
     return a>b ?a : b;
}
void Init()
{
      for(int i=1;i<=n;i++)
      {
          sa[i]=sa[i-1]+a[i];
          sb[i]=sb[i-1]+b[i];
      }
      memset(f,0,sizeof(f));
}
int Sum(int L1,int R1,int L2,int R2)
{
      return sa[R1]-sa[L1-1]+sb[R2]-sb[L2-1];
}
int F(int L1,int R1,int L2,int R2)
{
      if(L1>R1 && L2>R2)return 0;
      if(f[L1][R1][L2][R2]!=0)return f[L1][R1][L2][R2];
      int ans=0;
      if(L1<=R1)
      {
        ans=max( Sum(L1,R1,L2,R2)- F(L1+1,R1,L2,R2) ,ans);
          ans=max( Sum(L1,R1,L2,R2)- F(L1,R1-1,L2,R2) ,ans);
      }
        if(L2<=R2)
        {
          ans=max( Sum(L1,R1,L2,R2)- F(L1,R1,L2+1,R2) ,ans);
          ans=max( Sum(L1,R1,L2,R2)- F(L1,R1,L2,R2-1) ,ans );
        }
      return f[L1][R1][L2][R2]=ans;
}
int main()
{
  cin>>T;
  while(T--)
  {
      cin>>n;
      for(int i=1;i<=n;i++)
          cin>>a[i];
            for(int i=1;i<=n;i++)
              cin>>b[i];
            Init();
            cout<<F(1,n,1,n)<<endl;
  }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值