暑假集训3
去年暑假打过一次,但是当时太菜,今天看到之前写过,好奇多少分,考后交了一发,发现自己是真的菜
然后,就算开了个坑吧,四道题。。。
A. 数列
\(exgcd\)板子
然后,\(exgcd\)咋用来着?
滚回去学数论基础了
code
#include <cstdio>
using namespace std;
#define int long long
int min(int x, int y) { return x < y ? x : y; }
int max(int x, int y){return x > y ? x : y;}
int abs(int x) { return x < 0 ? -x : x; }
void swap(int &x, int &y){x ^= y; y ^= x; x ^= y;}
int exgcd(int a, int b, int &x, int &y){
if(b == 0){
x = 1; y = 0;
return a;
}
int gcd = exgcd(b, a % b, y, x);
y = y - a / b * x;
return gcd;
}
signed main(){
int n, a, b;
scanf("%lld%lld%lld", &n, &a, &b);
int x, y; if(a > b)swap(a, b);
int gcd = exgcd(a, b, x, y);
b = b / gcd; a = a / gcd;
x = (x % b + b) % b;
int ans = 0;
bool flag = 1;
for (int i = 1; i <= n; ++i){
int now; scanf("%lld",&now); now = abs(now);
if(now % gcd)flag = 0;
if(flag){
now = now / gcd;
int nx = (x * now % b + b) % b;
int ny = (now - a * nx) / b;
ans += min(abs(nx) + abs(ny), abs(nx - b) + abs(ny + a));
}
}
if(flag)printf("%lld\n", ans);
else printf("-1\n");
return 0;
}
B.数对
$a_i >b_j $ 且 $ b_i > a_j $
\(a_i + b_i >a_j + b_j\)
所以按照\(a+b\)排序,然后线段树优化
对\(a+b\)相同的\(a\)小的在前
code
#include<bits/stdc++.h>
using namespace std;
const int maxn = 200005;
typedef long long ll;
int n;
struct note{
ll a, b, w;
bool operator < (const note & x)const{
return a + b == x.a + x.b ? a < x.a : a + b < x.a + x.b;
}
}d[maxn];
struct tree{
ll t[maxn << 2 | 1], add[maxn << 2 | 1];
void push_up(int x){t[x] = max(t[x << 1], t[x << 1 | 1]);}
void push_down(int x){
int ls = x << 1, rs = x << 1 | 1;
t[ls] += add[x];
t[rs] += add[x];
add[ls] += add[x];
add[rs] += add[x];
add[x] = 0;
}
void modify(int x, int l, int r, int pos, ll val){
if(l == r)return t[x] = max(val, t[x]), void();
int mid = (l + r) >> 1;
if(add[x])push_down(x);
if(pos <= mid)modify(x << 1, l, mid, pos, val);
else modify(x << 1 | 1, mid + 1, r, pos, val);
push_up(x);
}
void modify_add(int x, int l, int r, int L, int R, ll val){
if(L <= l && r <= R){
t[x] += val;
add[x] += val;
return;
}
if(add[x])push_down(x);
int mid = (l + r) >> 1;
if(L <= mid)modify_add(x << 1, l, mid, L, R, val);
if(R > mid)modify_add(x << 1 | 1, mid + 1, r, L, R, val);
push_up(x);
}
ll query(int x, int l, int r, int L, int R){
if(L <= l && r <= R)return t[x];
int mid = (l + r) >> 1;
if(add[x])push_down(x);
ll ans = 0;
if(L <= mid)ans = max(ans, query(x << 1, l, mid, L, R));
if(R > mid)ans = max(ans, query(x << 1 | 1, mid + 1, r, L, R));
return ans;
}
}t;
int lsh[maxn << 1 | 1], cnt;
int main(){
scanf("%d",&n);
for(int i = 1; i <= n; ++i)scanf("%lld%lld%lld",&d[i].a, &d[i].b, &d[i].w);
for(int i = 1; i <= n; ++i)lsh[++cnt] = d[i].a, lsh[++cnt] = d[i].b;
sort(lsh + 1, lsh + cnt + 1); cnt = unique(lsh + 1, lsh + cnt + 1) - lsh - 1;
for(int i = 1; i <= n; ++i)d[i].a = lower_bound(lsh + 1, lsh + cnt + 1, d[i].a) - lsh;
for(int i = 1; i <= n; ++i)d[i].b = lower_bound(lsh + 1, lsh + cnt + 1, d[i].b) - lsh;
sort(d + 1, d + n + 1);
for(int i = 1; i <= n; ++i){
if(d[i].a < d[i].b)t.modify_add(1, 1, cnt, d[i].a + 1, d[i].b, d[i].w);
t.modify(1, 1, cnt, d[i].a, d[i].w + t.query(1, 1, cnt, 1, min(d[i].a, d[i].b)));
}
printf("%lld\n",t.query(1, 1, cnt, 1, cnt));
return 0;
}
C. 最小距离
多源最短路,记录由哪个特殊点更新,枚举边更新链接的两个点的记录的特殊点的答案
code
#include<bits/stdc++.h>
using namespace std;
const int maxn = 200005;
typedef long long ll;
typedef pair<ll, int> pli;
struct edge{int to, net, val;}e[maxn << 3 | 1];
int tot, head[maxn];
void add(int u, int v, int w){
e[++tot].net = head[u];
head[u] = tot;
e[tot].to = v;
e[tot].val = w;
}
ll dis[maxn],ans[maxn];
bool vis[maxn];
int n, m, p, rem[maxn], ts[maxn];
priority_queue<pli, vector<pli>, greater<pli> >q;
int main(){
scanf("%d%d%d", &n, &m, &p);
for (int i = 1; i <= p; ++i) scanf("%d", &ts[i]);
for (int i = 1; i <= m; ++i){
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
add(u, v, w); add(v, u, w);
}
memset(ans, 0x3f, sizeof(ans));
memset(dis, 0x3f, sizeof(dis));
for(int i = 1; i <= p; ++i) q.push(pli(0, ts[i])), dis[ts[i]] = 0, rem[ts[i]] = ts[i];
while(!q.empty()){
int x = q.top().second; q.pop();
if(vis[x])continue; vis[x] = 1;
for(int i = head[x]; i; i = e[i].net){
int v = e[i].to;
if(dis[v] > dis[x] + e[i].val){
dis[v] = dis[x] + e[i].val;
rem[v] = rem[x];
q.push(pli(dis[v], v));
}
}
}
// for(int i = 1; i <= n; ++i)printf("%d ",dis[i]);
for(int i = 1; i <= m; ++i){
int u = e[i + i].to, v = e[i + i - 1].to;
// printf("%d %d %d %d\n",u, v, rem[u], rem[v]);
if(rem[u] != rem[v]){
ll d = dis[u] + dis[v] + e[i + i].val;
ans[rem[u]] = min(ans[rem[u]], d);
ans[rem[v]] = min(ans[rem[v]], d);
}
}
for(int i = 1; i <= p; ++i)printf("%lld ", ans[ts[i]]);
return 0;
}
D.真相
感谢\(KafuuChinocpp\)的题解
考虑某人说\(+、-\)那么他后面的人说话的真假就确定了,所以没有\(\S\)可以直接扫一遍看是否矛盾
有\(\$\)的,如果他的真假性确定,那么前面有多少人说真话/假话就确定了,开桶维护有\(i\)个人说真话的断言为真时有多少人说真话,使用差分进行优化
code
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
const int maxn = 1e5 + 55;
int tot, n;
char c[maxn];
int num[maxn];
void check(){
bool flag = true;
for(int i = 1; i <= n; ++i)if(c[i] == '-')flag = 1 - flag;
if(flag){printf("consistent\n");return;}
flag = false;
for(int i = 1; i <= n; ++i)if(c[i] == '-')flag = 1 - flag;
if(!flag){printf("consistent\n");return;}
printf("inconsistent\n");return;
}
int cf[maxn], sum[maxn];
int las(int x){return x == 1 ? n : x - 1;}
void work(){
for(int i = 0; i <= n; ++i)cf[i] = 0;
for(int i = 0; i <= n; ++i)sum[i] = 0;
int l, r;
for(int i = 1; i <= n; ++i)if(c[i] == '$'){l = r = i; break;}
do{
int pos = l;
int ctrue = 1, cfalse = 0; bool now = 1;
while(c[las(l)] != '$'){
l = las(l);
if(c[l] == '-')now = 1 - now;
ctrue += now; cfalse += 1 - now;
}
sum[num[pos]] += ctrue;
cf[0] += cfalse;
cf[num[pos]] -= cfalse;
cf[num[pos] + 1] += cfalse;
l = las(l);
}while(l != r);
sum[0] += cf[0];
for(int i = 1; i <= n; ++i){
cf[i] += cf[i - 1];
sum[i] += cf[i];
}
for(int i = 0; i <= n; ++i)if(sum[i] == i){printf("consistent\n");return;}
printf("inconsistent\n");
}
int main(){
int t; scanf("%d",&t);
for(int ask = 1; ask <= t; ++ask){
tot = 0; scanf("%d",&n);
for(int i = 1; i <= n; ++i){
scanf(" %c",&c[i]);
if(c[i] == '$')++tot, scanf("%d",&num[i]);
}
if(tot)work();
else check();
}
return 0;
}