做完感觉数论好像还没入门。。感觉并不基础。。
A - Bi-shoe and Phi-shoe
A题当时wa了好多次,做的时候逻辑出现了错误,只是将求出来的欧拉值和数字(pair)排了个序,然后找出第一个欧拉值大于等于它的那个数,但这样并不正确,因为有可能后面有欧拉值比他更大的但值更小的。
之后我的做法是维护一个1到当前数欧拉值的最大值,这样使得从1到n的first也是一个递增序列,从而可以求出第一个欧拉值大于等于它的那个数。
并不是所有的题都能这样做的,这个做法的正确性在于一个欧拉函数的性质:欧拉函数大于等于x的那个数就是x之后的第一个质数,那么这个性质就能保证,如果一个数不是质数的话,它的欧拉值在之前肯定出现过,所以我们要找的ans肯定不会是它,那么就能进行从前往后进行递推欧拉值从而覆盖不是ans的数的值。
#include<iostream>
#include<algorithm>
#include<cstring>
#include<stdio.h>
using namespace std;
#define N 1500000
pair<int, int>pii[N+5];
void init()
{
for (int i = 0;i <= N;i++)pii[i].first = 0;
//memset(phi, 0, sizeof(phi));
pii[1].first = 1;
//phi[1] = 1;
for (int i = 2;i <= N;i++)if (!pii[i].first)
{
for (int j = i;j <= N;j += i)
{
if (!pii[j].first)pii[j].first = j;
pii[j].first = pii[j].first / i*(i - 1);
}
}
for (int i = 1;i <= N;i++)pii[i].second = i,pii[i].first=max(pii[i].first,pii[i-1].first);
sort(pii + 1, pii + N + 1);
}
int solve(int num)
{
int l = 2, r = N;
int mid, ans = 0;
while (l <= r)
{
mid = l + r >> 1;
if (pii[mid].first >= num)
{
ans = mid;r = mid - 1;
}
else
l = mid + 1;
}
return ans;
}
int main()
{
int T;
scanf("%d", &T);
init();
//cout << pii[1500000].first << endl;
//for (int i = 2;i <= 10000;i++)cout << pii[i].first << endl;
int cas = 1;
while (T--)
{
int n;
scanf("%d", &n);
int tmp;
long long ans = 0;
for (int i = 1;i <= n;i++)
{
scanf("%d", &tmp);
int idx = solve(tmp);
ans = ans + pii[idx].second;
}
printf("Case %d: %lld Xukha\n", cas++, ans);
//printf("%d\n", ans);
}
}
B - Prime Independence
这题一看就是最大独立集,建图方式也很简单,但是N为4e4,如果暴力建图的话会T。那么应该怎么做呢?
我们来一步一步分析一下,如果一个数和另一个数是素数乘积关系,那么他们的唯一分解式因子的幂的和肯定是一奇一偶,所以我们可以根据这个来区分左右边,其次数字不大,我们可以用一个数组来记录这个数是否出现过,这种情况下我们只需把一个数的因子算出来然后枚举就行了。
另外学好用Hopcroft-Carp算法去代替匈牙利算法,毕竟快一些。
#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
#include<queue>
#include<stdio.h>
#include<math.h>
using namespace std;
const int maxn = 500005;
const int N = 40005;
const int inf = 0x3f3f3f3f;
int p[2][maxn];
bool is[maxn];
int prm[maxn], id;
void init()
{
memset(is, 1, sizeof(is));
is[0] = is[1] = 0;
int k = 0;
prm[k++] = 2;
for (int i = 4;i < maxn;i+=2)is[i] = 0;
int e = (int)sqrt(0.0 + maxn) + 1;
int i;
for (i = 3;i < e;i += 2)if(is[i])
{
prm[k++] = i;
for (int s = 2 * i, j = i*i;j < maxn;j += s)
is[j] = 0;
}
for (;i < maxn;i += 2)if (is[i])prm[k++] = i;
id = k;
}
int a[N];
int nL, nR;
int Left[N], Right[N];
vector<int>V[N];
void build(int idx)
{
int num = a[idx];
int tmp = num;
int e =(int)sqrt(num + 0.0) + 1;
int cnt=0;
for (int i = 0;i < id&&prm[i] < e;i++)
{
if (num == 1)break;
if (num%prm[i] == 0)
{
V[idx].push_back(prm[i]);
while (num%prm[i] == 0)
{
cnt++;
num = num / prm[i];
}
}
}
if (num != 1)
{
cnt++;
V[idx].push_back(num);
}
int go = cnt % 2;
p[go][tmp] = idx;
if (!go)
Left[++nL] = idx;
else
Right[++nR] = idx;
}
struct Edge
{
int u, v, nxt;
Edge(int _u, int _v, int _nxt):u(_u),v(_v),nxt(_nxt){}
Edge(){}
}edges[N * 10];
int head[N], tot;
void addedge(int u, int v)
{
edges[tot] = Edge(u, v, head[u]);
head[u] = tot++;
}
int n;
void buildmap()
{
for (int i = 1;i <= n;i++)
{
int num = a[i];
int Size = V[i].size();
int go = p[0][num] ? 0 : 1;
for (int j = 0;j < Size;j++)
{
int tmp = num / V[i][j];
if (p[go ^ 1][tmp])
{
addedge(i, p[go ^ 1][tmp]);
addedge(p[go ^ 1][tmp], i);
}
}
}
}
int cx[N], cy[N];
int dx[N], dy[N];
bool used[N];
int dis;
bool bfs()
{
memset(dx, -1, sizeof(dx));
memset(dy, -1, sizeof(dy));
dis = inf;
queue<int>Q;
for (int i = 1;i <= nL;i++)
{
int u = Left[i];
if (cx[u] == -1)
{
Q.push(u);
dx[u] = 0;
}
}
while (!Q.empty())
{
int u = Q.front();Q.pop();
if (dx[u] > dis)break;
for (int i = head[u];~i;i = edges[i].nxt)
{
int v = edges[i].v;
if (dy[v] == -1)
{
dy[v] = dx[u] + 1;
if (cy[v] == -1)dis = dy[v];
else
{
dx[cy[v]] = dy[v] + 1;
Q.push(cy[v]);
}
}
}
}
return dis != inf;
}
int find(int u)
{
for (int i = head[u];~i;i = edges[i].nxt)
{
int v = edges[i].v;
if (!used[v] && dy[v] == dx[u] + 1)
{
used[v] = 1;
if (cy[v] != -1 && dy[v] == dis)continue;
else if (cy[v] == -1 || find(cy[v]))
{
cx[u] = v;
cy[v] = u;
return 1;
}
}
}
return 0;
}
int MaxMatch()
{
memset(cx, -1, sizeof(cx));
memset(cy, -1, sizeof(cy));
int res = 0;
while (bfs())
{
memset(used, 0, sizeof(used));
for (int i = 1;i <= nL;i++)
{
int u = Left[i];
if (cx[u] == -1)
res += find(u);
}
}
return res;
}
int main()
{
init();
int T;
scanf("%d", &T);
int cas = 1;
while (T--)
{
memset(p, 0, sizeof(p));
memset(head, -1, sizeof(head));
tot = 0;
nL = nR = 0;
scanf("%d", &n);
for (int i = 1;i <= n;i++)
scanf("%d", &a[i]), V[i].clear();
for (int i = 1;i <= n;i++)
build(i);
buildmap();
int ans = n - MaxMatch();
printf("Case %d: %d\n",cas++, ans);
}
return 0;
}
C - Aladdin and the Flying Carpet
好题一枚。
如果没有b这个要求,我们很容易可以算出a的约数的个数,所以现在的问题如何解决b这个的问题。
我们来分析一下b,如果b大于sqrt(a),那么无解。
所以基于这个条件b<=1e6,那么我们可以计算出约数个数后,再来枚举b,一减就完事了。
#include<iostream>
#include<algorithm>
#include<cstring>
#include<math.h>
#include<stdio.h>
using namespace std;
typedef long long ll;
const ll maxn = 1e6+5;
bool is[maxn];
int prm[maxn], id;
void init()
{
memset(is, 1, sizeof(is));
is[0] = is[1] = 0;
prm[++id] = 2;
for (ll i = 4;i < maxn;i += 2)is[i] = 0;
ll e = (ll)sqrt(0.0 + maxn) + 1;
ll i;
for (i = 3;i <= e;i += 2)if (is[i])
{
prm[++id] = i;
for (ll s = 2 * i, j = i*i;j < maxn;j += s)
is[j] = 0;
}
for (;i < maxn;i += 2)if (is[i])prm[++id] = i;
}
int main()
{
init();
int T;
int cas = 1;
scanf("%d", &T);
while (T--)
{
ll a, b;
scanf("%lld %lld", &a, &b);
if ((int)sqrt(0.0 + a) < b)
{
printf("Case %d: 0\n", cas++);
continue;
}
ll _a = a;
ll ans = 1;
for (int i = 1;i < id&&prm[i] <= a / prm[i];i++)
{
if (a%prm[i] == 0)
{
int cnt = 0;
while (a%prm[i] == 0)
{
a /= prm[i];
cnt++;
}
ans *= (cnt + 1);
}
}
if (a != 1)
ans *= (1 + 1);
ans /= 2;
for (int i = 1;i < b;i++)if (_a%i == 0)ans--;
printf("Case %d: %lld\n", cas++, ans);
}
return 0;
}
D - Sigma Function
又是一道好题。
我们来认真分析一下。
一个数的因子和为
奇数*奇数=奇数
偶数*偶数=偶数
偶数*奇数=偶数
所以我们要求偶数的个数,不妨求奇数的个数然后再一减。
如果质因子有2,那么可以不用管,它的和肯定为奇数,那么后面的质因子由于都是奇数,那么我们必须得要奇数个奇数才能使和为奇数,所以k必须是个偶数。
观察除去2的其他质因子我们会发现,这个数其实是一个平方数。
所以答案就为n-sqrt(n)-sqrt(n/2).(读者可以自己思考一下)。
#include<iostream>
#include<algorithm>
#include<cstring>
#include<stdio.h>
#include<math.h>
using namespace std;
int main()
{
int T;
scanf("%d", &T);
int cas = 1;
while (T--)
{
long long n;
scanf("%lld", &n);
printf("Case %d: %lld\n",cas++, n - (int)sqrt(n + 0.0) - (int)sqrt((n + 0.0) / 2));
}
}
E - Leading and Trailing
后三位好求,快速幂模1000即可。
前三位就需要一点数学知识了。
所有的数可以表示成
n=10a
n
=
10
a
这个a一般是小数,那么
nk=10ak
n
k
=
10
a
k
这里把ak分成两部分,整数和小数部分,即x和y,那么
nk=10x∗10y
n
k
=
10
x
∗
10
y
,这里规定x为整数,y为小数,那么x就是指定位数的,y则是指定值的。
因为
n=10a
n
=
10
a
,所以
a=logn
a
=
l
o
g
n
,所以我们只需求出a*k的小数位,然后乘以100即可。
fmod(x,y),可求出x模y后的值(double类型)。
#include<iostream>
#include<algorithm>
#include<cstring>
#include<stdio.h>
#include<math.h>
using namespace std;
typedef long long ll;
int quick_mul(ll a, ll b)
{
ll ans = 1;
while (b)
{
if (b % 2)ans = ans*a % 1000;
a = a*a % 1000;
b /= 2;
}
return ans%1000;
}
int main()
{
int T, k, n, kase = 0;
scanf("%d", &T);
int cas = 1;
while (T--)
{
scanf("%d %d", &n, &k);
int x = (int)pow(10.0, 2.0 + fmod(1.0*k*log10(1.0*n), 1));
int y = quick_mul(n, k);
printf("Case %d: %d %03d\n", cas++, x, y);
}
return 0;
}
G - Harmonic Number (II)
这题有几个结论比较重要。
第一个结论: 一个数k出现的区间为(n/(k+1),n/k](k<=sqrt(n))
第二个结论:大于sqrt(n)的数并不是所有都出现了,他们的出现都与小于sqrt(n)的一一对应。
综上,我们可以用sqrt(n)的时间来计算出所有值的和。
#include<iostream>
#include<algorithm>
#include<cstring>
#include<stdio.h>
#include<math.h>
using namespace std;
typedef long long ll;
int main()
{
int T;
scanf("%d", &T);
int cas = 1;
while (T--)
{
ll n;
scanf("%lld", &n);
int e = (int)sqrt(0.0 + n);
ll ans = 0;
for (int i = 1;i <= e;i++)
{
ans += 1LL * (n / i - n / (i + 1))*i + n / i;
if (i == n / i)ans -= i;
}
printf("Case %d: %lld\n", cas++, ans);
}
}
H - Pairs Forming LCM
把两个数变成唯一分解式,那么他们的lcm就为各项因子的幂的最大值的乘积。
所以基于这一点,我们就可以计算出答案了。
假如当前因子的幂的最大值为cnt,那么另一个数 的因子的幂的个数就的区间为[0,cnt],理论上有cnt+1个选择,然后再乘以2表示不同边,但是如果它也选cnt的话,实际上会算重1次,再乘以后面的数就会导致重复计算很多次,所以我们将其减1,那么它就只计算了1次。之后再把它的1加进去,这样所有的答案就算了两次。再除以2即可。
#include<iostream>
#include<algorithm>
#include<cstring>
#include<stdio.h>
#include<math.h>
using namespace std;
typedef long long ll;
const int maxn = 1e7 + 5;
bool is[maxn];
int prm[700000], id;
void init()
{
memset(is, 1, sizeof(is));
is[0] = is[1] = 0;
prm[++id] = 2;
for (ll i = 4;i < maxn;i += 2)is[i] = 0;
int e = (int)sqrt(0.0 + maxn) + 1;
ll i;
for (i = 3;i <= e;i += 2)if (is[i])
{
prm[++id] = i;
for (ll s = 2 * i, j = i*i;j < maxn;j += s)
is[j] = 0;
}
for (;i < maxn;i += 2)if (is[i])prm[++id] = i;
//cout << id << endl;
}
int main()
{
init();
int T;
scanf("%d", &T);
int cas = 1;
while (T--)
{
ll n;
scanf("%lld", &n);
ll ans = 1;
for (int i = 1;i <= id&&prm[i] <= n / prm[i];i++)
{
//if (n == 1)break;
if (n%prm[i] == 0)
{
int cnt = 0;
while (n%prm[i] == 0)
{
n /= prm[i];
cnt++;
}
ans *= (2*cnt + 1);
}
}
if (n != 1) { ans *= (2 + 1); }
ans = (ans + 1) / 2;
printf("Case %d: %lld\n", cas++, ans);
}
return 0;
}
I - Harmonic Number
这题一看就会想到打表,但是打表会mle,所以这里用一个小技巧,我们可以100个一存,那么就可以减少一些内存的使用了。之后再计算的时候暴力即可。
#include<iostream>
#include<algorithm>
#include<cstring>
#include<stdio.h>
using namespace std;
const int maxn = 1000005;
double H[maxn];
const double eps = 1e-7;
void init()
{
double tmp = 0;
for (int i = 1;i <= 100000000;i++)
{
tmp += 1.0 / i;
if (i % 100 == 0)H[i / 100] = tmp;
}
}
int main()
{
init();
int T;
scanf("%d", &T);
int cas = 1;
while (T--)
{
int n;
scanf("%d", &n);
int tmp = n / 100;
double ans = H[tmp];
for (int i = tmp * 100 + 1;i <= n;i++)
{
ans += 1.0 / i;
}
printf("Case %d: %.10f\n", cas++, ans);
}
return 0;
}
J - Mysterious Bacteria
我们将数字进行唯一分解,那么所有因子的幂的最大公约数就是答案。
此外,当x为负数时,答案不能为偶数,所以我们就得将答案一直除以2直到它不是偶数为止。
#include<iostream>
#include<algorithm>
#include<cstring>
#include<stdio.h>
#include<vector>
#include<math.h>
using namespace std;
typedef long long ll;
const int maxn = 70000;
bool is[maxn];
int prm[maxn], id;
void init()
{
memset(is, 1, sizeof(is));
is[0] = is[1] = 0;
prm[++id] = 2;
for (int i = 4;i < maxn;i += 2)is[i] = 0;
int i;
int e = (int)sqrt(0.0 + maxn) + 1;
for (i = 3;i <= e;i += 2)if (is[i])
{
prm[++id] = i;
for (ll s = 2 * i, j = i*i;j < maxn;j += s)
is[j] = 0;
}
for (;i < maxn;i += 2)if (is[i])prm[++id] = i;
}
int a[maxn],cnt;
int gcd(int a, int b)
{
return b == 0 ? a : gcd(b, a%b);
}
int solve()
{
if (cnt == 1)return a[1];
int now = a[1];
for (int i = 2;i <= cnt;i++)
{
now = gcd(now, a[i]);
}
return now;
}
int main()
{
init();
int T;
scanf("%d", &T);
int cas = 1;
while (T--)
{
memset(a, 0, sizeof(a));
cnt = 0;
ll n;
scanf("%lld", &n);
int flag = true;
if (n < 0) { n = -n;flag = false; }
for (int i = 1;i <= id&&prm[i] <= n / prm[i];i++)
{
if (n == 1)break;
if (n%prm[i] == 0)
{
++cnt;
while (n%prm[i] == 0)
{
n /= prm[i];
a[cnt]++;
}
}
}
if (n != 1)
{
++cnt;
a[cnt] = 1;
}
int ans = solve();
if (!flag)
{
while (ans%2==0)
{
ans /= 2;
}
}
printf("Case %d: %d\n", cas++,ans);
}
return 0;
}
K - Large Division
大整数取模。
#include<iostream>
#include<algorithm>
#include<cstring>
#include<stdio.h>
#include<string>
using namespace std;
typedef long long ll;
int main()
{
int T;
scanf("%d", &T);
int cas = 1;
while (T--)
{
string a;
ll b;
cin >> a >> b;
b = abs(b);
int Size = a.size();
ll ans = 0;
int i=0;
if (a[0] == '-')i = 1;
for (;i < Size;i++)
{
ans = (ans * 10 + a[i] - '0') % b;
}
printf("Case %d: ", cas++);
if (ans)puts("not divisible");
else puts("divisible");
}
return 0;
}
M - Help Hanzo
计算小范围内质数个数模板
#include<iostream>
#include<algorithm>
#include<cstring>
#include<stdio.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 5;
int small[maxn], prime[maxn];
ll a, b;
void segment_sieve()
{
for (int i = 0;(ll)i*i <= b;i++)small[i] = 0;
for (int i = 0;i <= b - a;i++)prime[i] = 0;
for (int i = 2;(ll)i*i <= b;i++)
{
if (!small[i])
{
for (int j = i*i;(ll)j*j <= b;j += i)small[j] = 1;
for (ll j = (a + i - 1) / i*i;j <= b;j += i)
{
if (j == i)continue;
prime[j - a] = 1;
}
}
}
}
int main()
{
int T;
scanf("%d", &T);
int cas = 1;
while (T--)
{
cin >> a >> b;
int ans = 0;
segment_sieve();
for (int i = 0;i <= b - a;i++)if (!prime[i])ans++;
if (a == 1)ans--;
printf("Case %d: %d\n", cas++, ans);
}
}
N - Trailing Zeroes (III)
0必定是由1个2和一个5组成的,而从1到N,2的个数肯定多于5,所以我们只需计算1到N有多少个5就可以知道有多少个0了。
#include<iostream>
#include<algorithm>
#include<cstring>
#include<stdio.h>
#pragma warning (disable:4996)
using namespace std;
int Q;
int check(int num)
{
int ans = 0;
while (num)
{
ans += num / 5;
num /= 5;
}
if (ans == Q)return 1;
else if (ans > Q)return 2;
return 0;
}
int main()
{
int T;
scanf("%d", &T);
int cas = 1;
while (T--)
{
scanf("%d", &Q);
int l = 5, r = 5e8;
int mid, ans = 0;
while (l<=r)
{
mid = l + r >> 1;
if (check(mid))
{
ans = mid;
r = mid - 1;
}
else
l = mid + 1;
}
printf("Case %d: ", cas++);
if (check(ans) == 1)
printf("%d\n", ans);
else puts("impossible");
}
}
O - GCD - Extreme (II)
这题好难啊。写题解的时候都不知道怎么做的了。。
下面的题解摘自hyogahyoga
1.建立递推关系,s(n)=s(n-1)+gcd(1,n)+gcd(2,n)+……+gcd(n-1,n);
2.设f(n)=gcd(1,n)+gcd(2,n)+……+gcd(n-1,n)。
gcd(x,n)=i是n的约数(x< n),按照这个约数进行分类。设满足gcd(x,n)=i的约束有g(n,i)个,则有f(n)=sum(i*g(n,i))。
而gcd(x,n)=i等价于gcd(x/i,n/i)=1,因此g(n,i)等价于phi(n/i).phi(x)为欧拉函数。
3.降低时间复杂度。用筛法预处理phi[x]表
用筛法预处理f(x)- > 枚举因数,更新其所有倍数求解。
#include<iostream>
#include<algorithm>
#include<cstring>
#include<stdio.h>
#include<math.h>
using namespace std;
const int maxn = 4000005;
int phi[maxn];
void phi_table(int n)
{
for (int i = 2;i <= n;i++)phi[i] = 0;
phi[1] = 1;
for(int i=2;i<=n;i++)if(!phi[i])
for (int j = i;j <= n;j += i)
{
if (!phi[j])phi[j] = j;
phi[j] = phi[j] / i*(i - 1);
}
}
typedef long long ll;
ll s[maxn];
ll f[maxn];
void init()
{
for (int i = 1;i < maxn / 2;i++)
for (int j = i + i;j < maxn;j+=i)
f[j] += phi[j / i] * i;
}
int main()
{
phi_table(maxn - 5);
init();
int N;
while (~scanf("%d",&N)&&N)
{
//scanf("%d", &N);
memset(s, 0, sizeof(s));
s[1] = 0;
for (int i = 2;i <= N;i++)s[i] = s[i - 1] + f[i];
printf("%lld\n", s[N]);
}
}
P - Code Feat
当总个数很小的话,我们可以用中国剩余定理暴力求出所有的解,但个数特别大的话,这里有一个玄妙的地方,就是找出k/m最小的那个,然后通过从小到大枚举符合它的一个模的值,在判断其他的是否符合看这个值可不可行。感觉有点玄妙。。
#include<iostream>
#include<algorithm>
#include<cstring>
#include<stdio.h>
#include<set>
#include<vector>
using namespace std;
#pragma warning (disable:4996)
typedef long long ll;
const int maxC = 15;
const int limit = 1e4;
ll m[maxC];
int k[maxC];
ll Y[maxC][105];
int bestc;
set<ll>vis[maxC];
int C, S;
void solve()
{
for (int i = 1;i <= C;i++)if (i != bestc)
for (int j = 1;j <= k[i];j++)
{
vis[i].insert(Y[i][j]);
}
for (int ti = 0;S;ti++)
{
for (int i = 1;i <= k[bestc];i++)
{
ll num = 1LL * ti*m[bestc] + Y[bestc][i];
if (num == 0)continue;
bool flag = true;
for (int j = 1;j <= C;j++)if (j != bestc)
{
ll tmp = num%m[j];
if (!vis[j].count(tmp))
{
flag = false;
break;
}
}
if (flag)
{
printf("%lld\n", num);
if (--S == 0)break;
}
}
}
}
vector<ll>ans;
ll _a[15];
ll M;
void initchina()
{
M = 1;
for (int i = 1;i <= C;i++)
M *= m[i];
}
ll exgcd(ll a, ll b, ll &x, ll &y)
{
if (b == 0) { x = 1;y = 0;return a; }
ll d = exgcd(b, a%b, x, y);
ll t = x;x = y;y = t - a / b*y;
return d;
}
void gcd(ll a, ll b, ll& d, ll& x, ll& y) {
if (!b) {
d = a;
x = 1;
y = 0;
}
else {
gcd(b, a%b, d, y, x);
y -= x*(a / b);
}
}
void solvechina()
{
ll x, d;
ll res = 0;
for (int i = 1;i <= C;i++)
{
ll Mi = M / m[i];
gcd(m[i], Mi, d, d, x);
//x = (x%m[i] + m[i]) % m[i];
ll tmp = Mi*x%M;
tmp = tmp*_a[i] % M;
res = (res + tmp) % M;
}
if (res<0)
res = (res + M) % M;
ans.push_back(res);
}
void dfs(int d)
{
if (d == C + 1)
{
solvechina();
return;
}
for (int i = 1;i <= k[d];i++)
{
_a[d] = Y[d][i];
dfs(d + 1);
}
}
void init()
{
for (int i = 1;i <= C;i++)vis[i].clear();
ans.clear();
}
int main()
{
while (~scanf("%d %d", &C, &S) && C)
{
init();
bestc = -1;
ll casnum = 1;
for (int i = 1;i <= C;i++)
{
scanf("%lld %d", &m[i], &k[i]);
casnum = casnum*k[i];
for (int j = 1;j <= k[i];j++)
{
scanf("%lld", &Y[i][j]);
}
sort(Y[i] + 1, Y[i] + k[i] + 1);
if (bestc == -1 || m[bestc] * k[i] < m[i] * k[bestc])
{
bestc = i;
}
}
if (casnum > limit)
solve();
else
{
initchina();
dfs(1);
sort(ans.begin(), ans.end());
for (int i = 0;S;i++)
{
for (int j = 0;j < ans.size();j++)
{
ll theans = M*i + ans[j];
if (theans > 0)
{
printf("%lld\n", theans);
if (--S == 0)break;
}
}
}
}
puts("");
}
return 0;
}
Q - Emoogle Grid
这题也挺好的。
首先判断最大x的blocks上面的ans是否等于R,如果不等那么再加1行在判断,如果仍然不等的话就可以用BSGS求离散对数了。
#include<iostream>
#include<algorithm>
#include<stdio.h>
#include<cstring>
#include<map>
#include<vector>
#include<math.h>
using namespace std;
map<pair<int,int>,int>M;
typedef pair<int,int>pii;
typedef long long ll;
const ll mod=1e8+7;
void change(int x,int y)
{
pii tmp=make_pair(x,y);
M[tmp]=0;
tmp=make_pair(x+1,y);
if(!M.count(tmp))
M[tmp]=1;
}
int N,K,B,R;
void takeone()
{
for(int i=1;i<=N;i++)
{
pii tmp=make_pair(1,i);
if(M.count(tmp))continue;
M[tmp]=1;
}
}
ll Countone()
{
ll cnt=0;
map<pii,int>::iterator itor;
for(itor=M.begin();itor!=M.end();itor++)
{
if(itor->second==1)
cnt++;
}
return cnt;
}
ll quick_mul(ll a,ll b)
{
ll ans=1;
while(b)
{
if(b%2)ans=ans*a%mod;
a=a*a%mod;
b/=2;
}
return ans;
}
ll extgcd(ll a,ll b,ll &x,ll &y)
{
if(b==0){x=1;y=0;return a;}
ll d=extgcd(b,a%b,x,y);
int t=x;x=y;y=t-a/b*y;
return d;
}
ll Inv(ll a,ll p)
{
ll x,y,d;
d=extgcd(a,p,x,y);
if(d==1)return (x%p+p)%p;
return -1;
}
ll BSGS(ll a,ll b,ll p)
{
a%=p,b%=p;
map<ll,ll>h;
ll m=ceil(sqrt(p)),x,y,d,t=1,v=1;
for(ll i=0;i<m;i++)
{
if(h.count(t))h[t]=min(h[t],i);
else h[t]=i;
t=(t*a)%p;
}
for(ll i=0;i<m;i++)
{
d=extgcd(v,p,x,y);
x=(x*b/d%p+p)%p;
if(h.count(x))return i*m+h[x];
v=(v*t)%p;
}
return -1;
}
int main()
{
int T;
scanf("%d",&T);
int cas=1;
while(T--)
{
M.clear();
scanf("%d %d %d %d",&N,&K,&B,&R);
int x,y;
int mxrow=0,mxcnt=0;
int one=0;
for(int i=1;i<=B;i++)
{
scanf("%d %d",&x,&y);
change(x,y);
if(x==1)one++;
if(mxrow<x)
{
mxrow=x;
mxcnt=1;
}
else if(mxrow==x)
mxcnt++;
}
printf("Case %d: ",cas++);
ll cnt=Countone();
if(mxrow>=1)
cnt+=N-one;
ll sum=1LL*mxrow*N-B;
ll ans=quick_mul(K,cnt-mxcnt)*quick_mul(K-1,sum-(cnt-mxcnt))%mod;
if(ans==R)
{
printf("%d\n",mxrow);
}
else
{
if(mxrow>=1)
ans*=quick_mul(K,mxcnt)*quick_mul(K-1,N-mxcnt)%mod;
else
ans*=quick_mul(K,N);
if(ans==R)
printf("%d\n",mxrow+1);
else
{
mxrow++;
ll _k=quick_mul(K-1,N);
ll invans=Inv(ans,mod);
ll theans=BSGS(_k,1LL*R*invans%mod,mod);
printf("%lld\n",theans+mxrow);
}
}
}
return 0;
}
R - 青蛙的约会
解同余方程。
求最小正整数解那个地方想了很久终于想通了。
#include<iostream>
#include<algorithm>
#include<stdio.h>
#include<cstring>
#include<math.h>
#include<stdlib.h>
using namespace std;
typedef long long ll;
#define Abs(x) ((x)<0?-x:x)
ll gcd(ll a, ll b)
{
return b == 0 ? a : gcd(b, a%b);
}
ll exgcd(ll a, ll b, ll &x, ll &y)
{
if (b == 0) { x = 1;y = 0;return a; }
ll d = exgcd(b, a%b, x, y);
ll t = x;x = y;y = t - a / b*y;
return d;
}
int main()
{
ll x, y, m, n, L;
cin >> x >> y >> m >> n >> L;
ll d = gcd(m - n, L);
if (d==0||(y - x) % d != 0)
puts("Impossible");
else
{
ll t, k;
if (m - n > 0)
exgcd(m - n, L, t, k);
else
exgcd(n - m, -L, t, k);
ll ans = t*(y - x) / d;
d = Abs(d);
ans %= (L / d);
ans = (ans + L / d) % (L / d);
printf("%lld\n", ans);
}
}
W - Prime Time
这题虽然相比较上面的而言比较简单,但还是有一些坑点,就是精度问题。所以涉及浮点数输出的时候不妨加个eps。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define eps 1e-7
bool check(ll a)
{
ll e=(ll)sqrt(0.0+a)+1;
for(ll i=2;i<e;i++)if(a%i==0)return 0;
return 1;
}
int t[10005];
void init()
{
t[0]=1;
for(int i=1;i<=10000;i++)
{
t[i]=t[i-1];
ll tmp=1LL*i*i+i+41;
if(check(tmp))t[i]++;
}
}
int main()
{
//freopen("D:\\1.txt","r",stdin);
//freopen("D:\\2.txt","w",stdout);
init();
int a,b;
while(~scanf("%d %d",&a,&b))
{
int left;
if(a==0)left=0;
else left=t[a-1];
printf("%.2f\n",(0.0+t[b]-left)/(b-a+1)*100.0+eps);
}
}
X - The equation
解不等式,有好多坑的地方。。。
首先判断一下a和b为0的情况。
除去这种特殊情况后,用exgcd求出答案,那么X=x+k*(b/d),Y=y-k*(a/d)。联立不等式求出k的范围,为了避免负数的情况,我们可以预处理一下a和b和c的正负。
#include<iostream>
#include<algorithm>
#include<cstring>
#include<stdio.h>
#include<math.h>
using namespace std;
typedef long long ll;
ll extgcd(ll a,ll b,ll &d,ll &x,ll &y)
{
if(!b)
d=a,x=1,y=0;
else
{
extgcd(b,a%b,d,y,x);
y-=a/b*x;
}
}
int main()
{
ll a,b,c,x1,x2,y1,y2;
scanf("%lld %lld %lld %lld %lld %lld %lld",&a,&b,&c,&x1,&x2,&y1,&y2);
c=-c;
if(c<0)
c=-c,a=-a,b=-b;
if(a<0)
a=-a,swap(x1,x2),x1=-x1,x2=-x2;
if(b<0)
b=-b,swap(y1,y2),y1=-y1,y2=-y2;
if(a==0||b==0)
{
if(a==0&&b==0)
{
if(c==0)
cout<<(x2-x1+1)*(y2-y1+1)<<endl;
else puts("0");
}
else if(a==0)
{
ll tmp=c/b;
if(c%b==0&&tmp>=y1&&tmp<=y2)printf("%lld\n",x2-x1+1);
else puts("0");
}
else if(b==0)
{
ll tmp=c/a;
if(c%a==0&&tmp>=x1&&tmp<=x2)printf("%lld\n",y2-y1+1);
else puts("0");
}
}
else
{
ll x,y,d;
extgcd(a,b,d,x,y);
if(c%d)puts("0");
else
{
x=x*(c/d);
y=y*(c/d);
double tmp1=b/d;
double tmp2=a/d;
ll r=min(floor(1.0*(x2-x)/tmp1),floor(1.0*(y-y1)/tmp2)),l=max(ceil(1.0*(x1-x)/tmp1),ceil(1.0*(y-y2)/tmp2));
if(l>r)puts("0");
else printf("%lld\n",r-l+1);
}
}
}
Y - Farey Sequence
求phi,然后前缀和即可。
#include<iostream>
#include<algorithm>
#include<cstring>
#include<stdio.h>
using namespace std;
const int maxn=1e6+5;
typedef long long ll;
ll phi[maxn];
void init()
{
for(int i=2;i<maxn;i++)phi[i]=0;
phi[1]=1;
for(int i=2;i<maxn;i++)if(phi[i]==0)
for(int j=i;j<maxn;j+=i)
{
if(!phi[j])phi[j]=j;
phi[j]=phi[j]/i*(i-1);
}
for(int i=3;i<maxn;i++)
phi[i]+=phi[i-1];
}
int main()
{
init();
int n;
while(~scanf("%d",&n)&&n)
{
printf("%lld\n",phi[n]);
}
}
Z - The Super Powers
只要幂不是质数,那么就是 super power number,所以先处理出质数,然后快速幂的时候额外判断一下是否超过了limit。
#include<iostream>
#include<algorithm>
#include<cstring>
#include<stdio.h>
#include<set>
#include<vector>
#include<math.h>
using namespace std;
typedef unsigned long long ll;
set<ll>S,ans;
const int maxn=100;
ll limit=(1ULL<<64)-1;
bool is[maxn];
int prm[maxn],id;
void init()
{
memset(is,1,sizeof(is));
int i, j, k = 0;
int s, e = (int)(sqrt(0.0 + maxn) + 1);
prm[k++] = 2; is[0] = is[1] = 0;
for (i = 4; i < maxn; i += 2) is[i] = 0;
for (i = 3; i < e; i += 2) if (is[i]) {
prm[k++] = i;
for (s = i * 2, j = i * i; j < maxn; j += s)
is[j] = 0;
// 因为j 是奇数,所以+ 奇数i 后是偶数,不必处理!
}
for ( ; i < maxn; i += 2) if (is[i]) prm[k++] = i;
id=k;
}
ll quick_mul(ll a,ll b)
{
ll ans=1;
while(b)
{
if(b%2)
{
//if(ans>limit/a)return -1;
ans*=a;
if(b==1)break;
}
if(ans>limit/a/a)return -1;
a*=a;
b/=2;
}
return ans;
}
int main()
{
init();
//cout<<limit<<endl;
for(int i=2;i<=64;i++)if(!is[i])
{
ll now=1;
while(1)
{
ll tmp=quick_mul(now,i);
if(tmp==-1)break;
ans.insert(tmp);
now++;
}
}
set<ll>::iterator itor;
for(itor=ans.begin();itor!=ans.end();itor++)
cout<<*itor<<endl;
}
注意,快速幂中b=1的时候实际上不用往下走了,所以要加个判断来break掉,否则可能会输出-1.
中国剩余定理即求离散对数的BSGS会在另一篇博客详细介绍。总之数论感觉仍然没有入门,还需要加油啊。