Atcoder
136E
题意:给定
n
n
n个数,每次可以选择两个数
一
个
+
1
,
一
个
−
1
一个+1,一个-1
一个+1,一个−1,求这
n
n
n个数的gcd 最大是多少.
分析:
- 首先 d = g c d ( A 1 , . . . A n ) ∣ A 1 , A 2 , A 3 , . . . A n d=gcd(A_1,...A_n)|A_1,A_2,A_3,...A_n d=gcd(A1,...An)∣A1,A2,A3,...An,那么 d ∣ ∑ ( A i ) d|\sum(A_i) d∣∑(Ai),枚举 ∑ ( A i ) \sum(A_i) ∑(Ai)的所有约数即可
- 怎么判断这个约数是否可行呢,我们考虑将 A 1 , . . A n A_1,..A_n A1,..An对于约数 d ′ d' d′取模得到 a i , . . a n a_i,..a_n ai,..an,那么肯定有一些数加上某些值使得整除,一些数减去某些值使得整除,我们对 a i a_i ai排序,从大到小遍历,如果某个时候前面需要的和后面多余的正好相等,说明符合条件
const int maxn = 500+10;
LL a[maxn];
LL b[maxn];
LL ans = 1;
int n,k;
void update(LL x){
LL t = 0;
for(int j = 1;j <= n; ++j)
b[j] = a[j]%x, t += b[j];
sort(b+1,b+n+1,greater<LL>());
LL tt = t;
for(int j = 1;j <= tt/x; ++j)
t -= b[j];
if(t <= k)
ans = max(ans,x);
}
int main(void)
{
cin>>n>>k;
LL sum = 0;
for(int i = 1;i <= n; ++i)
cin>>a[i],sum += a[i];
// cout<<sum<<endl;
for(LL i = 2;i*i <= sum; ++i){
if(sum % i == 0){
update(i);
update(sum/i);
}
}
update(sum);
cout<<ans<<endl;
return 0;
}
atcoder 136F
题意: 给定
n
n
n个点,它共有
2
n
−
1
2^n-1
2n−1个非空子集,每一个子集都能找到一个最小的矩形包括,这个矩形中点的数量就是这个集合的value,求所以子集的value和
分析: 对于此类问题我们不能枚举所有的子集,只能枚举一个点的贡献次数,直接求包含不容易求,我们考虑反求,采用容斥定理,一个点
(
x
i
,
y
i
)
(x_i,y_i)
(xi,yi)将整个平面分成了四个部分,左上,右上,左下,右下。这四个部分的点的个数可以统计出来(采用树状数组即可),这四个部分本身肯定不包含
(
x
i
,
y
i
)
(x_i,y_i)
(xi,yi),我们还可以通过这个求出,左,上,右,下的集合的数量减去,再然后加上左上,右上,左下,右下即可。
struct Point{
int x,y;
// Point (int x = 0,int y = 0)
};
Point p[maxn];
bool operator <(const Point &a,const Point &b){
return a.x < b.x;
}
LL sum[maxn];
int s[maxn],pre[maxn];
int x[maxn],y[maxn];
LL tree[maxn];
void Add(int x,int p){
for(;x < maxn; x += lowbit(x))
tree[x] += p;//,x += lowbit(x);
}
LL Sum(int x){
LL sum = 0;
for(;x > 0; x -= lowbit(x))
sum += tree[x];
return sum;
}
LL pow2[maxn];
int A[maxn],B[maxn];
inline void Add(LL &a,LL b){
a += b;
while(a >= mod)
a -= mod;
while(a < 0)
a += mod;
}
int main(void)
{
pow2[0] = 1;
for(int i = 1;i < maxn; ++i)
pow2[i] = pow2[i-1]*2%mod;
// cout<<pow2[10]<<endl;
int n;cin>>n;
for(int i = 1;i <= n;++i)
cin>>p[i].x>>p[i].y,x[i]= p[i].x,y[i] = p[i].y;
sort(p+1,p+n+1);
sort(y+1,y+n+1);
int N = unique(y+1,y+n+1)-y-1;
for(int i = 1;i <= n;++i)
{
p[i].x = i,p[i].y = lower_bound(y+1,y+N+1,p[i].y)-y;
}
LL ans = n*(pow2[n]-1)%mod;
for(int i = 1;i <= n; ++i){
A[i] = Sum(p[i].y);
Add(p[i].y,1);
}
me(tree);
for(int i = n;i >= 1; --i){
B[i] = Sum(p[i].y);
Add(p[i].y,1);
}
for(int i = 1;i <= n; ++i){
LL tmp = pow2[i-1]+pow2[n-i]+pow2[p[i].y-1]+pow2[n-p[i].y]-pow2[A[i]]-pow2[i-A[i]-1]-pow2[n-i-B[i]]-pow2[B[i]];
tmp %= mod;
Add(ans,-tmp);
}
cout<<(ans%mod+mod)%mod<<endl;
return 0;
}
137E
题意: 每一条路都有一定数量的硬币,边可以重复经过,求经过起点到终点的经过所有边的硬币和最大是多少(重复经过重复算),但是到终点的时候要收
T
∗
P
T*P
T∗P的费用。
分析:经过一条边,收取硬币的同时也需要付出P,可以一开始就将边权修改,
−
P
-P
−P然后将belloman-ford 跑2*n次,看最后是否更新了终点的值即可
const int maxn = 5000+10;
int u[maxn],v[maxn],c[maxn];
LL d[maxn];
int main(void)
{
int n,m,p;cin>>n>>m>>p;
for(int i = 2;i <= n;++i)
d[i] = -inf;
for(int i = 1;i <= m; ++i){
scanf("%d%d%d",&u[i],&v[i],&c[i]);
c[i] -= p;
}
for(int i = 1;i <= 2*n; ++i){
for(int j = 1;j <= m; ++j){
if(d[u[j]]==-inf) continue;
if(d[v[j]] < d[u[j]]+c[j]) d[v[j]] = i <= n?d[u[j]]+c[j]:1e18;
}
}
if(d[n] == inf)
cout<<-1<<endl;
else
cout<<max(0LL,d[n])<<endl;
return 0;
}
137 F
题意: 给定
p
−
1
p-1
p−1次多项式的前p项的值,求多项式的系数
分析: 按照朗格朗日公式进行展开即可,注意多项式乘法可以用可逆dp来做
const int maxn = 3000+10;
LL a[maxn];
// int dp[maxn];
int fac[maxn],invfac[maxn];
int dp[maxn][maxn];
int p;
int mod;
void Add(int &a,int b){
a += b;
if(a >= mod)
a -= mod;
}
int ans[maxn],tmp[maxn];
int main(void)
{
cin>>p;
invfac[0] = fac[0] = 1;
for(int i = 1;i < p; ++i)
fac[i] = fac[i-1]*i%p;
invfac[p-1] = qpow(fac[p-1],p-2,p);
for(int i = p-2;i > 0;--i)
invfac[i] = invfac[i+1]*(i+1)%p;
mod = p;
for(int i = 0;i < p; ++i)
cin>>a[i];
dp[0][1] = 1;
for(int i = 1;i < p; ++i){
for(int j = 0;j <= p; ++j){
dp[i][j] =( mod-1ll*i*dp[i-1][j]%mod)%mod;
if(j) Add(dp[i][j], dp[i-1][j-1]);
}
}
for(int i = 0;i < p; ++i){
if(!a[i]) continue;
for(int j = p-1;j >= 0; --j)
tmp[j] = (dp[p-1][j+1]+1ll*tmp[j+1]*i%mod)%mod;
int sign = 1;
if((p-i-1)&1) sign = -1;
for(int j = 0;j < p; ++j){
LL t = 1;
t = t*invfac[i]%mod;
t = (sign*t*invfac[p-i-1]%mod+mod)%mod;
Add(ans[j],t*tmp[j]%mod+mod);
}
}
for(int i =0;i < p; ++i)
cout<<(ans[i]%mod+mod)%mod<<" ";
cout<<endl;
return 0;
}
分析:
atcdoer 138F
题意:求满足
y
%
x
=
y
x
o
r
x
,
A
≤
x
≤
y
≤
B
y\%x =y\ xor\ x,A\leq x\leq y \leq B
y%x=y xor x,A≤x≤y≤B 的
(
x
,
y
)
(x,y)
(x,y)的数量
分析:分析如果
x
,
y
x,y
x,y的最高位不同,那么
y
⊕
x
>
y
%
x
y\oplus x > y\%x
y⊕x>y%x,不符合条件,所以肯定有
x
,
y
x,y
x,y的最高位相同,进一步的如果
x
,
y
x,y
x,y的最高位相同,就有
y
<
2
∗
x
y < 2*x
y<2∗x,也即
y
%
x
=
y
−
x
=
y
⊕
x
y\%x = y-x=y\oplus x
y%x=y−x=y⊕x,两者的二进制中的每一位必有
y
i
≥
x
i
y_i \geq x_i
yi≥xi,就可以愉快的进行数位dp了,我们遍历
B
B
B的最高位直到最高位和A相同
当然有更好的实现方法,我只贴出我的实现
其中dp的状态 a1,b1,a2,b2 是 当前搜索的a是否等于A,是否等于B,当前搜索的b是否等于A,是否等于B
//
const int maxn = 63;
LL F[maxn][2][2][2][2];
bool A[maxn], B[maxn];
LL solve(int n, int a1, int b1, int a2, int b2) {
if (n == -1) return 1;
LL &res = F[n][a1][b1][a2][b2];
if (res != -1) return res;
res = 0;
int a11 = a1 ? A[n] : 0;
int b11 = b1 ? B[n] : 1;
int a22 = a2 ? A[n] : 0;
int b22 = b2 ? B[n] : 1;
// cout << n << " " << a11 << " " << b11 << " " << a22 << " " << b22 << endl;
for (int i = a11; i <= b11; ++i) {
for (int j = a22; j <= b22; ++j) {
// cout << i << " " << j << endl;
if (i > j) continue;
// if (i && j) continue;
LL t = solve(n - 1, a1 && i == a11, b1 && i == b11, a2 && j == a22, b2 && j == b22);
t = t % mod;
res += t;
//cout << n << ' ' << i << ' ' << j << " " << t << endl;
res %= mod;
}
}
return res;
}
int main(void)
{
LL a, b; cin >> a >> b;
LL ans = 0;
for (int i = 62; i >= 0; --i) {
if (((1LL << i)&b) && ((1LL << i) & a)) {
LL t2 = b;
LL t1 = a;
for (int j = i - 1; j >= 0; --j)
A[j] = (1LL << j)&t1, B[j] = (1LL << j)&t2;
memset(F, -1, sizeof(F));
ans += solve(i - 1, 1, 1, 1, 1);
ans %= mod;
break;
}
else if ((1LL << i)&b) {
LL t2 = b;
LL t1 = (1LL << i);
for (int j = i - 1; j >= 0; --j)
A[j] = (1LL << j)&t1, B[j] = (1LL << j)&t2;
memset(F, -1, sizeof(F));
ans += solve(i - 1, 1, 1, 1, 1);
ans %= mod;
b = t1-1;
}
}
cout << ans << endl;
return 0;
}
/*
00
*/