Problem C
理解清楚7的出现次数是指什么,对于每一位的贡献有多少
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
const ll mod = 1000000007;
const int maxn = 100000 + 5;
char s[maxn];
ll p[maxn], ppgod[maxn], postfix[maxn];
int main(void) {//1000对7的贡献=个位7贡献10*10,十位7贡献10*10,百位7贡献10*10
p[0] = 0; //10000对7的贡献=个/十/百/千位的7都贡献10*10*10
ppgod[0] = 1;
for (int i = 1; i <= 100000; i++) {
p[i] = (ppgod[i - 1] * i) % mod;//i*10^(i-1)
ppgod[i] = (ppgod[i - 1] * 10) % mod;//i表示后缀0的个数
//ppgod[i]表示10的i次方
}
int t; scanf("%d", &t);
while (t--) {
scanf("%s", s);
int len = strlen(s);
postfix[len] = 0;//求从个位开始直到第len-i位的数值
for (int i = len - 1; i >= 0; i--) {
int x = s[i] - '0';
//本位是第len-i位,要乘以10^(len-i-1)
postfix[i] = (postfix[i + 1] + x * ppgod[len - i - 1]) % mod;
}
ll ans = 0;
for (int i = 0; i < len; i++) {
int x = s[i] - '0';//本位是第len-i位
ans = (ans + x * p[len - 1 - i]) % mod;
if (x > 7)
ans = (ans + ppgod[len - i - 1]) % mod;//本位的7贡献10^(len-i-1)
else if (x == 7)
ans = (ans + postfix[i + 1] + 1) % mod;//本位的7贡献+1(0...0) 以及加上后面的贡献
}
printf("%lld\n", ans);
}
return 0;
}
Problem D
求最大被5整除序列和,查了一下求最长序列,知道了用mod的方法
代码如下:
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 5;
int n;
ll a[maxn], dp[5], tmp[5];
int main(void) {
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
for (int j = 0; j < 5; j++)
if (j == 0 || tmp[j] != 0) {
int xb = (j + a[i]) % 5;
dp[xb] = max(dp[xb], tmp[j] + a[i]);
}
int xb = a[i] % 5;
tmp[xb] = max(tmp[xb], a[i]);
for (int j = 0; j < 5; j++) {
tmp[j] = max(tmp[j], dp[j]);
}
}
printf("%lld\n", tmp[0]);
return 0;
}
Problem E
问要多少步可以关掉全部的灯,每次必须先关入度为0的灯,拓扑一下就可以了。
#include<cstdio>
#include<vector>
#include<cstring>
#include<queue>
using namespace std;
const int maxn = 4e4 + 5;
int n, m, in[maxn];
bool on[maxn], vis[maxn];
vector<int>nxt[maxn];
void switchIt(int id) {
on[id] = !on[id];
for (int i = 0; i < nxt[id].size(); i++) {
int v = nxt[id][i];
if (vis[v])continue;
vis[v] = true;
switchIt(v);
}
}
int main(void) {
on[1] = true;
scanf("%d %d", &n, &m);
while (m--) {
int u, v;
scanf("%d %d", &u, &v);
nxt[u].push_back(v);
in[v]++;
}
queue<int>Q;
for (int i = 1; i <= n; i++)
if (!in[i])Q.push(i);
vector<int>range;
while (!Q.empty()) {
int u = Q.front(); Q.pop();
range.push_back(u);
for (int i = 0; i < nxt[u].size(); i++) {
int v = nxt[u][i];
in[v]--;
if (!in[v])Q.push(v);
}
}
int cnt = 0;
for (int i = 0; i < range.size(); i++) {
if (!on[range[i]])continue;
memset(vis, 0, sizeof(vis));
switchIt(range[i]);
cnt++;
}
printf("%d\n", cnt);
return 0;
}
Problem F
以前没看懂"|"是整除的意思,后知后觉这是水题
#include<cstdio>
using namespace std;
typedef long long ll;
const int limit = 1e6;
const int maxn = 1e6 + 5;
ll sum[maxn];
int Q, L, R;
int main(void) {
for (int i = 1; i <= limit; i++) {
for (int j = i; j <= maxn; j += i)
sum[j] += i;
}
for (int i = 2; i <= limit; i++)
sum[i] += sum[i - 1];
scanf("%d", &Q);
while (Q--) {
scanf("%d %d", &L, &R);
printf("%lld\n", sum[R] - sum[L - 1]);
}
return 0;
}
Problem G
题解
期末考试:
将所有a换成二进制,统计每一位上有几个数为1,这样问题就转换成有若干个物品,
每个物品花费2的若干次方,得到的价值为2的若干次方乘上一个数,每种物品有两个形态,
一个形态表示k这一位为0,则价值乘上的数为a中这一位为1的数的个数,
另一个形态表示k这一位为1,价值乘上的数为a中这一位为0的数的个数。
一开始我们先贪心求出一个k使得sigma(a^k)最小,这个贪心是显然的,
之后我们从高位往低位贪心求k,
如果这一位k已经为1,则不用管直接继续下一位,
如果这一位k为0,那么贪心取,
如果这一位k为1的价值加上已有价值小于等于m的话,那这一位一定为1,
否则k这一位一定为0(为1就不符合题意了)最后输出得到的k就是答案
注意一下数据,这个数据很大,看似需要高精度,实际上只需要一个特判就可以判掉无解
时间复杂度o(40(n+q))
#include<cstdio>
using namespace std;
typedef long long ll;
const int maxn = 100000 + 5;
int n, a, Q, zeroCnt[51], oneCnt[51];
ll m, pow2[51];
int main(void) {
scanf("%d", &n);
for (int i = 31; i <= 50; i++)zeroCnt[i] = n;
for (int i = 0; i < n; i++) {
scanf("%d", &a);
for (int j = 0; j <= 30; j++) {
if (a & 1)
oneCnt[j]++;
else
zeroCnt[j]++;
a >>= 1;
}
}
pow2[0] = 1;
for (int i = 1; i <= 50; i++)
pow2[i] = pow2[i - 1] << 1;//2^i
scanf("%d", &Q);
while (Q--) {
scanf("%lld", &m);
ll k = 0, sum = 0;
for (int i = 0; i <= 50; i++) {//先贪心求k使得和最小
if (oneCnt[i] > zeroCnt[i]) {//1多,本位补1
k += pow2[i];
sum += zeroCnt[i] * pow2[i];
}
else
sum += oneCnt[i] * pow2[i];
}
if (sum > m) {
printf("-1\n"); continue;
}
for (int i = 50; i >= 0; i--) {
if (k & pow2[i])continue;
ll tmp = pow2[i] * (zeroCnt[i] - oneCnt[i]);
if (tmp < 0)continue;
if (sum + tmp > m)continue;
sum += tmp;
k += pow2[i];
}
printf("%lld\n", k);
}
return 0;
}