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:1mi≠ki:A33∗(ki−mi−1)+2∗A33A22=A33∗(ki−mi)
一个技巧是令M= LG
M=Pk1−m11Pk2−m22...Pkn−mnn
这样就用算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;
}
}