OI 笑传 #3
赛时
开题先去看了大样例,发现是全活的。
wc 哥们 T1 怎么是原,写写过样例了。
T2 转化下就变成了维护类似一棵树上不同的点个数,先写了个暴力。
wc T3 怎么似曾相识,先把 \(k\le 9\) 的暴力写了。
T4 这函数是个啥?
哦哥们原来函数只有 \(4\) 种取值,那我会 \(O(n)\) 算 \(f\)
于是先把 \(n\le 20\) 写了。
哦我草哥们一遍过样例。
感觉优化点在找前缀相同那一块,可能要上一些神秘 Trie 东西?
不管了现在暴力都写完了已经超越了之前所有的比赛。
此时还有 2.5h。
回头看 T2。
哦哥们原来我维护的是条链,改改就过样例了。
然后开始想 T3,发现旁边老哥也在想 T3。
我草这个差距为 \(t\) 也太恶心了。
当然考虑可能有些 \(a_i\) 可以被多次变换下代替成更小的,问题是这些数可以选比自己更大的,那这样的话有些变换需要的数是不存在的。
哦我会 \(m=0\) 了。
哦哥们我不会。
草看来又有神秘但神仙转化方法我没想到,认菜了。
旁边老哥好像有做法但是被大样例创飞了。
怎么莫名感觉 T4 可以根号分治???
但是这个求和也太恶心了。
还有 1h 了于是摆烂。去写了 OI 笑传 3
好吧这场没有想出任何有价值的东西,不挂分就是赢。
看来又输了哥们。
cnm T2 改正解没改 N,数组开小了。
为啥 T3 RE?谁把我 const 删了?
发现旁边老哥写了神秘搜索剪枝过了 T3 并成为最优解 /bx
upd: 被 hack 了,原题数据太弱了。
T4 是神秘串,T1 T2 水,T3 还是很好的题。
T3
第一个想法是如果 \(m=0\),那么某些 \(a_t\) 可以被松弛成更小的东西。
问题是这些数一定存在吗?松弛的点可以是更大的 \(t\),也可以是更小的 \(t\)。
观察下大样例发现不一定存在的,判断这些数是个难题。
拆点或者建源可以吗?不是这到每个数的状态都不一样怎么建点。
于是场上就不会了wwwwww。以下是正解。
又是和二进制位有关的东西,考虑从每个点开始枚举转移不同的位置数。
也就是说,我们把对于任意数变有 \(t\) 个二进制位不同这个东西拆出来,一步步变。
基于此我们定义一个状态:设 \(f_{x,i,j}\) 表示在一次变数的过程中,变了前 \(i\) 位,与原数不同的位置有 \(j\) 个,变成的数为 \(x\) 的最小距离。
你会发现这样的状态可以极大的压缩原图中的那两两个数之间的各种各样的情况。
对于所有直接连接的边 \((u,v,w)\),在 \(f_{u,0,0},f_{v,0,0}\) 之间连边权 \(w\) 的双向边。
如果决策不变这一位,转移是这样:\(f_{x,i,j} \rightarrow f_{x,i+1,j}\)(\(\rightarrow\) 表示赋值)。
如果决策变这一位,可以用异或描述变的过程,转移是这样:\(f_{x,i,j} \rightarrow f_{x \operatorname{xor} 2^i,i+1,j+1}\)。
当考虑完 \(i=k\) 位后,我们可以即时中止转移并将距离作为到 \(x\) 的一个可能距离,也就是:\(f_{x,i=k,j} +a_j \rightarrow f_{x,0,0}\)
但是你会发现这样一个点就很难表示了,我当然知道你有一个想法就是把这些点放进 map 里去,但想想就觉得这也太恶心了吧兄弟。
而且这样建出来的点和边数都是 \(O(nk^2)\) 的,再塞个 \(\log\) 会爆的啊。
此时要有一个很重要的观察,原图中转移 1,2 建出来的边全是 \(0\) 权边。
这东西在 Dijkstra 看来是很唐的,因为 \(0\) 权的点一定会在下一轮立刻被选中开始松弛。
而且在我们建出的图中这些 \(0\) 权边还都是连在一起的,这让我们可以干什么?
我们当然可以像 Bfs 一样一次性全把这些点松弛了,不让他们进优先队列。
而且这样还有个好处,你会发现如果我们可以吧所有连着的点一次性处理,这些连着的点都有个特点就是 \(i,j\ne 0\)。
也就是说,对于所有 \(i,j\ne 0\) 的状态,我们根本不需要在优先队列里记录,只需要记录 \(i=j=0\) 的状态,因此,我们可以只记录 \(x\)。
这也太爽了,本题于是就做完了。
code,但是原题有亿点卡常。
T3
#define psb push_back
#define mkp make_pair
#define ls p<<1
#define rs (p<<1)+1
#define rep(i,a,b) for( int i=(a); i<=(b); ++i)
#define per(i,a,b) for( int i=(a); i>=(b); --i)
#define rd read()
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll read(){
ll x=0,f=1;
char c=getchar();
while(c>'9'||c<'0'){if(c=='-') f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
const int N=131099;
int f[N][18][18];
struct ed{
int x;int i;int j;
int v;
int w;
bool operator<(const ed &x) const{
return x.w<w;
}
};
vector<ed> e[N];
int a[30];
int dist[N];
bitset<N> vis;
priority_queue<ed> q;
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
int k,m,s;cin>>k>>m>>s;
int n=(1<<k);
for(int i=1;i<=k;i++){
cin>>a[i];
}
for(int i=1;i<=m;i++){
int u,v,w;cin>>u>>v>>w;
e[u].push_back(ed{v,0,0,v,w});
e[v].push_back(ed{u,0,0,u,w});
}
memset(dist,0x7f,sizeof dist);
memset(f,0x7f,sizeof f);
dist[s]=0;
q.push(ed{s,0,0,s,0});
while(q.size()){
ed tc=q.top();q.pop();
int u=tc.v;
ll w=tc.w;
if(vis[u])continue;
vis[u]=1;dist[u]=w;
for(ed sk:e[u]){
int v=sk.v;ll w=sk.w;
if(dist[v]>dist[u]+w){
dist[v]=dist[u]+w;
q.push(ed{v,0,0,v,dist[v]});
}
}
f[u][0][0]=dist[u];
queue<ed> fq;
fq.push(tc);
while(fq.size()){
ed cs=fq.front();fq.pop();
if(cs.i<k){
if(cs.i+1<=k&&cs.j+1<=k&&f[cs.x^(1<<cs.i)][cs.i+1][cs.j+1]>f[cs.x][cs.i][cs.j]){
f[cs.x^(1<<cs.i)][cs.i+1][cs.j+1]=f[cs.x][cs.i][cs.j];
fq.push(ed{cs.x^(1<<cs.i),cs.i+1,cs.j+1,0,0});
}
if(cs.i+1<=k&&f[cs.x][cs.i+1][cs.j]>f[cs.x][cs.i][cs.j]){
f[cs.x][cs.i+1][cs.j]=f[cs.x][cs.i][cs.j];
fq.push(ed{cs.x,cs.i+1,cs.j,0,0});
}
}
else if(cs.i==k&&dist[cs.x]>dist[u]+a[cs.j]){
dist[cs.x]=dist[u]+a[cs.j];
q.push(ed{cs.x,0,0,cs.x,dist[cs.x]});
}
}
}
for(int i=0;i<n;i++){
cout<<dist[i]<<' ';
}
return 0;
}
闲话
今晚上吃完饭终于去逛了逛校园,好像已经十天没出过这栋楼了?哥们怎么能忍住的。
晚上体育活动去打羽,四年没打了,哥们菜成啥了。
回来科研了下虚拟机科技,装 noilinux 结果没装完到回宿舍时间于是断电了。
我草这不是似定了,我电脑的神秘电池随时可以似的。
于是只能把虚拟机关掉了,我想这下还得重装了。
结果回宿舍打开一看我草竟然能从关掉的地方直接继续安装进度,这是怎么实现的有点神仙啊。

浙公网安备 33010602011771号