给定一个长度为N的数列,A1, A2, ... AN,如果其中一段连续的子序列Ai, Ai+1, ... Aj(i <= j)之和是K的倍数,我们就称这个区间[i, j]是K倍区间。
你能求出数列中总共有多少个K倍区间吗?
你能求出数列中总共有多少个K倍区间吗?
输入
第一行包含两个整数N和K。(1 <= N, K <= 100000)
以下N行每行包含一个整数Ai。(1 <= Ai <= 100000)
以下N行每行包含一个整数Ai。(1 <= Ai <= 100000)
输出
输出一个整数,代表K倍区间的数目。
样例输入
5 2 1 2 3 4 5
样例输出
6
求区间[l,r]的和是k的倍数的个数。求区间和,我们可以通过前缀和来求出。我们规定sum[i]表示第1个元素到第i个元素的和。那么sum[r] - sum[l-1]就是区间[l,r]的和。区间[l,r]的和是k的倍数即(sum[r] - sum[l-1])%k == 0 即sum[r]%k == sum[l-1]%k
那么,我们求出每个前缀和,在求的过程中取模,两个相等的前缀和就能组成一个k倍区间。我们可以在计算完前缀和以后,使用两层for循环来计数k倍区间的个数。但是由于数据量较大,这样是会超时的。
#include<iostream> #include<bits/stdc++.h> using namespace std; int a[100005],str[100005],cnt[100005]; int main() { int n,k,i,j; cin>>n>>k; for(i=0;i<n;i++) { cin>>a[i]; str[i]=(str[i-1]+a[i])%k; } long long ans=0; for(i=0;i<n;i++) { ans+=cnt[str[i]]; cnt[str[i]]++; } cout<<ans+cnt[0]<<endl; return 0; }
小明几乎每天早晨都会在一家包子铺吃早餐。他发现这家包子铺有N种蒸笼,其中第i种蒸笼恰好能放Ai个包子。每种蒸笼都有非常多笼,可以认为是无限笼。
每当有顾客想买X个包子,卖包子的大叔就会迅速选出若干笼包子来,使得这若干笼中恰好一共有X个包子。比如一共有3种蒸笼,分别能放3、4和5个包子。当顾客想买11个包子时,大叔就会选2笼3个的再加1笼5个的(也可能选出1笼3个的再加2笼4个的)。
当然有时包子大叔无论如何也凑不出顾客想买的数量。比如一共有3种蒸笼,分别能放4、5和6个包子。而顾客想买7个包子时,大叔就凑不出来了。
小明想知道一共有多少种数目是包子大叔凑不出来的。
每当有顾客想买X个包子,卖包子的大叔就会迅速选出若干笼包子来,使得这若干笼中恰好一共有X个包子。比如一共有3种蒸笼,分别能放3、4和5个包子。当顾客想买11个包子时,大叔就会选2笼3个的再加1笼5个的(也可能选出1笼3个的再加2笼4个的)。
当然有时包子大叔无论如何也凑不出顾客想买的数量。比如一共有3种蒸笼,分别能放4、5和6个包子。而顾客想买7个包子时,大叔就凑不出来了。
小明想知道一共有多少种数目是包子大叔凑不出来的。
输入
第一行包含一个整数N。(1 <= N <= 100)
以下N行每行包含一个整数Ai。(1 <= Ai <= 100)
以下N行每行包含一个整数Ai。(1 <= Ai <= 100)
输出
一个整数代表答案。如果凑不出的数目有无限多个,输出INF。
样例输入
2 4 5
样例输出
6
拓展欧几里德:
1. 求整数 x和y 使得 ax + by = 1.
2. 可以发现, 如果gcd(a, b) ≠ 1,则显然无解.
3. 反之, 如果gcd(a, b) = 1, 则可以通过拓展原来的 辗转相除法 来求解.
4. 事实上,一定存在整数对(x, y)使得ax+by = gcd(a,b) = 1
如果所有 蒸笼里的包子数的最大公约数,不为1,则说明有无数种数目凑不出来。如果最大公约数为1,则说明有限个数目凑不出来。
#include<iostream> #include<bits/stdc++.h> using namespace std; int a[101],dp[10002]; int gcd(int a,int b) { return b==0?a:gcd(b,a%b); } int main() { int n; cin>>n; int i,j; for(i=0;i<n;i++) { cin>>a[i]; } int t=a[0]; for(i=1;i<n;i++) { t=gcd(t,a[i]); } if(t!=1) cout<<"INF"<<endl; else { dp[0]=1; for(i=0;i<n;i++) { for(j=0;j+a[i]<10002;j++) { if(dp[j]) { dp[j+a[i]]=1; } } } long long ans=0; for(i=0;i<10002;i++) { if(!dp[i]) ans++; } cout<<ans<<endl; } return 0; }
方格分割
- 6x6的方格,沿着格子的边线剪开成两部分。
- 要求这两部分的形状完全相同。
- 试计算:
- 包括这3种分法在内,一共有多少种不同的分割方法。
可以转换为,这是一个 6 x 6的矩阵,将[3, 3]位置看成起点,分相反的两条路径开始搜索(标志),当搜索到 边界时就是停止遍历 (r == 0 || c == 0 || r == 6 || c == 6) ,即是一种方案。这显然是经典的回溯问题,但是要注意这要对两条相反的路径进行标志。最后方案数/4, 因为旋转对称属于一种方案(4个方向嘛)
#include <iostream> #include <cstdio> #include <cstring> #include <cstdlib> using namespace std; const int maxn = 6 + 2; bool used[maxn][maxn]; int ans; int dir[4][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}}; void dfs(int r, int c) { if (r == 0 || c == 0 || r == 6 || c == 6) { ans++; return; } for (int i = 0; i < 4; i++) { int rx = r + dir[i][0], ry = c + dir[i][1]; if (!used[rx][ry]) { used[rx][ry] = true; used[6 - rx][6 - ry] = true; dfs(rx, ry); used[rx][ry] = false; used[6 - rx][6 - ry] = false; } } } void solve() { memset(used, 0, sizeof(used)); used[3][3] = true; dfs(3, 3); cout << ans / 4 << endl; } int main() { solve(); return 0; }