2019 GDUT Spring Team_up Training IV(Div.1+Div.2)(分块容斥,dfs和dp)

Problem C — limit 1 second

Fear Factoring

The Slivians are afraid of factoring; it’s just, well, difficult.
Really, they don’t even care about the factors themselves, just how much they sum to.
We can define F(n) as the sum of all of the factors of n; so F(6) = 12 and F(12) = 28. Your taskis, given two integers a and b with a ≤ b, to calculate
在这里插入图片描述

Input

The input consists of a single line containing space-separated integers a and b (1 ≤ a ≤ b ≤ 1012;b-a ≤ 106).

Output

Print S on a single line.

Sample
Input

101 101

Output

102

Input

28 28
Output
56

Input

1 10

Output

87

Input

987654456799 987654456799

Output

987654456800

Input

963761198400 963761198400
Output
5531765944320

扫描二维码关注公众号,回复: 5968518 查看本文章
Input

5260013877 5260489265

Output

4113430571304040
第一种做法:
除法分块:
区块值 区块始末
举个例子 :12
次数是有12/n算出来的 像当n==5时 12/5==2,也就是说5出现了两次

[1]:12次 [2] :6次 [3] : 4次   [4] :3次

[5]:   2次   [6]: 2次   [7] : 1次   [8]: 1次

[9]: 1次     [10] :1次     [11] :1次     [12] 1 次

left是当前区块的开始 ,right 是当前区块的终点

区块是按出现次数分的,像
出现次数为12次 分一个区块 ,这个区块有1个元素 :1
出现次数为6次 分一个区块, 这个区块有1个元素 :2
出现次数为4次 分一个区块, 这个区块有1元素 : 3
出现次数为3次 分一个区块 , 这个区块有1个元素 4
出现次数为2次 分一个区块, 这个区块有2个元素 5 6
出现次数为1次,分一个区块 ,这个区块有6个元素:7 8 9 10 11 12

可以发现两个规律:
1: 越小的数分在区块值大的区块
2: 同一区块内的元素是按等差序列分布的。

可以按开始元素小的开始枚举
left是区块的开始元素,right是区块的结束元素
n/left 该区块的值
区块值也就是区块内的元素总共出现的次数

#include<iostream>
#include<cstdio>
using namespace std;
#define ull unsigned long long
ull sum(ull n)
{
  ull ans=0;
  ull left,right;
  for(left=1;left<=n;left=right+1){
    right=n/(n/left);
    ans += (n/left)*(left+right)*(right-left+1)/2;
  }
  return ans;
}
int main(){
    ull a,b;
    scanf("%llu %llu", &a, &b);
    printf("%llu\n", sum(b)-sum(a-1));
    return 0;
}

这里不能用long long 会爆,要用unsigned long long.
第二种代码:
考虑每个约数的贡献,那么分段等差数列求和即可。(容斥)

#include<iostream>
#include<cstdio>
using namespace std;
#define ll long long
ll cal(ll n){
  ll i,j;
  ll ans=0;
  for(i=1;i<=n;i=j+1){  
    j=n/(n/i);  
    ans+=(i+j)*(j-i+1)*(n/i);
  }
  return ans;
}

int main(){
  ll a,b;
  scanf("%lld %lld",&a,&b);
  printf("%lld\n",(cal(b)-cal(a-1))/2);
}

第二种做法:
暴力枚举,复杂度nlogn
i:1 → sqrt(b)(枚举因数)
  k:b → a (中间的因子是b的数)
       ans+=i;
  if i*i!=k ans+=k/i

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN = 11000;
ll a, b;
int main(){
    scanf("%lld%lld",&a,&b);
    ll M=sqrt(b);
    ll tmp=max(a-1,M);
    ll ans=0;
    for(ll i=1;i<=M;i++){
        for(ll k=b-b%i;k>=a&&i*i<=k;k-=i) {
            ans+=i;
            if(i*i!=k) //i*i=k时,只需一次就好
                ans+=k/i;
        }
    }
    printf("%lld\n",ans);
    return 0;
}

Problem J — limit 1 second

Grid Coloring

You have an m-by-n grid of squares that you wish to color. You may color each square either redor blue, subject to the following constraints:
• Every square must be colored.
• Colors of some squares are already decided (red or blue), and cannot be changed.
• For each blue square, all squares in the rectangle from the top left of the grid to that squaremust also be blue.Given these constraints, how many distinct colorings of the grid are there? The grid cannot be rotated.

Input

The first line of input consists of two space-separated integers m and n (1 ≤ m, n ≤ 30).Each of the next m lines contains n characters, representing the grid. Character ‘B’ indicates squares that are already colored blue. Similarly, ‘R’ indicates red squares. Character ‘.’ indicates squares that are not colored yet.

Output

Print, on a single line, the number of distinct colorings possible.For the first sample, the 6 ways are:
BB   BB  BR  BR  BB  BB
BB   BR  BR  BR  BR  BB
BR   BR  BR  RR  RR  RR

Sample
Input

3 2

B.
.R

Output

6

Input

7 6

…B
.B…R.

…B…
.R…
…R…

Output

3

Input

2 2
R.
.B

Output

0

题目大意:

给你一个地图,地图上有R和B,B的左上角包括它自己都只能是B,问一共有多少种摆放法,把所有的地图填满。

题目思路:

先将所有确定的B和R填满,接下来用dp
附上大佬代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

char ch[35][35];
int big[35];
int sma[35];
ll dp[35][35];


int main(){
    int n, m;
    while(~scanf("%d%d", &n, &m)){
        memset(sma, 0, sizeof(sma));
        memset(big, 0x3f, sizeof(big));
        memset(dp, 0, sizeof(dp));
        for(int i = 1; i <= n; i++) scanf("%s",ch[i]+1);
        for(int i = 1; i <= m; i++){
            for(int j = 1; j <= n; j++){
                if(ch[j][i] == 'B'){
                    sma[i] = max(sma[i], j);
                }
                else if(ch[j][i] == 'R'){
                    big[i] = min(big[i], j);
                }
            }
        }
        for(int i = sma[1]; i <= min(n, big[1]-1); i++) dp[1][i] = 1;
        for(int i = 2; i <= m; i++){
            for(int j = sma[i]; j <= min(n, big[i]-1); j++){
                for(int k = j; k <= n; k++){
                    dp[i][j] += dp[i-1][k];
                }
            }
        }
        ll ans = 0;
        for(int i = 0; i <= n; i++) ans += dp[m][i];
        printf("%lld\n", ans);
    }
    return 0;
}

第二种解法dp思想

#include<bits/stdc++.h>
using namespace std;
#define ll long long

ll n,m,dp[35][35];//f[i][j]表示考虑了前j列,红色格子已经涂到了第i行的方案数
char s[35][35];

int main()
{
   // freopen("input.in","r",stdin);
    cin>>n>>m;
    for(int i=1;i<=n;i++)scanf("%s",s[i]+1);
    for(int i=1;i<=n;i++){
        for(int j=0;j<=m;j++){
            bool ok=1;
            for(int k=j;k>=1;k--)if(s[i][k]=='R')ok=0;  //向上扫
            for(int k=j+1;k<=m;k++)if(s[i][k]=='B')ok=0;  //向下扫
            if(ok) //两种都可以放
                if(i==1)dp[i][j]=1; //第一列为1种
                else for(int k=j;k<=m;k++)dp[i][j]+=dp[i-1][k]; 
        }
    }
    ll sum=0;
    for(int j=0;j<=m;j++)sum+=dp[n][j];
    cout<<sum<<endl;
    return 0;
}

感觉已经像是老套路了。先把一些能够确定颜色的点涂上颜色,那么此时图会变成左上角一些蓝色,右下角一些红色的图,然后中间是无色的。考虑最终的答案图,红色格子的个数一定是递增的,那么我们开始Dp,记f[i][j]表示考虑了前i列,红色格子已经涂到了第j行的方案数,预处理出第i列在结束时红色格子可能的个数,转移就非常好转移了。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 35;

int n, m;
int Mp[N][N], dn[N], up[N];
ll f[N][N];
char s[N];

void ColorB(int x, int y) {
  for(int i = 1; i <= x; i++) {
    for(int j = 1; j <= y; j++) {
      if(!Mp[i][j] || Mp[i][j] == -1) Mp[i][j] = -1;
      else {
        puts("0");
        exit(0);
      }
    }
  }
  return ;
}

void ColorR(int x, int y) {
  for(int i = x; i <= n; i++) {
    for(int j = y; j <= m; j++) {
      if(!Mp[i][j] || Mp[i][j] == 1) Mp[i][j] = 1;
      else {
        puts("0");
        exit(0);
      }
    }
  }
}

int main() {
  scanf("%d%d", &n, &m);
  for(int i = 1; i <= n; i++) {
    scanf("%s", s + 1);
    for(int j = 1; j <= m; j++) {
      if(s[j] == 'R') Mp[i][j] = 1;
      else if(s[j] == 'B') Mp[i][j] = -1;
    }
  }
  for(int i = 1; i <= n; i++) {
    for(int j = 1; j <= m; j++) {
      if(Mp[i][j] == -1) ColorB(i, j);
      else if(Mp[i][j] == 1) ColorR(i, j);
    }
  }
  for(int j = 1; j <= m; j++) {
    int fs = n + 1;
    for(int i = 1; i <= n; i++) {
      if(!Mp[i][j]) {
        dn[j] = i;
        if(!up[j]) up[j] = i;
      }
      if(Mp[i][j] == 1) fs = min(fs, i);
    }
    if(!up[j]) up[j] = dn[j] = fs;
    else dn[j]++;
  }
  f[0][n + 1] = 1;
  for(int i = 1; i <= m; i++) {
    for(int j = up[i]; j <= dn[i]; j++) {
      for(int k = n + 1; k >= j; k--) {
        f[i][j] += f[i - 1][k]; 
      }
    }
  }
  ll ans = 0;
  for(int i = up[m]; i <= dn[m]; i++) ans += f[m][i];
  printf("%lld\n", ans);
  return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_43333395/article/details/89266665
今日推荐