2018.08.20 NOIp模拟赛
GKK大佬出的毒瘤题,烧脑。全是原题就不要密码保护了。
第一题
一张图,每条边有代价也有限制,遍历过的点可以解锁这些限制,求最短路。这是一道套路题,平时根本没见过,考场上因为一个状态或错了调了好久好久。对每个点状压记个状态判断能不能走这条边就行了。
code
#include<bits/stdc++.h>
#define Set(a, b) memset(a, b, sizeof (a))
#define fir first
#define sec second
#define mp make_pair
#define For(i, j, k) for(int i = j; i <= k; ++i)
#define Travel(i, u) for(int i = beg[u], v = to[i]; i; i = nex[i], v = to[i])
using namespace std;
inline int read() {
int x = 0, p = 1; char c = getchar();
for(; !isdigit(c); c = getchar()) if(c == '-') p = -1;
for(; isdigit(c); c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
return x *= p;
}
template<typename T> inline bool chkmin(T &a, T b) { return a > b ? a = b, 1 : 0; }
template<typename T> inline bool chkmax(T &a, T b) { return a < b ? a = b, 1 : 0; }
inline void File() {
freopen("dalao.in", "r", stdin);
freopen("dalao.out", "w", stdout);
}
typedef pair<int, int> PII;
const int N = 200 + 10, M = 6e3 + 10;
const int maxp = (1 << 13) + 10, inf = 0x7f7f7f7f;
int e = 1, beg[N], nex[M], to[M], w[M], stt[M];
int vis[N][maxp], dis[N][maxp], st[N];
int n, m, p, k;
inline void add(int x, int y, int z) {
to[++ e] = y, nex[e] = beg[x], beg[x] = e, w[e] = z;
}
inline void spfa() {
Set(vis, 0), Set(dis, 127);
queue<PII> Q; Q.push(mp(st[1], 1)), vis[1][st[1]] = 1, dis[1][st[1]] = 0;
while (!Q.empty()) {
PII u = Q.front(); Q.pop(), vis[u.sec][u.fir] = 0;
Travel(i, u.sec) {
int nst = u.fir | st[v];
if (dis[v][nst] > dis[u.sec][u.fir] + w[i] && (stt[i] | u.fir) == u.fir) {
dis[v][nst] = dis[u.sec][u.fir] + w[i];
if (!vis[v][nst]) vis[v][nst] = 1, Q.push(mp(nst, v));
}
}
}
}
int main() {
File();
cin >> n >> m >> p >> k;
For(i, 1, k) {
int pos = read(), num = read(), x;
For(i, 1, num) x = read(), st[pos] |= (1 << (x - 1));
}
For(i, 1, m) {
int x = read(), y = read(), z = read(), num = read(), a, s = 0;
For(i, 1, num) a = read(), s |= (1 << (a - 1));
add(x, y, z), stt[e] = s;
add(y, x, z), stt[e] = s;
}
spfa();
int ans = inf;
For(i, 0, (1 << p) - 1) chkmin(ans, dis[n][i]);
printf("%d\n", ans == inf ? -1 : ans);
return 0;
}
第二题
用\(~n~\)个数去匹配\(~m~\)个数对,可以选择匹配与否,\(\sum ~[a_i > b_j] \times (a_i - b_j + c_j)~\)的最大值。我考场上想了一个贪心,把数对按\(~c_i - b_i~\)从大到小排序,每次二分一个满足条件的最小的\(~a~\)去匹配,当\(~(a_i - b_j + c_j) > 0~\)是计入答案,最后计算可以通过更换剩余的\(~a~\)而产生的更大的贡献。
其实这个离正解贪心差不多了,但我太菜了没想到一个细节:就算一个\(~(a_i - b_j + c_j)~\)的贡献是负的,之后也可以通过更换\(~a~\)来产生更大的贡献。我那样打就导致大样例答案总是小一点,于是就滚去打阶乘的\(~20pts~\)的暴力了。所以正解贪心就是:先把数对按\(~c_i - b_i~\)从大到小排序, 每次二分\(~a~\)看这个数对是否满足条件,满足就记下来,最后再一起算答案,每次用最大的\(~a~\)去匹配先前记下来的数对。我好菜啊。。。
code
#include<bits/stdc++.h>
#define For(i, j, k) for(int i = j; i <= k; ++i)
#define Forr(i, j, k) for(int i = j; i >= k; --i)
using namespace std;
inline int read() {
int x = 0, p = 1; char c = getchar();
for(; !isdigit(c); c = getchar()) if(c == '-') p = -1;
for(; isdigit(c); c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
return x *= p;
}
inline void File() {
freopen("winner.in", "r", stdin);
freopen("winner.out", "w", stdout);
}
typedef long long ll;
const int N = 1e5 + 10;
int a[N], n, m, b[N], c[N], val[N], cnt;
struct node { int b, c; } P[N];
multiset<ll> S;
inline bool cmp(const node &a, const node &b) { return a.c - a.b > b.c - b.b; }
inline bool cmpa(const int &a, const int &b) { return a > b; }
int main() {
File();
n = read(), m = read();
For(i, 1, n) S.insert(a[i] = read());
For(i, 1, m) b[i] = read(), c[i] = read(), P[i] = (node) {b[i], c[i]};
sort(P + 1, P + 1 + m, cmp);
For(i, 1, m) {
auto it = S.upper_bound(P[i].b);
if (it == S.end()) continue;
else val[++ cnt] = P[i].c - P[i].b, S.erase(it);
}
sort(a + 1, a + 1 + n, cmpa);
ll ans = 0;
For(i, 1, cnt) if (val[i] + a[i] > 0) ans += val[i] + a[i];
printf("%lld\n", ans);
return 0;
}
第三题
先%%%HYJ大佬!给出\(~m, ~a, ~b, ~c~\), \(~a, ~b, ~c~\)两两互质,求解\(~x ^ a + y ^ b \equiv z^c ~(mod ~m)~\). 可以构造使\(~x = 2 ^ {kb}, ~ y = 2 ^ {ka}, z = 2 ^ p~\), 那么原式化为\(~2 ^ {kab + 1} \equiv 2 ^ {pc}~\), 则特判掉\(~m~\)是\(~2 ^ k~\)次幂,\(~k \in N^{+}\)的情况。剩余则有\(~kab + 1 = pc~\), 转化一下就是扩欧了。膜烂hyj考场上火速ak并教会我!
code
#include<bits/stdc++.h>
#define For(i, j, k) for(int i = j; i <= k; ++i)
#define Forr(i, j, k) for(int i = j; i >= k; --i)
using namespace std;
inline int read() {
int x = 0, p = 1; char c = getchar();
for(; !isdigit(c); c = getchar()) if(c == '-') p = -1;
for(; isdigit(c); c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
return x *= p;
}
inline void File() {
freopen("guess.in", "r", stdin);
freopen("guess.out", "w", stdout);
}
typedef long long ll;
int mod, a, b, c;
inline ll qpow(ll a, ll b) {
ll res = 1;
if (b < 0) b = -b;
for (res = 1; b; a = a * a % mod, b >>= 1)
if (b & 1) res = res * a % mod;
return res;
}
inline void BF_Solve() {
int flag = 0;
For(x, 1, mod - 1) {
For(y, 1, mod - 1) {
For(z, 1, mod - 1) {
if ((qpow(x, a) + qpow(y, b)) % mod == qpow(z, c) % mod) {
printf("%d %d %d\n", x, y, z);
flag = 1; break;
}
}
if (flag) break;
}
if (flag) break;
}
}
ll exgcd(ll a, ll b, ll &x, ll &y) {
if (b == 0) return x = 1, y = 0, a;
ll d = exgcd(b, a % b, x, y), tmp;
tmp = x, x = y, y = tmp - a / b * y;
return d;
}
int main() {
File();
for (int Case = read(); Case --; ) {
mod = read(), a = read(), b = read(), c = read();
int tt = mod, x, y, z;
while (!(tt & 1)) tt /= 2;
if (tt != 1) {
ll A = 1ll * a * b, B = c, X, Y, d;
d = exgcd(B, A, X, Y);
while (X < 0 || Y > 0) X += A, Y -= B;
x = qpow(2, 1ll * Y * b), y = qpow(2, 1ll * Y * a), z = qpow(2, X);
} else {
int res = mod >> 1;
if (a > 1) x = res, y = z = 1;
if (a == 1 && b > 1) y = res, x = z = 1;
if (a == 1 && b == 1 && c > 1) x = y = z = res;
if (a == 1 && b == 1 && c == 1) x = 1, y = 2, z = 3;
}
printf("%d %d %d\n", x, y, z);
}
return 0;
}
今天整个神游,第一题十点才调出来,大概是昨天晚上改AGC005F到一点的还没调出来的后遗症,以后考试要保持好精力,不然得不偿失。GKK不愧是gay, 只是题面少了谁,美中不足。