给定一个长度为N的数列,A1, A2, ... AN,如果其中一段连续的子序列Ai, Ai+1, ... Aj(i <= j)之和是K的倍数,我们就称这个区间[i, j]是K倍区间。


你能求出数列中总共有多少个K倍区间吗?
输入
第一行包含两个整数N和K。(1 <= N, K <= 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个包子时,大叔就凑不出来了。


小明想知道一共有多少种数目是包子大叔凑不出来的。
输入
第一行包含一个整数N。(1 <= N <= 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;
}



方格分割

  1. 6x6的方格,沿着格子的边线剪开成两部分。
  2. 要求这两部分的形状完全相同。
  3. 试计算:
  4. 包括这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; 
}

 

posted on 2019-02-27 21:04  可怕hiahia  阅读(107)  评论(0编辑  收藏  举报