20210812 数列,数对,最小距离,真相
考场
T1 一眼扩欧,前段时间刚复习过
T2 感觉很可做,但想了很久也不会
T3 画了画图发现可以多源次短路
T4 感觉很可做,想到了根据 $
分段,细节没想
开 T1,写完过不了大样例,看了半天也没看出来。回头看草稿纸上的式子,发现少乘了一项,改完又拍出一组错,发现 \(|x|=|x-k_1|\) 相等是 WA 了,顺便改了改暴力,保证它求出的一定正确(while
+assert
)。然后发现拍得太慢,调小数据拍了 \(10^4\) 组就改回去了,又调大了。此时 8.30,感觉很稳
T2 想起以前考过的 一道题,但这题顺序不确定,尝试了各种排序都过不了大样例,最后 shuffle
走人
T3 写的很顺利,一发过了大样例,拍上就去上厕所了,回来发现挂了???很慌,害怕假了,改小数据拍出来发现有个地方没判起点是否相同,然后就过拍了。调数据的时候不知道为什么,暴力要么不到 1s 就跑完了,要么就 10s+,最后选了大数据
9.50 开 T4,此时心态还比较稳健。写着写着发现有个地方不会处理,只能 \(O(n^2)\) 暴力背包,先跳过了。写完其他部分还是不会。。。得分退化到 65。大概 10.50 过了小样例,大样例,我大样例呢???重下了一边下发文件,发现只有这题没大样例,搞人心态呀。于是手造数据开始调,直接调到 11.10,先把其他 3 题交了,想着保 30 把,开始大力特判,最后胡乱交了
res
rk2 100+10+100+0
T2 乱搞一分没有
T4 改数组时没改全,RE 了
rk1 杨哲灏 100+0+100+30
rk2 杨宸骁 10+100+0+100
总结
前段时间每天早晨学数学挺有用的,这次的扩欧推的很顺利
T3 是 dky 讲过的一道原题:《[GXOI/GZOI2019]旅行者》,但考场上一点没想起来,ycx 好歹还记得二进制分组的做法,而 wcr zjj 都写了另一种
数列
方程 \(ax+by=c,a\le b\) 的 \(|x|+|y|\) 最小解在 \(x\) 取最小正值或最大负值时取到
考场代码
int n;
LL a,b,c;
LL d,xx,yy,k1,k2,ans;
LL exgcd(LL a,LL b) {
if( !b ) { xx = 1, yy = 0; return a; }
LL d = exgcd(b,a%b);
LL z = xx; xx = yy, yy = z - a / b * yy;
return d;
}
signed main() {
// freopen("a.in","r",stdin);
// freopen("a.out","w",stdout);
read(n,a,b);
if( a > b ) swap(a,b);
d = exgcd(a,b), k1 = b / d, k2 = a / d;
For(i,1,n) {
read(c); c = -c;
if( c % d ) { puts("-1"); return 0; }
LL x = (c/d*xx % k1+k1)%k1, y = (c-a*x)/b;
if( abs(x-k1)+abs(y+k2) < abs(x)+abs(y) ) x -= k1, y += k2;
ans += abs(x)+abs(y);
}
write(ans);
return iocl();
}
/*
1 455 824
412
*/
数对
考虑同时选两对数 \((a_i,b_i),(a_j,b_j)\),如果 \(a_i\le b_j,a_j\ge b_i\),那么 \(i\) 一定在 \(j\) 前面,反之亦然。那么按 \(a+b\) 排序后 DP 即可
code
const int N = 1e5+5;
int n;
struct Node { int a,b,w; } a[N];
int mx,lsh[N*2];
LL ans;
#define ls (u<<1)
#define rs (u<<1|1)
namespace seg {
struct Node { int l,r; LL mx,add; } t[N*8];
void up(int u) { t[u].mx = max(t[ls].mx,t[rs].mx); }
void down(int u,LL x) { t[u].mx += x, t[u].add += x; }
void down(int u) { down(ls,t[u].add), down(rs,t[u].add), t[u].add = 0; }
void build(int u,int l,int r) {
t[u] = Node{l,r,0,0};
if( l == r ) return;
int mid = l+r>>1;
build(ls,l,mid), build(rs,mid+1,r);
}
void modify(int u,int p,LL x) {
if( t[u].l == t[u].r ) { ckmax(t[u].mx,x); return; }
down(u);
modify( p<=t[ls].r?ls:rs ,p,x);
up(u);
}
void add(int u,int l,int r,LL x) {
if( l <= t[u].l && t[u].r <= r ) { down(u,x); return; }
down(u);
if( l <= t[ls].r ) add(ls,l,r,x);
if( t[rs].l <= r ) add(rs,l,r,x);
up(u);
}
LL query(int u,int l,int r) {
if( l <= t[u].l && t[u].r <= r ) return t[u].mx;
down(u);
LL res = 0;
if( l <= t[ls].r ) res = query(ls,l,r);
if( t[rs].l <= r ) ckmax(res,query(rs,l,r));
return res;
}
}
#undef ls
#undef rs
signed main() {
read(n);
For(i,1,n) read(a[i].a,a[i].b,a[i].w), lsh[++mx] = a[i].a, lsh[++mx] = a[i].b;
sort(lsh+1,lsh+mx+1), mx = unique(lsh+1,lsh+mx+1)-lsh-1;
For(i,1,n) a[i].a = lower_bound(lsh+1,lsh+mx+1,a[i].a)-lsh,
a[i].b = lower_bound(lsh+1,lsh+mx+1,a[i].b)-lsh;
sort(a+1,a+n+1,[](const Node &x,const Node &y){return x.a+x.b<y.a+y.b;});
seg::build(1,1,mx);
For(i,1,n) {
seg::modify(1,a[i].a,seg::query(1,1,min(a[i].a,a[i].b))+a[i].w);
if( a[i].a < a[i].b ) seg::add(1,a[i].a+1,a[i].b,a[i].w);
}
write(seg::t[1].mx);
return iocl();
}
最小距离
从特殊点开始一起跑次短路,限制每个点的最短路、次短路必须由不同的特殊点开始
考场代码
typedef pair<LL,int> PLI;
const int N = 2e5+5;
int n,m,p,id[N],mm=1,head[N],to[N*2],w[N*2],nxt[N*2];
PLI dis[N],dis2[N];
struct Node { int id; LL dis,dis2; };
bool operator < (const Node &x,const Node &y)
{ return x.dis!=y.dis ? x.dis>y.dis : x.dis2>y.dis2; }
priority_queue<Node> pq;
signed main() {
// freopen("c.in","r",stdin);
// freopen("c.out","w",stdout);
read(n,m,p);
For(i,1,p) read(id[i]);
For(i,1,m) {
int x,y,z; read(x,y,z);
to[++mm] = y, w[mm] = z, nxt[mm] = head[x], head[x] = mm;
to[++mm] = x, w[mm] = z, nxt[mm] = head[y], head[y] = mm;
}
mem(dis,0x3f,n), mem(dis2,0x3f,n);
For(i,1,p) dis[id[i]] = MP(0,id[i]), pq.push(Node{id[i],0,dis2[id[i]].fi});
while( !pq.empty() ) {
Node now = pq.top(); pq.pop();
int u = now.id;
if( dis[u].fi < now.dis || (dis[u].fi==now.dis && dis2[u].fi<now.dis2) )
continue;
// printf("@ %d %lld %lld\n",u,dis[u].fi,dis2[u].fi);
for(int i = head[u], v; v = to[i], i; i = nxt[i]) {
bool flg = 0;
if( dis[u].fi+w[i] < dis[v].fi ) {
if( dis[u].se != dis[v].se ) dis2[v] = dis[v];
dis[v].fi = dis[u].fi+w[i], dis[v].se = dis[u].se, flg = 1;
} else if( dis[u].se != dis[v].se && dis[u].fi+w[i] < dis2[v].fi )
dis2[v].fi = dis[u].fi+w[i], dis2[v].se = dis[u].se, flg = 1;
else if( dis2[u].se != dis[v].se && dis2[u].fi+w[i] < dis2[v].fi )
dis2[v].fi = dis2[u].fi+w[i], dis2[v].se = dis2[u].se, flg = 1;
if( flg ) pq.push(Node{v,dis[v].fi,dis2[v].fi});
}
}
For(i,1,p) write(dis2[id[i]].fi,' ');
return iocl();
}
真相
考场上想的就是正解。。。
考虑以 $
分段,对于每一段如果开始的点真/假确定了,整段真话的数量和最后 $
的真假都确定了。枚举每个不同的 $
,那么所有值和它相同的必须为真,不同的必须为假,判断一下真话数量是否与该值相等即可。时间复杂度 \(O(n)\)
code
char readc() {
char c=getchar();
while(c!='+'&&c!='-'&&c!='$')c=getchar();
return c;
}
#define yes() puts("consistent")
#define no() puts("inconsistent")
const int N = 2e6+5;
int T,n,a[N];
int m,sum0,pos[N],cnt[N][2],sum[N][2];
bool b[N];
vector<int> num[N];
int calc(int l,int r,bool k) {
int res = k;
b[l] = k;
For(i,l,r) b[i+1] = !(b[i] ^ a[i]), res += b[i+1];
return res;
}
namespace sub{
bool main() {
if( m ) return 0;
calc(1,n,0);
if( b[1] == b[n+1] ) { yes(); return 1; }
calc(1,n,1);
if( b[1] == b[n+1] ) { yes(); return 1; }
no();
return 1;
}
}
void solve() {
read(n);
For(i,1,n) {
char op = readc();
if( op == '+' ) a[i] = 1;
else if( op == '-' ) a[i] = 0;
else read(a[i]), pos[++m] = i;
a[i+n] = a[i];
}
if( sub::main() ) return;
pos[0] = pos[m];
For(i,1,m) {
int j = pos[i], l = pos[i-1]+1, r = j-1+(i==1)*n, x;
if( l > r ) cnt[i][0] = 0, cnt[i][1] = 1;
else x = calc(l,r,0), cnt[i][b[r+1]] = x,
x = calc(l,r,1), cnt[i][b[r+1]] = x;
num[a[j]].pb(i), sum[a[j]][0] += cnt[i][0], sum[a[j]][1] += cnt[i][1];
sum0 += cnt[i][0];
}
bool flg = 1;
For(i,1,m) if( sum0 == a[pos[i]] ) flg = 0;
if( flg ) { yes(); return; }
For(i,0,n) if( num[i].size() && i == sum0-sum[i][0]+sum[i][1] )
{ yes(); return; }
no();
}
signed main() {
read(T);
while( T-- ) {
m = sum0 = 0;
For(i,1,n) num[i].clear(), sum[i][0] = sum[i][1] = 0;
solve();
}
return 0;
}