a
问题描述
有 n n 个青蛙, m m 个石头围成一圈编号为 0 m−1 0 m − 1 ,第i 只青蛙每次跳 ai a i 步,这意味着青蛙能从石头 j mod m j m o d m 跳到石头 (j+ai) mod m ( j + a i ) m o d m 。青蛙每跳一个石头,就占领它。每只青蛙最开始在0 号石头,它们可以一直跳下去。这些青蛙最后占领的石头编号和为多少?
输入
第一行一个整数T,接下来T 组数据,每组数据输入两行。
第一行输入两个整数n;m。第二行输入n 个整数ai。
输出
对于每组数据输出一行,形如”Case #X: Y”。X 为数据编号,从1 开始,Y
为占领的石头编号和。
数据范围
对于
30%
30
%
的数据,
n⩽10,m⩽1e3
n
⩽
10
,
m
⩽
1
e
3
,
对于
100%
100
%
的数据,
1⩽T⩽50
1
⩽
T
⩽
50
,
1⩽n⩽1e4
1
⩽
n
⩽
1
e
4
,
1⩽m⩽1e9
1
⩽
m
⩽
1
e
9
,
1⩽ai⩽1e9
1
⩽
a
i
⩽
1
e
9
。
解题法
对于每一只青蛙,跳到天荒地老也不一定能跳完所有石头。
仔细画图一瞧,占领的每一块石头正好相距
gcd(m,ai)
g
c
d
(
m
,
a
i
)
。
哇!其实是因为有一个神奇的方程保证有解的条件。
(我不可能知道的,具体请自己YY)
对于每一只青蛙,占据的石头一定是个等差数列:
! 其实没有m这一项 !
故和就是:
开一个 bool 数组,记录是否访问过。 → → 30分get!
那么问题来了,如何去重呢?
30→100 30 → 100 的蜕变,仅需一步:
容斥
针对因数的容斥
将gcd们排好序,由小到大,每一个有船新贡献的因数必定至少含有至少一个船新质因数,就此可以进行估计,1e9的数据,只需很少( ⩽20 ⩽ 20 个)个因数就可以搞出所有被占据的石头。
代码
#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
#define FN "a"
const int maxn=1e4+5;
void Cin(int &x) {
char ch;
while(!isdigit(ch=getchar()));
for(x=ch-'0';isdigit(ch=getchar());x=(x<<3)+(x<<1)+ch-'0');
}
int n,m;
int a[maxn],gc[maxn],que[66];
int gcd(int a,int b) {return b?gcd(b,a%b):a;}
int lcm(int a,int b) {return 1LL*a/gcd(a,b)*b;}
long long sum(int x) {return 1LL*m*(m/x+1)/2-m;}
int main() {
freopen(FN".in","r",stdin);
freopen(FN".out","w",stdout);
int T;
Cin(T);
for(int t=1;t<=T;t++) {
Cin(n);
Cin(m);
for(int i=1;i<=n;i++) {
Cin(a[i]);
gc[i]=gcd(m,a[i]);
}
std::sort(gc+1,gc+n+1);
int cnt=0;
for(int i=1;i<=n;i++) {
bool fl=false;
for(int j=1;j<=cnt;j++)
if(!(gc[i]%que[j]))
fl=true;
if(fl)
continue;
que[++cnt]=gc[i];
}
long long ans=0;
for(int s=1;s<(1<<cnt);s++) {
int d=1,fl=-1;
for(int i=1;i<=cnt;i++)
if(s>>(i-1)&1) {
fl=-fl;
d=lcm(d,que[i]);
}
ans+=fl*sum(d);
}
printf("Case #%d: %I64d\n",t,ans);
}
return 0;
}
b
问题描述
对于数字
n
n
, 内,有多少个数可以被由它转化成的二进制里面的1 的个数整除
比如9 的二进制形式为1001,但是
9%2≠0
9
%
2
≠
0
,所以它不是
比如8 的二进制形式为1000,
8%1=0
8
%
1
=
0
,所以它是
如对于9 来说
[1,9]
[
1
,
9
]
内有5 个数字是合法的。
数据范围
对于
20%
20
%
的数据,
n⩽50000000
n
⩽
50000000
,
对于
100%
100
%
的数据,
1⩽n⩽1e19
1
⩽
n
⩽
1
e
19
。
解题法
裸数位DP,唯一问题是可能有点卡空间,四维数组还是很夸张的。
233的作用只是为了不写 memset() m e m s e t ( ) 。
代码
#include<cstdio>
#include<iostream>
#define FN "b"
unsigned long long N,ans;
int digit[70],figure,vis[70][70][70][2];
unsigned long long dp[70][70][70][2];
unsigned long long dfs(int fig,bool tag,int sum,int num,int last,int _233_) {
if(vis[fig][num][last][tag]==_233_)
return dp[fig][num][last][tag];
if(!fig) {
if(!last && sum==num)
return dp[fig][num][last][tag]=1;
return dp[fig][num][last][tag]=0;
}
int h=tag?digit[fig]:1;
unsigned long long ANS=0;
for(int i=h;i>=0;i--)
ANS+=dfs(fig-1,tag && (i==h),sum,num+i,((last<<1)%sum+i)%sum,_233_);
vis[fig][num][last][tag]=_233_;
return dp[fig][num][last][tag]=ANS;
}
int main() {
freopen(FN".in","r",stdin);
freopen(FN".out","w",stdout);
std::cin>>N;
while(N) {
digit[++figure]=(int) N&1;
N>>=1;
}
for(int i=1;i<=figure;i++)
ans+=dfs(figure,true,i,0,0,i);
std::cout<<ans;
return 0;
}
c
问题描述
有一个 n∗m n ∗ m 的矩阵,左上角为 (1,1) ( 1 , 1 ) 右下角为 (n,m) ( n , m ) ,每个格子边长为4,每个格子中央有权值为 aij a i j 。对于一个格子的顶点,这个点的权值是 ∑ni=1∑mj=1dis2∗aij ∑ i = 1 n ∑ j = 1 m d i s 2 ∗ a i j , dis 是这个点到格子 (i,j) ( i , j ) 中心的欧几里得距离。求该权值最小的顶点。如有相同权值的顶点,首先选择x 坐标较小的顶点,如果还有相同,再选择y 坐标较小的顶点。
数据范围
对于
20%
20
%
的数据,
norm=1
n
o
r
m
=
1
,
对于
100%
100
%
的数据,
1⩽n,m⩽1000
1
⩽
n
,
m
⩽
1000
,
1⩽aij⩽100000
1
⩽
a
i
j
⩽
100000
。
解题法
dis问什么要给你平方?
当然是要化成横和纵分开算啊!
横纵预处理费用, n2 n 2 暴力找最小。
如何预处理?
拍扁!
对于一个纵列,需要算出 ∑ni=0∗22∗a[i] ∑ i = 0 n ∗ 2 2 ∗ a [ i ]
处理出横纵两个数组就好了。
一句话来说:纵列算横边(2),横行算纵边(还是2)。
官方给的题解竟然有扫描线这几个字!
迷醉。
代码
#include<bits/stdc++.h>
#define FN "c"
const int maxn=1005;
long long a[maxn][maxn],c[maxn],l[maxn];
long long ansx[maxn],ansy[maxn];
int main() {
freopen(FN".in","r",stdin);
freopen(FN".out","w",stdout);
int n,m;
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++)
for(int j=0;j<m;j++) {
scanf("%I64d",&a[i][j]);
c[i]+=a[i][j];
l[j]+=a[i][j];
}
for(int i=0;i<=n;i++) {
for(int j=i-1,x=2;j>=0;j--,x+=4)
ansx[i]+=c[j]*x*x;
for(int j=i,x=2;j<n;j++,x+=4)
ansx[i]+=c[j]*x*x;
}
for(int i=0;i<=m;i++) {
for(int j=i-1,x=2;j>=0;j--,x+=4)
ansy[i]+=l[j]*x*x;
for(int j=i,x=2;j<m;j++,x+=4)
ansy[i]+=l[j]*x*x;
}
long long temp=1e18;
int posx,posy;
for(int i=0;i<=n;i++)
for(int j=0;j<=m;j++)
if(ansx[i]+ansy[j]<temp) {
temp=ansx[i]+ansy[j];
posx=i;
posy=j;
}
printf("%I64d\n%d %d",temp,posx,posy);
return 0;
}
总结(讲垃圾话)
今天玩Hi了,T2竟然完全没有想到数位DP,感觉DP宛若没学过一般(其实因为我看1e19就去想数学去了)
T1其实已经差不多了,结果没有正确估计船新因子数,导致不敢写。
这一次尻♂试已经将我拉回大众水平了。
多想想!
好像最近有点贪♂Van了,控制游戏时间啊!
此上。