Codeforces Round #137 (Div. 2)

C:

给你n个数和m个数 n , m <= 10^5,每个数 <= 10^7,n个数相乘代表分子,m个数相乘代表分母,现在要把这个分数的最简式也这样子代表出来,也就是说用nout个数代表分子,mout个数代表分母,必须是最简式,每个数的范围都得是 <= 10^7。

思路:

直接统计分子分母的所有素因子的个数,然后消去各个可以消去的素因子即可。

这里我用的筛素数法是O(N)的并且顺便处理出了每个数最小的素因子,这样子的话分解素因子岂不是很简单了。。。复杂度超低nice~

#include 
   
   
    
    
#include 
    
    
     
     
#include 
     
     
      
      
#include 
      
      
       
       
using namespace std;
#define pb push_back

const int maxp = 10000010;
const int maxn = 100000 + 10;
int pri[777777], pnum, minp[maxp];
bool vis[maxp];
int a[maxn], b[maxn], cnt1[maxp], cnt2[maxp];

// O(n)筛素数法,minp[i]表示i的最小的素因子
void get_prime(int n) {
    vis[1] = 1; pnum = 0;
    for(int i = 2;i <= n; i++) {
        if(!vis[i]) {
            pri[pnum++] = i;
            minp[i] = i;
        }
        for(int j = 0;j < pnum; j++) {
            if(pri[j] * i > n)  break;
            vis[pri[j] * i] = 1;
            minp[pri[j] * i] = pri[j];
            if(i % pri[j] == 0) break;
        }
    }
}

vector
       
       
         pr1, pr2; int main() { get_prime(10000000); int n, m, mx = 0; scanf("%d%d", &n, &m); for(int i = 0; i < n; i++) { scanf("%d", &a[i]); mx = max(mx, a[i]); int cur = a[i]; while(cur > 1) { cnt1[minp[cur]] ++; cur /= minp[cur]; } } for(int i = 0;i < m; i++) { scanf("%d", &b[i]); mx = max(mx, b[i]); int cur = b[i]; while(cur > 1) { cnt2[minp[cur]] ++; cur /= minp[cur]; } } for(int i = 1;i <= mx; i++) cnt1[i] = cnt2[i] = min(cnt1[i], cnt2[i]); for(int i = 0;i < n; i++) { int cur = a[i], now = cur; while(cur > 1) { int tmp = minp[cur]; if(cnt1[tmp]) now /= tmp, cnt1[tmp]--; cur /= tmp; } pr1.pb(now); } for(int i = 0;i < m; i++) { int cur = b[i], now = cur; while(cur > 1) { int tmp = minp[cur]; if(cnt2[tmp]) now /= tmp, cnt2[tmp]--; cur /= tmp; } pr2.pb(now); } sort(pr1.begin(), pr1.end()); sort(pr2.begin(), pr2.end()); printf("%d %d\n", n, m); for(int i = 0;i < n; i++) printf("%d ", pr1[i]); puts(""); for(int i = 0;i < m; i++) printf("%d ", pr2[i]); puts(""); return 0; } 
       
      
      
     
     
    
    
   
   

D:

直接贪心从a数列中从大到小到从小到大的b数列中选择尽可能多的和 >= x的即可,最好的排名肯定是1的。

E:

要你用m个字母组成n个字母,问有多少种情况,这里有些限制有些字母不能组合。

思路:很裸的矩阵二分幂

#include 
   
   
    
    
#include 
    
    
     
     
#include 
     
     
      
      
using namespace std;
typedef __int64 ll;

const int mod = 1000000007 ;

char s[11];

int pow_mod(int q[55][55], ll n, int m) {
    int a[55][55], aa[55][55], qq[55][55];
    for(int i = 0;i < m; i++)
        for(int j = 0;j < m; j++)
            a[i][j] = i == j ? 1 : 0;
    while(n) {
        if(n & 1) {
            for(int i = 0;i < m; i++)
                for(int j = 0;j < m; j++) {
                    aa[i][j] = 0;
                    for(int k = 0;k < m; k++)
                        aa[i][j] = (aa[i][j] + (ll)a[i][k]*q[k][j])%mod;
                }
            for(int i = 0;i < m; i++)
                for(int j = 0;j < m; j++)
                    a[i][j] = aa[i][j];
        }
        for(int i = 0;i < m; i++)
            for(int j = 0;j < m; j++) {
                qq[i][j] = 0;
                for(int k = 0;k < m; k++)
                    qq[i][j] = (qq[i][j] + (ll)q[i][k]*q[k][j])%mod;
            }
        for(int i = 0;i < m; i++)
            for(int j = 0;j < m; j++)
                q[i][j] = qq[i][j];
        n /= 2;
    }
    ll ret = 0;
    for(int i = 0;i < m; i++)
        for(int j = 0;j < m; j++)
            ret += a[i][j];
    return ret%mod;
}

int a[55][55];
int main() {
    int  m, k;
    ll n;
    scanf("%I64d%d%d", &n, &m, &k);
    for(int i = 0;i < m; i++)  
        for(int j = 0;j < m; j++)
            a[i][j] = 1;
    for(int i = 0;i < k; i++) {
        scanf("%s", s);
        int x, y;
        if(s[0] <= 'Z')
            x = 26 + s[0] - 'A';
        else
            x = s[0] - 'a';
        if(s[1] <= 'Z')
            y = 26 + s[1] - 'A';
        else
            y = s[1] - 'a';
        a[x][y] = 0;
    }
    printf("%d\n", pow_mod(a, n-1, m));
    return 0;
}

     
     
    
    
   
   

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值