AtCoder Beginner Contest136EF,137EF,138 F

Atcoder

136E

题意:给定 n n n个数,每次可以选择两个数 一 个 + 1 , 一 个 − 1 一个+1,一个-1 +1,1,求这 n n n个数的gcd 最大是多少.
分析

  1. 首先 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)的所有约数即可
  2. 怎么判断这个约数是否可行呢,我们考虑将 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 2n1个非空子集,每一个子集都能找到一个最小的矩形包括,这个矩形中点的数量就是这个集合的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 TP的费用。
分析:经过一条边,收取硬币的同时也需要付出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 p1次多项式的前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,AxyB ( x , y ) (x,y) (x,y)的数量
分析:分析如果 x , y x,y x,y的最高位不同,那么 y ⊕ x &gt; y % x y\oplus x &gt; y\%x yx>y%x,不符合条件,所以肯定有 x , y x,y x,y的最高位相同,进一步的如果 x , y x,y x,y的最高位相同,就有 y &lt; 2 ∗ x y &lt; 2*x y<2x,也即 y % x = y − x = y ⊕ x y\%x = y-x=y\oplus x y%x=yx=yx,两者的二进制中的每一位必有 y i ≥ x i y_i \geq x_i yixi,就可以愉快的进行数位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
*/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值