被称作遗憾之物 爬满了脊骨 又把控了痛楚 被称作无用之物 修筑了唯一的通路
test30
前两题都 0pts,nbm(?
2-A 飞船制造 (spaceship.cpp)
怎么有傻子没开 c++11 写了 rank 然后 re 惹 /fad
考虑依次枚举 \(s=i+j+k\),计算出 \(s\) 一定的方案数就能确定唯一的 \(s\),方案数计算好像只能考虑背包。然后依次枚举 \(i\),计算出 \(s,i\) 一定时 \((j,k)\) 的方案数就能确定唯一的 \(i\),方案数可以考虑 \(j\) 的上下界。然后依次枚举 \(j\),不断增加排名直到符合题目要求即可。
#include<bits/stdc++.h>
#define int long long
#define up(i,l,r) for(int i=l; i<=r; ++i)
#define dn(i,r,l) for(int i=r; i>=l; --i)
using namespace std;
const int N=3000005;
int n, rk, f[3][N];
signed main() {
// freopen("1.txt","r",stdin);
freopen("spaceship.in","r",stdin);
freopen("spaceship.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n >> rk;
up(i,1,3*n) f[0][i]=f[0][i-1]+(i<=n);
up(i,1,3*n) f[1][i]=f[0][i-1]-(i-n-1>=0?f[0][i-n-1]:0);
up(i,1,3*n) f[1][i]+=f[1][i-1];
up(i,1,3*n) f[2][i]=f[1][i-1]-(i-n-1>=0?f[1][i-n-1]:0);
int counter=0;
up(s,3,3*n) {
int val=f[2][s];
if(counter+val>=rk) {
up(i,1,n) {
int l=max(1ll,s-i-n);
int r=min(n,s-i-1);
if(l<=r) {
if(counter+r-l+1>=rk) {
up(j,1,n) {
if(1<=s-i-j&&s-i-j<=n) {
if(++counter==rk) {
cout << i << ' ' << j << ' ' << s-i-j << '\n';
return 0;
}
}
}
}
counter+=r-l+1;
}
}
}
counter+=val;
}
return 0;
}
2-B 机器人 (robot.cpp)
怎么有人下发错误的 statement,谋害本可使爆零,以及这个原题“阻碍关系具有传递性”想何出什么样的意味(?
发现 \(n\leq 1000\),努力去想简单暴力的办法,考虑所有的可能的阻碍对 \((i,j,t)\) 表示 \(i\) 可能在 \(t\) 时刻阻碍 \(j\),然后去顺次考虑每一个点对生不生效。因为题目给了一些互不相等所以不用去纠边角情况。发现只要动态维护 \(u\) 有没有被阻碍、以及被阻碍到哪个地方就能判断生效情况以及更新阻碍情况,不尽之处皆是一些简单分讨(?
#include<bits/stdc++.h>
#define int long long
#define up(i,l,r) for(int i=l; i<=r; ++i)
#define dn(i,r,l) for(int i=r; i>=l; --i)
#define pb push_back
using namespace std;
const int N=1005, M=1000005;
int n, m, x[N], y[N], tag[N], in[N], Ans[N];
vector<int> to[N];
char opt[N];
struct node {
int i, j, t;
bool operator<(const node &rhs) const { return t<rhs.t; }
} p[M];
void dfs(int x) {
for(int y:to[x]) dfs(y), Ans[x]+=Ans[y]+1;
}
signed main() {
freopen("robot.in","r",stdin);
freopen("robot.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n;
up(i,1,n) cin >> opt[i] >> x[i] >> y[i];
up(i,1,n) {
if(opt[i]!='N') continue;
up(j,1,n) if(opt[j]=='E') {
if(x[j]>x[i]||y[j]<y[i]) continue;
int ti=y[j]-y[i], tj=x[i]-x[j];
if(ti==tj) continue;
if(ti<tj) p[++m]=(node){i,j,tj};
if(tj<ti) p[++m]=(node){j,i,ti};
}
}
sort(p+1,p+1+m);
up(u,1,m) {
int i=p[u].i, j=p[u].j;
// cout << "breat " << i << ' ' << j << '\n';
if(!tag[j]&&(!tag[i]||opt[i]=='N'&&y[j]<=y[i]+tag[i]
||opt[i]=='E'&&x[j]<=x[i]+tag[i])) {
tag[j]=p[u].t, to[i].pb(j), ++in[j];
}
}
up(i,1,n) if(!in[i]) dfs(i);
up(i,1,n) cout << Ans[i] << '\n';
return 0;
}
2-C 总统大选 (election.cpp)
大样例全过挂分的来了。上次见到你,你叫作《机械师》。
贪心,首先先拿 \(a\) 再拿 \(b\) 显然是劣的,考虑操作拿 \(b_1,\dots,b_m,a_{m+1},\dots,a_{m_0}\),答案就是 \(\sum_{i=1}^m \frac{b_i}{i}+\sum_{i=m+1}^{m}\frac{a_i}{m+1}\)。最终的操作序列一定是按照 \(b\uparrow\) 的顺序获得协作者,然后在剩下的州里面挑 \(a\) 最小的或者选票直到足够。按照这个做是按照 \(b\uparrow\) 排序,枚举协作者总人数 \(m\),然后背包 \(f[i][j][k]\) 表示考虑前 \(i\) 个人选了 \(j\) 个协作者 \(k\) 个演讲的最小时间,答案是 \(\min_{m=0}^{m_0}\{f[n][m][m_0-k]\}\),还需要优化掉一个 \(O(n)\)。
直觉上我们希望拿最小的若干个 \(b\) 再拼凑上若干个后缀上最小的 \(a\),但是这么做是错误的,感性理解就是有些点虽然 \(b\) 不大但 \(a\) 尤其小,其拿 \(a\) 的性价比太高了,具体而言假定 \((a_1,b_1),(a_2,b_2)\) 要协作一个选票一个,设 \(b_1>b_2\),协作 1 比协作 2 优的条件形如 \(b_1+\frac{a_2}{2}<b_2+\frac{a_1}{2}\),也就是 \(2(b_2-b_1)<(a_1-a_1)\) 是有可能被满足的。
但是不影响扣掉这些被 \(a\) 的东西之后协作者堆成一个前缀,考虑处理前缀非 \(a\) 即 \(b\) 的答案之后往后合并即可,相当于额外加了一个枚举前缀位置。
只做 \(\forall i\in[1,m_0],j+k=i\) 就足够贡献了,还是枚举 \(m\),那么新的状态计算 \(f[i][j]\) 表示考虑了前 \(i\) 个州选了 \(j\) 个协作者的最小时间,计算后缀选 \(u\) 个 \(a\) 的最小时间和即可合并出答案。
#include<bits/stdc++.h>
#define int long long
#define db double
#define up(i,l,r) for(int i=l; i<=r; ++i)
#define dn(i,r,l) for(int i=r; i>=l; --i)
#define pii pair<int,int>
#define mp make_pair
#define a first
#define b second
using namespace std;
const int N=505, inf=1e13;
int n, m, arr[N], sum[N][N];
pii p[N];
db Ans=inf, f[N][N];
inline void chk(db &a,db b) { a=min(a,b); }
signed main() {
freopen("election.in","r",stdin);
freopen("election.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n >> m;
up(i,1,n) {
cin >> p[i].a >> p[i].b;
if(p[i].b==-1) p[i].b=inf;
}
sort(p+1,p+1+n,[](pii i,pii j){return i.b<j.b;});
dn(i,n,1) {
int tot=0;
up(j,i,n) arr[++tot]=p[j].a;
sort(arr+1,arr+1+tot);
up(j,1,n-i+1) sum[i][j]=sum[i][j-1]+arr[j];
}
up(k,0,m) {
up(i,0,m) up(j,0,k) f[i][j]=inf;
f[0][0]=0;
up(i,1,m) up(j,0,min(i,k)) {
chk(f[i][j],f[i-1][j]+(db)p[i].a/(k+1));
chk(f[i][j+1],f[i-1][j]+(db)p[i].b/(j+1));
}
up(i,0,m) chk(Ans,f[i][k]+(db)sum[i+1][m-i]/(k+1));
}
cout << fixed << setprecision(15) << Ans << '\n';
return 0;
}
2-D 风力发电机 (wind.cpp)
我是怎么做到不同 mst 最后才考虑重构树的,题目有点像模板套模板套模板嗯对。
因为求解答案是加入权值为 \(0\) 的边重新求 mst,所以实际上要使用的边肯定是原 mst 的子集,复杂问题进一步的我们建立出重构树,显然这样子可以体现 mst 更好的性质(权值递增、集合合并、顺序明确)。
现在考虑选定一些特殊点怎么在 mst 上考虑贡献,假设只有 \(p,q\) 就是删除 \(lca(p,q)\),因为能删除的是 \(p/q\to lca(p,q)\) 的边,而越上面的越不劣。进一步考虑很多个点 \(p_1,\dots,p_m\) 的情况,有点像虚树因为会刚好新建立 \(m-1\) 个虚点诶,更具体的可以考虑手摸 \(m\) 比较小的情况,发现可以归纳法看到贡献是对的。
进一步考虑一个点什么时候会成为虚点,显然是左右子树皆不为空的情况,就是存在 \((p,q)\) 满足 \(l\leq p<q\leq r\) 然后 \(p/q\) 分别在左右子树里面吧,哦发现 dsu on tree 点对个数只有 \(O(n\log n)\),可以处理出来。现在考虑怎么求解答案,就是对于 \(l,r\) 查询 \((p,q,w)\) 满足 \(l\leq p<q\leq r\) 的不同 \(w\) 的权值和,扫描线处理掉一维,然后要对 \(q\leq r\) 的每个 \(w\) 考虑 \(\max p\),打在树状数组上即可快速查询。
#include<bits/stdc++.h>
#define int long long
#define up(i,l,r) for(int i=l; i<=r; ++i)
#define dn(i,r,l) for(int i=r; i>=l; --i)
#define pb push_back
using namespace std;
const int N=200005, M=5000005;
int n, m, T, len, dsu[N], tot, val[N], ls[N], rs[N], Ans[N], lst[N], tr[N], pre;
struct edge { int u, v, w; } e[N], p[M], q[N];
vector<int> to[N];
set<int> point[N];
int get(int x) { return x==dsu[x]?x:dsu[x]=get(dsu[x]); }
void add(int x,int v) { for( ; x<=n; x+=x&-x) tr[x]+=v; }
int ask(int x) { int ret=0; for( ; x; x-=x&-x) ret+=tr[x]; return ret; }
int dfs(int x) {
if(x<=n) {
point[x].insert(x);
return x;
}
int l=dfs(ls[x]), r=dfs(rs[x]);
if(point[l].size()>point[r].size()) swap(l,r);
for(int val:point[l]) {
auto it=point[r].lower_bound(val);
if(it!=point[r].end()) p[++len]=(edge){val,*it,x};
if(it!=point[r].begin()) p[++len]=(edge){*--it,val,x};
}
for(int val:point[l]) point[r].insert(val);
point[l].clear();
return r;
}
signed main() {
freopen("wind.in","r",stdin);
freopen("wind.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n >> m >> T, tot=n;
up(i,1,m) cin >> e[i].u >> e[i].v >> e[i].w, ++e[i].u, ++e[i].v;
sort(e+1,e+1+m,[](edge i,edge j){return i.w<j.w;});
up(i,1,n) dsu[i]=i;
up(i,1,m) {
int x=get(e[i].u), y=get(e[i].v);
if(x!=y) {
pre+=e[i].w, val[++tot]=e[i].w, dsu[tot]=tot;
ls[tot]=x, rs[tot]=y, dsu[x]=dsu[y]=tot;
// cout << " " << tot << "(" << e[i].w << ")"<< ' ' << ls[tot] << '\n';
// cout << " " << tot << "(" << e[i].w << ")"<< ' ' << rs[tot] << '\n';
}
}
dfs(tot);
sort(p+1,p+1+len,[](edge i,edge j){return i.v<j.v;});
// up(i,1,len) cout << "check " << p[i].u << ' ' << p[i].v << ' ' << p[i].w << '\n';
up(i,1,T) q[i].w=i, cin >> q[i].u >> q[i].v, ++q[i].u, ++q[i].v;
sort(q+1,q+1+T,[](edge i,edge j){return i.v<j.v;});
int j=1;
up(i,1,T) {
while(j<=len&&p[j].v<=q[i].v) {
// cout << "insert " << p[j].u << ' ' << p[j].v << ' ' << val[p[j].w] << '\n';
int id=p[j].w;
if(lst[id]) {
if(lst[id]<p[j].u) {
add(lst[id],-val[id]);
lst[id]=p[j].u;
add(p[j].u,val[id]);
}
}
else add(p[j].u,val[id]), lst[id]=p[j].u;
++j;
}
Ans[q[i].w]=ask(n)-ask(q[i].u-1);
}
up(i,1,T) cout << pre-Ans[i] << '\n';
return 0;
}

浙公网安备 33010602011771号