A Problem - 1878A - Codeforces
这是一道签到题 我们可以发现只要数组a中存在k就行。
题目大意是将 x(1<= x <= 1e8) 分成n个整数(1<=n<=x)并使这n个数的gcd最大
我们假设x分成了 a1 a2 a3.....an且 gcd(a1,a2....an)=d;可以发现 d为x的约数。这样我们可以暴力枚举x的约数看看这个约数是否能满足条件。 条件是 x/d>=n 意思是我们至少要满足a1 a2...an都为d的倍数 。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
int i,j,n,m,k,l,o,p,t,dl[200001],num[200001],ans;
int main()
{
//freopen("1.in","r",stdin);
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
ans=1;
if(m==1) printf("%d\n",n);
else
{
for(i=2;i<=sqrt(n);i++)
if(n%i==0)
{
if(n/i>=m) ans=max(ans,i);
if(n/(n/i)>=m) ans=max(ans,(n/i));
}
printf("%d\n",ans);
}
}
}
C Multiset
这道题是权值线段树的模板题。
#include<bits/stdc++.h>
using namespace std;
int j,n,m,k,l,q,tree[6000001],x,a[1000001],i;
inline int read() {
int x = 0; bool neg = false; char ch = getchar();
while(!isdigit(ch)) (ch == '-') && (neg = true), ch = getchar();
while(isdigit(ch)) x = x * 10 + ch - '0', ch = getchar();
return neg ? -x : x;
}
void insert(int x,int l,int r,int i,int j)
{
tree[x]+=j;
if(l==r) return;
int mid=(l+r)>>1;
if(i<=mid) insert(x<<1,l,mid,i,j);
else insert(x<<1|1,mid+1,r,i,j);
}
void dele(int x,int l,int r,int i,int j)
{
if(l==r)
{
if(j==1)tree[x]--;
else printf("%d\n",l);
return;
}
int mid=(l+r)>>1;
if(tree[x<<1]>=i) dele(x<<1,l,mid,i,j);
else dele(x<<1|1,mid+1,r,i-tree[x<<1],j);
tree[x]--;
}
int main()
{
//freopen("1.in","r",stdin);
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++)
{
x=read();
++a[x];
}
for(i=1;i<=n;i++)
if(a[i]>0) insert(1,1,n,i,a[i]);
for(i=1;i<=m;i++)
{
x=read();
if(x>0) insert(1,1,n,x,1);
else dele(1,1,n,-x,1);
}
if(tree[1]==0) printf("0\n");
else dele(1,1,n,1,2);
}
D 同余方程
这道题要求的最小正整数解。 相当于求ax+by=1。所以我们可以用exgcd来做这道题。 但我们发现exgcd是求 ax+by=gcd(a,b)的解。这和我们要的有点出入。不过题目已经给出保证有解,即ax+by=1有解。 一个方程 ax+by=m 有解的必要条件是 m mod gcd(a,b)=0; 所以可以推出 gcd(a,b)=1; 接下来只要求ax+by=1就行。 最后注意题目要求正整数解。
E 排列计数
对于这道题我们相当于要求 其中
表示1~x错排的方案数(即 a[i]!=i)
我们考虑如何推导f[x] 假设当前位置x上的数x放在位置y上(1<=y<x)如果数y放在位置x上那就相当与x,y互换位置 剩下x-2个数错排,如果数y不放在位置x上那此时相当于有x-1个数错排因为y不能放在位置x上,所以f[x]=(x-1)*(f[x-1]+f[x-2]);这样我们就可以线性求f[n]。 同时我们在求时要注意mod 1e9+7这需要用乘法逆元来求。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
long long i,j,n,m,k,l,mod,f[1000001],qz[1000001],ans,res,res1,t;
long long ksm(long long x,long long y)
{
long long cnt=1;
while(y>0)
{
if(y%2==1) cnt*=x,cnt%=mod;
x*=x;x%=mod;
y/=2;
}
return cnt;
}
int main()
{
f[0]=1;f[2]=1;f[1]=0;mod=1000000007;
qz[0]=1;qz[1]=1;qz[2]=2;
for(i=3;i<=1000000;i++)
f[i]=(i-1)*(f[i-1]+f[i-2])%mod,
qz[i]=qz[i-1]*i%mod;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
ans=f[n-m];
res=qz[m]*qz[n-m]%mod;
res=ksm(res,mod-2);
ans=(ans*qz[n]%mod)*res;ans%=mod;
printf("%lld\n",ans);
}
}
题目大意: 给定一个序列a1 a2 a3....an 对于一个正整数k(n%k==0),将数组a分成k个子数组{a1,a2..ak}{ak+1.....a2k}..{an-k+1...an},如果存在一个正整数m使得他将数组中的每个元素除以 m后的余数都替换为该元素,若所有子数组都相同则答案加一。求1<=k<=n有多少个满足的k。
根据题意我们可以得到若干个式子
.......
......
对于a1=x+k1*m,ak+1=x+k2*m 所以a1-ak+1=(k1-k2)*m; 所以 m为a1-ak+1的约数。那么我们相当于求 abs(ai-ai+k) (1<=i<=n-k)的公共约数。判断这公共约数是否存在就行用gcd来做。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
int i,j,n,m,k,l,o,p,x,y,dl[300001],t,a[300001],ans;
int gcd(int a,int b)
{
if(b==0) return a;
else return gcd(b,a%b);
}
int main()
{
//freopen("1.in","r",stdin);
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
for(i=1;i<=n;i++) scanf("%d",&a[i]);
dl[0]=0;
for(i=1;i<=sqrt(n);i++)
if(n%i==0)
{
dl[++dl[0]]=i;
if(i*i!=n) dl[++dl[0]]=n/i;
}
ans=0;
for(i=1;i<=dl[0];i++)
{
x=0;
for(j=1;j<=n-dl[i];j++)
{
if(x==0) x=abs(a[j]-a[j+dl[i]]);
else
{
x=gcd(x,abs(a[j]-a[j+dl[i]]));
}
if(x==1) break;
}
if(x!=1) ans++;
}
printf("%d\n",ans);
}
}
题目大意: 求 (这里a已经从小到大排好序)
这道题我们可以用欧拉反演来做 即运用定理 。(d|n表示d为n的约数[...]表示里面的条件成立则为1反之为0)
对于两个数a[i],a[j]我们设res=gcd(a[i],a[j]),那么
其中我们可以用个桶来优化 设cnt[x]表示从1~i-1中满足a[i]%x==0 的个数。
这样我们就可以先用vector预处理存储每个数的约数然后线性做了
对于用欧拉筛预处理就行
换句话说就是对于a[i],a[j]他们公共约数乘对应欧拉函数得到的值就是他们两的gcd。
因为他们的公共约数就是gcd(a[i],a[j])的所有约数。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
#include<cmath>
using namespace std;
long long i,j,n,m,k,l,phi[200001],prime[200001],cnt[200001],t,a[200001],x;
long long ans,mod,sum;
bool bz[200001];
void getphi()
{
phi[1]=1;
for(int i=2;i<=100000;i++)
{
if(bz[i]==0)
{
bz[i]=1;
prime[++prime[0]]=i;
phi[i]=i-1;
}
for(int j=1;j<=prime[0];j++)
{
if(prime[j]*i>100000) break;
bz[i*prime[j]]=1;
if(i%prime[j]==0) phi[i*prime[j]]=phi[i]*prime[j];
else phi[i*prime[j]]=phi[i]*(prime[j]-1);
}
}
}
int main()
{
//freopen("1.in","r",stdin);
scanf("%d",&t);
getphi();
while(t>0)
{
t--;
scanf("%d",&n);
for(i=1;i<=n;i++) scanf("%d",&a[i]);
memset(cnt,0,sizeof cnt);
sort(a+1,a+1+n);
vector<vector<int> > q;
for(i=1;i<=n;i++)
{
vector<int> temp;
x=a[i];
for(j=1;j<=sqrt(x);j++)
if(x%j==0)
{
temp.push_back(j);
if(x/j!=j) temp.push_back(x/j);
}
q.push_back(temp);
}
ans=0;
for(i=0;i<n;i++)
{
sum=0;
for(j=0;j<q[i].size();j++)
{
sum+=(long long)cnt[q[i][j]]*phi[q[i][j]];
cnt[q[i][j]]++;
}
ans+=sum*(n-i-1);
}
printf("%lld\n",ans);
}
}
题目大意:给一颗无向树,根为1。对于每个节点,求其子树中,哪个距离下的节点数量最多。数量相同时,取较小的那个距离。
我们设f[i][x]表示对于当前点i距离为x的点有多少个,我们首先可以用O(n^2)的暴力来做,但这样会超时。但我们可以发现f[i][x]和深度有关,因此考虑长链剖分,我们考虑每次继承重儿子的dp数组和答案然后暴力更新轻儿子的贡献,我们每次暴力更新轻儿子时,轻儿子f[][x]的数组长度就是该链的长度,则时间复杂度是所有长链的长度之和,即O(n)。对于空间而言,可以通过维护一根指针来合理分配空间,使得所用空间也是线性的。
(我不太擅长处理指针所以用树上启发式合并直接压缩dp数组为一维,更新轻儿子时就更暴力罢了 仅供参考)
仅供参考
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int i,j,n,m,k,l,h[1000001],cnt,x,y,deep[1000001],md[1000001],in[1000001],out[1000001],sum,fa[1000001],son[1000001],f[1000001],wz[1000001],ans[1000001],len;
struct node{
int to,ne;
}e[4000001];
void add(int u,int v)
{
e[++cnt].ne=h[u];
e[cnt].to=v;
h[u]=cnt;
}
void dfs(int x,int y)
{
deep[x]=y;
in[x]=++sum;
wz[sum]=x;
for(int i=h[x];i!=0;i=e[i].ne)
if(deep[e[i].to]==0)
{
fa[e[i].to]=x;
dfs(e[i].to,y+1);
if(md[e[i].to]>md[x])
{
md[x]=max(md[x],md[e[i].to]);
son[x]=e[i].to;
}
}
md[x]++;
out[x]=sum;
}
void dfs1(int x,int y)
{
for(int i=h[x];i!=0;i=e[i].ne)
{
if(e[i].to==son[x]||e[i].to==fa[x]) continue;
dfs1(e[i].to,0);
}
if(son[x]!=0) dfs1(son[x],1);
f[deep[x]]++;
if(f[deep[x]]>f[len]||(f[deep[x]]==f[len]&&len>deep[x])) len=deep[x];
for(int i=h[x];i!=0;i=e[i].ne)
{
if(e[i].to==son[x]||e[i].to==fa[x]) continue;
for(int j=in[e[i].to];j<=out[e[i].to];j++)
{
f[deep[wz[j]]]++;
if(f[deep[wz[j]]]>f[len])
{
len=deep[wz[j]];
}
else if(f[deep[wz[j]]]==f[len]&&len>deep[wz[j]])
{
len=deep[wz[j]];
}
}
}
ans[x]=len-deep[x];
if(y==0)
{
for(int i=in[x];i<=out[x];i++)
{
f[deep[wz[i]]]--;
if(f[deep[wz[j]]]>f[len])
{
len=deep[wz[j]];
}
else if(f[deep[wz[j]]]==f[len]&&len>deep[wz[j]])
{
len=deep[wz[j]];
}
}
}
}
int main()
{
//freopen("1.in","r",stdin);
scanf("%d",&n);
for(i=1;i<n;i++)
{
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
dfs(1,1);
dfs1(1,1);
for(i=1;i<=n;i++) printf("%d\n",ans[i]);
}
ak爽!