题意很简单,就是给你一个数组大小为n的值从1到n,之后有m次操作,要么修改前面的数组中的某一项,要么是输出从x到y(包括边界)中的数与p互质的数的和。由于m很小(m<=1000)故,将修改的部分保存下来之后统计输出时,在判断修改的部分是否与p互质,若是要修改原来的,在修改现在的。
注意,就是多次修改同一个地方,只记录最后一次。
对于统计从x到y的与p互质数的和,有结果=数1到y的满足要求的结果-数1到x-1的满足要求的结果。
而从1开始到n的数中与p互质的数的和的求法,就是个容斥原理的算法。
剔除p中单个素因子的倍数,加上两个因子的倍数,接着在剔除三个素因子的倍数,直到所有的素因子结束。
。/** * @Title: HDU4407.java * @Package com.wangzhu.hdu * @Description: TODO * @author 王竹 * @date 2012-9-22 下午7:40:27 * @version V1.0 * */ package com.wangzhu.hdu; import java.io.BufferedInputStream; import java.util.Arrays; import java.util.Scanner; /** * @ClassName: HDU4407 * @Description: TODO * @author 王竹 * @date 2012-9-22 下午7:40:27 * */ public class HDU4407 { public static void main(String[] args) { // System.setIn(new FileInputStream("data.in")); Scanner cin = new Scanner(new BufferedInputStream(System.in)); int t, n, m, cas, x, y, c, p; initPrime(); while (cin.hasNext()) { t = cin.nextInt(); while (t-- > 0) { n = cin.nextInt(); m = cin.nextInt(); node = new Node[m + 1]; lastIndex = new int[MAXN]; nodeLen = 0; for (int i = 0; i < m; i++) { cas = cin.nextInt(); if (cas == 2) { x = cin.nextInt(); c = cin.nextInt(); node[nodeLen] = new Node(x, c); lastIndex[x] = nodeLen++; } else { x = cin.nextInt(); y = cin.nextInt(); p = cin.nextInt(); work(x, y, p, n); } } } } cin.close(); } private final static int MAXN = 400001; static int[] lastIndex; private final static int VAL = 1000; static int[] prime = new int[VAL]; static boolean[] isPrime = new boolean[VAL]; static int primeLen; public static void initPrime() { Arrays.fill(isPrime, false); primeLen = 0; prime[primeLen++] = 2; for (int i = 4; i < VAL; i += 2) { isPrime[i] = true; } for (int i = 3; i < VAL; i += 2) { if (!isPrime[i]) { prime[primeLen++] = i; } for (int j = 0; (j < primeLen) && (i * prime[j] < VAL); j++) { isPrime[i * prime[j]] = true; if (i % prime[j] == 0) { break; } } } } static Node[] node; static int nodeLen; static int[] primeFactor; // 存储素因子 static int primeFactorLen; /** * 分解数p获得其素因子 * * @param p */ public static void mkPrimeFactor(int p) { primeFactor = new int[VAL]; int temp = (int) Math.sqrt(1.0 * p); primeFactorLen = 0; for (int i = 0; (i < primeLen) && (prime[i] <= temp); i++) { if (p % prime[i] == 0) { primeFactor[primeFactorLen++] = prime[i]; while (p % prime[i] == 0) { p /= prime[i]; } } } if (p > 1) { primeFactor[primeFactorLen++] = p; } } private static void work(int x, int y, int p, int n) { mkPrimeFactor(p); long res = getSum(y) - getSum(x - 1); for (int i = 0; i < nodeLen; i++) { if (node[i].x >= x && node[i].x <= y) { // 若是多次修改同一个地方,则只取最后一次 if (i != lastIndex[node[i].x]) { continue; } if (isCoPrime(node[i].x, p)) { res -= node[i].x; } if (isCoPrime(node[i].c, p)) { res += node[i].c; } } } System.out.println(res); } private static long getSum(int i) { long res = (long) i * (i + 1) / 2; res -= dfs(0, i, 1); return res; } private static boolean isCoPrime(int c, int p) { return gcd(c, p) == 1; } /** * 最大公约数 * * @param a * @param b * @return */ public static long gcd(long a, long b) { long r; if (a < b) { r = a; a = b; b = r; } while (b != 0) { r = a % b; a = b; b = r; } return a; } /** * 容斥原理 获得在n下所有与p互质的数的和 * * @param start * @param n * @param now * @return */ private static long dfs(int start, int n, long now) { long res = 0, temp; for (int i = start; i < primeFactorLen; i++) { temp = lcm(now, primeFactor[i]);// 每一个开始,之后就是其最小公倍数 res += getPrimeSum(temp, n) - dfs(i + 1, n, temp); } return res; } /** * 最小公倍数 * * @param now * @param i * @return */ private static long lcm(long now, long i) { return (long) now / gcd(now, i) * i; } private static long getPrimeSum(long num, int n) { long temp = n / num; return temp * (temp + 1) / 2 * num; } } class Node { int x; int c; public Node(int x, int c) { this.x = x; this.c = c; } }