T1
发现 很小,在最短路的基础上再加一维表示一个二进制数,代表当前拥有的剑的种类数,直接用 就可以过了
Codes
#include<bits/stdc++.h>
#define pb push_back
using namespace std;
const int N = 200 + 10;
vector<int> G[N], v[N], w[N];
int n, m, p, k, val[N];
int dis[N][1 << 13], vis[N][1 << 13], ans = INT_MAX;
void Init() {
int x, y, z, t;
scanf("%d%d%d%d", &n, &m, &p, &k);
for(int i = 1; i <= k; ++ i) {
scanf("%d%d", &x, &y);
for(int j = 1; j <= y; ++ j) {
scanf("%d", &z);
val[x] |= (1 << (z - 1));
}
}
for(int i = 1; i <= m; ++ i) {
int sum = 0;
scanf("%d%d%d%d", &x, &y, &z, &t);
G[x].pb(y), v[x].pb(z);
G[y].pb(x), v[y].pb(z);
for(int j = 1; j <= t; ++ j) {
scanf("%d", &z);
sum |= (1 << (z - 1));
}
w[x].pb(sum), w[y].pb(sum);
}
}
void SPFA() {
for(int i = 1; i <= n; ++ i)
for(int j = 0; j < (1 << p); ++ j)
dis[i][j] = 1e9 + 9, vis[i][j] = 0;
queue<int> q1, q2; q1.push(1), q2.push(val[1]);
dis[1][val[1]] = 0, vis[1][val[1]] = 1;
while(!q1.empty()) {
int x1 = q1.front(), x2 = q2.front();
q1.pop(); q2.pop();
for(int j = 0, sz = G[x1].size(); j < sz; ++ j) {
int y1 = G[x1][j], y2 = x2 | val[y1];
if((x2 & w[x1][j]) >= w[x1][j] && dis[y1][y2] > dis[x1][x2] + v[x1][j]) {
dis[y1][y2] = dis[x1][x2] + v[x1][j];
if(!vis[y1][y2]) {
q1.push(vis[y1][y2] = y1);
q2.push(y2);
}
}
}
vis[x1][x2] = 0;
}
for(int j = 0; j < (1 << p); ++ j)
ans = min(ans, dis[n][j]);
printf("%d\n", ans == 1e9 + 9 ? -1 : ans);
}
int main() {
#ifndef ONLINE_JUDGE
freopen("dalao.in", "r", stdin);
freopen("dalao.out", "w", stdout);
#endif
Init();
SPFA();
return 0;
}
T2
神奇的贪心,正解做法太神仙了,反正贪心也是对的
首先把房间按
从大到小排序,把
放进一个
里,遍历房间,先确定房间的最大匹配数
,并把能匹配的房间取出来,然后把
最大的
个数取出来,每次尝试把最大的
和最大的
加入答案,可以发现
时答案会更优,这样子就做完了。
正确性的话,由匹配的方式可知最后房间的序列的每一个前缀都一定能对应上
的一个匹配,那么直接取
最大的几个一定不会更劣,由于每一个前缀都有对应,直接枚举前缀长度就好了,复杂度
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5 + 10;
int n, m, a[N], cnt;
struct node {
int b, c, val, fuck;
}A[N], tmp[N];
bool cmp(node x, node y) {
if(x.val == y.val) return x.b > y.b;
return x.val > y.val;
}
multiset<int> S;
multiset<int>::iterator it;
signed main() {
#ifndef ONLINE_JUDGE
freopen("winner.in", "r", stdin);
freopen("winner.out", "w", stdout);
#endif
int ans = 0;
scanf("%lld%lld", &n, &m);
for(int i = 1; i <= n; ++ i) {
scanf("%lld", &a[i]);
S.insert(a[i]);
}
sort(a + 1, a + n + 1);
for(int i = 1; i <= m; ++ i) {
scanf("%lld%lld", &A[i].b, &A[i].c);
A[i].val = A[i].c - A[i].b;
}
sort(A + 1, A + m + 1, cmp);
for(int i = 1; i <= m; ++ i) {
it = S.upper_bound(A[i].b);
if(it == S.end()) continue;
tmp[++ cnt] = A[i]; S.erase(it);
}
//cerr << cnt << endl;
for(int i = 1; i <= cnt; ++ i)
if(tmp[i].val + a[n - i + 1] > 0)
ans += tmp[i].val + a[n - i + 1];
cout << ans << endl;
return 0;
}
T3
这个构造题其实还是不难的,只是考试的时候自己没有耐心去想.
并且数据范围还提示了
时要特判
首先如果
我们可以很容易构造出某一项为
首先
时
即可
然后
时,构造出
, 即选择
同理
时可以构造出
, 即选择
时
我们令
那么可以得到一个方程
转换一下就是
求解即可
时间复杂度
#include<bits/stdc++.h>
#define int long long
using namespace std;
int read() {
int _ = 0, ___ = 1; char __ = getchar();
for(; !isdigit(__); __ = getchar()) if(__ == '-') ___ = -1;
for(; isdigit(__); __ = getchar()) _ = (_ << 3) + (_ << 1) + (__ ^ 48);
return _ * ___;
}
void exgcd(int a, int b, int &x, int &y) {
if(!b) {
x = 1, y = 0; return;
}
exgcd(b, a % b, x, y);
int xx = y, yy = x - a / b * y;
x = xx, y = yy;;
}
int T, m, a, b, c;
int qpow(int a, int x) {
int ret = 1;
while(x) {
if(x & 1) (ret *= a) %= m;
x >>= 1, (a *= a) %= m;
}
return ret;
}
signed main() {
#ifndef ONLINE_JUDGE
freopen("1479.in", "r", stdin);
freopen("1479.out", "w", stdout);
#endif
for(T = read(); T -- ; ) {
m = read(), a = read(), b = read(), c = read();
int k = log2(m);
if((1ll << k) == m) {
if(a == 1 && b == 1 && c == 1) printf("1 1 2\n");
else if(a > 1) printf("%lld 1 1\n", m / 2);
else if(b > 1) printf("1 %lld 1\n", m / 2);
else if(c > 1) printf("%lld %lld %lld\n", m / 2, m / 2, m / 2);
}
else {
int x, y; exgcd(c, -a * b, x, y);
while(x < 0 || y < 0) x += a * b, y += c;
printf("%lld %lld %lld\n", qpow(2, y * b), qpow(2, y * a), qpow(2, x));
}
}
return 0;
}