签到。输出A&B,当(A&B == 0)时特判。
因为给的数字是1-n的全排列,对于操作2,可以转换为查询区间[r+1,n]里面第一个大于等于k的数字。(考虑到该区间可能没有大于等于k的数字,可以在n+1位置插入数字n+1,就一定能查询到了。所以每次查询区间[r+1,n+1]第一个大于等于k的数字。)对于操作1,可以将加了1e7的数字放在一个set里面,无论查询的r给的是多少,答案都可以从set里面的数字更新。(即lower_bound找到第一个大于等于k的数字)
这样就只剩下一个问题了,如何查询区间第一个大于等于k的数字。可以考虑主席树。因为对于操作1只需要把更新的数字放入set所以就不需要更新主席树。每次操作2,如果k小于等于左子树最大值,则递归左子树。找到答案则直接return,否则递归右子树(这时候右子树的所有值都大于k)。
ps:这题用cin关同步居然没用,导致我一度怀疑模板有问题。
具体看代码:
#include
using namespace std;
#define sz(a) a.size()
#define all(x) (x).begin(), (x).end()
#define foo(i, s, e) for(int i=(s);i<=(e);i++)
#define fod(i, s, e) for(int i=(e);i>=(s);i--)
#define endl '\n'
#define debug(a) cout<<#a<<": "<
#define mod(x) (((x)%MOD+MOD)%MOD)
#define mem(a) memset((a),0,sizeof(a))
typedef long long ll;
typedef unsigned long long ull;
const int INF = 0x3f3f3f3f;
const int MOD = 1000000007;
const int maxn = 2e5 + 7;
int a[maxn], b[maxn], T[maxn];
int len = 0, tot;
struct node {
int L, R;
int sum;
} tree[maxn * 30];
int update(int root, int x, int l, int r) {
int newroot = ++tot;
tree[newroot] = tree[root]; tree[newroot].sum++;
if(l == r) return newroot; int mid = (l + r) / 2;
if(x <= mid) tree[newroot].L = update(tree[root].L, x, l, mid);
else tree[newroot].R = update(tree[root].R, x, mid + 1, r);
return newroot;
}
int sear(int left_root, int right_root, int l, int r, int k) {
if(tree[right_root].sum == tree[left_root].sum) return INF;
if(l == r) return l;
int ans = INF; int mid = (l + r) / 2;
if(k <= mid)
ans = sear(tree[left_root].L, tree[right_root].L, l, mid, k);
if(ans == INF)
ans = sear(tree[left_root].R, tree[right_root].R, mid + 1, r, k);
return ans;
}
int main() {
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
// ios::sync_with_stdio(false);
int Tx; scanf("%d", &Tx);
while(Tx--) {
tot = 0; int n, m; scanf("%d%d", &n, &m);
set<int> se; se.insert(n + 1);
for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
len = n + 1; T[0] = 0; tree[0].sum = tree[0].L = tree[0].R = 0;
for(int i = 1; i <= n; i++)
T[i] = update(T[i - 1], a[i], 1, len);
T[n + 1] = update(T[n], n + 1, 1, len);
int ans = 0;
while(m--) {
int op; scanf("%d", &op);
if(op == 1) {
int x; scanf("%d", &x);
x ^= ans;
se.insert(a[x]);
} else {
int r, k; scanf("%d%d", &r, &k);
r ^= ans, k ^= ans;
ans = *se.lower_bound(k);
ans = min(ans, sear(T[r], T[n + 1], 1, n + 1, k));
printf("%d\n", ans);
}
}
}
return 0;
}
直接优先队列+bfs,优先队列按照路的权值从小到大排序,每次取出头就是当前最小权值的路。
问题在于如何拓展才能不超时超内存。首先将初始化每个点的出边按照权值排序,将每个点最小权值出路放入优先队列。在bfs时当你取出头,如果将当前点的所有出边全部放入有点队列则肯定会超,考虑到我们只需要当前最短的边,而最短的边只有2种可能:
1.当前路扩展当前点最小的出边
2.当前路的前一个节点x拓展到x的下一条边(按权值大小排序了)。
所有优先队列里面需要保存当前节点,前一个节点,当前的路长度,和最后边在前一个节点的出边的位置。
#include
using namespace std;
#define sz(a) a.size()
#define all(x) (x).begin(), (x).end()
#define foo(i, s, e) for(int i=(s);i<=(e);i++)
#define fod(i, s, e) for(int i=(e);i>=(s);i--)
#define endl '\n'
#define debug(a) cout<<#a<<": "<
#define mod(x) (((x)%MOD+MOD)%MOD)
#define mem(a) memset((a),0,sizeof(a))
typedef long long ll;
typedef unsigned long long ull;
const int INF = 0x3f3f3f3f;
const int MOD = 1000000007;
const int maxn = 2e5 + 7;
struct node {
int x, y;
ll v;
int id;
node(int _x, int _y, ll _v, int _id) {
x = _x, y = _y, v = _v, id = _id;
}
friend bool operator <(node a, node b) {
return a.v > b.v;
}
};
struct path {
ll v; int y;
path(ll V, int Y) {
v = V; y = Y;
}
friend bool operator <(path a, path b) {
return a.v < b.v;
}
};
vector<path> dge[maxn];
ll ans[maxn]; int que[maxn];
priority_queue< node > pq;
int main() {
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
ios::sync_with_stdio(false);
int T; cin >> T;
while(T--) {
int n, m, q; cin >> n >> m >> q;
int x, y; ll v;
for(int i = 1; i <= n; i++)
dge[i].clear();
while(!pq.empty()) pq.pop();
while(m--) {
cin >> x >> y >> v;
dge[x].push_back(path(v, y));
}
int maxk = 0;
for(int i = 1; i <= q; i++) {
cin >> que[i];
maxk = max(maxk, que[i]);
}
for(int i = 1; i <= n; i++) {
if(dge[i].size() >= 2)
sort(all(dge[i]));
}
for(int i = 1; i <= n; i++) {
if(dge[i].size() >= 1)
pq.push(node(i, dge[i][0].y, dge[i][0].v, 0));
}
int cntk = 0;
while(!pq.empty()) {
node now = pq.top(); pq.pop();
ans[++cntk] = now.v;
if(cntk == maxk) break;
if(now.id < dge[now.x].size() - 1)
pq.push(node(now.x, dge[now.x][now.id + 1].y, now.v - dge[now.x][now.id].v + dge[now.x][now.id + 1].v, now.id + 1));
if(dge[now.y].size() >= 1)
pq.push(node(now.y, dge[now.y][0].y, now.v + dge[now.y][0].v, 0));
}
for(int i = 1; i <= q; i++)
cout << ans[que[i]] << endl;
}
return 0;
}
签到,可以用栈,每次放入头部,输出时标记已经输出过的不输出。也可以用链表模拟。
签到题
#include
using namespace std;
#define sz(a) a.size()
#define all(x) (x).begin(), (x).end()
#define foo(i, s, e) for(int i=(s);i<=(e);i++)
#define fod(i, s, e) for(int i=(e);i>=(s);i--)
#define endl '\n'
#define debug(a) cout<<#a<<": "<
#define mod(x) (((x)%MOD+MOD)%MOD)
#define mem(a) memset((a),0,sizeof(a))
typedef long long ll;
typedef unsigned long long ull;
const int INF = 0x3f3f3f3f;
const int MOD = 1000000007;
const int maxn = 2e5 + 7;
ll s[maxn];
vector<ll> ve;
int main() {
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
ios::sync_with_stdio(false);
int T; cin >> T;
while(T--) {
int n; ll k; ve.clear();
cin >> n >> k;
ll sum1 = 0; ll ans = k; ll sum = 0;
for(int i = 1; i <= n; i++) {
cin >> s[i];
sum += s[i];
sum1 += s[i] / k;
if(s[i] % k != 0)
ve.push_back(k - s[i] % k);
}
if(sum1 >= n - 1)
ans += sum;
else {
int y = n - 1 - sum1;
sort(all(ve));
for(int i = 0; i < y; i++)
ans += ve[i];
ans += sum;
}
cout << ans << endl;
}
return 0;
}