2021.10.18 膜你赛
T1
Description
由于小 X 是一个奆老,所以他看不起普通商店里卖的电子秤,他决定自己做一个。
他的称重工具是一架由金子制成的天平, 这架天平的精度非常高, 可以达到纳克的标准,
1g=10 9 ng,小 X 会把物品放在天平的右侧,然后在天平的左侧和右侧都放上一些砝码,直至
天平平衡。该天平的砝码是用钻石制成的,每个砝码的质量依次为 1ng、3ng、9ng、27ng、
81ng……,每个砝码的质量都是 3 的幂次(如 3 的 6 次幂表示为 3^6=729),且各不相同。
由于小 X 是一个奆老,他有对各个物品未卜先知的能力,他会告诉你他的物品的质量,
希望你给他一个方案,使得天平的两侧平衡。
Solution
对于给出的 W,进行三进制拆分,如果这一位是 1,砝码加在左边,如果当前是 2,砝码加在右边,并进位。
Code
/*
* @Author: smyslenny
* @Date: 2021.10.
* @Title:
* @Main idea:
*/
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <iomanip>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <vector>
#include <map>
#define int long long
#define INF 0x3f3f3f3f
#define orz cout<<"LKP AK IOI\n"
#define MAX(a,b) (a)>(b)?(a):(b)
#define MIN(a,b) (a)>(b)?(a):(b)
using namespace std;
const int mod=1e9+7;
const int M=1e3+5;
int n;
int read()
{
int x=0,y=1;
char c=getchar();
while(c<'0' || c>'9') {if(c=='-') y=0;c=getchar();}
while(c>='0' && c<='9') { x=x*10+(c^48);c=getchar();}
return y?x:-x;
}
int tre[M],num,fg;
int ans_l[M],ans_r[M],js_l,js_r=1;
bool check(int x)
{
return x==n;
}
namespace substack1{
void dfs(int pos)
{
if(fg) return;
if(check(num))
{
fg=1;
for(int i=1;i<=js_l;i++) printf("%lld ",ans_l[i]);
printf("\n%lld ",n);
for(int i=1;i<=js_r;i++) printf("%lld ",ans_r[i]);
printf("\n");
return;
}
if(pos>10) return;
ans_l[++js_l]=tre[pos];
num+=tre[pos];
dfs(pos+1);
num-=tre[pos]*2;
js_l--;
ans_r[++js_r]=tre[pos];
dfs(pos+1);
num+=tre[pos];
js_r--;
dfs(pos+1);
}
void main()
{
dfs(0);
}
}
namespace substack2{
void dfs(int pos)
{
if(fg) return;
if(check(num))
{
fg=1;
for(int i=1;i<=js_l;i++) printf("%lld ",ans_l[i]);
printf("\n%lld ",n);
for(int i=1;i<=js_r;i++) printf("%lld ",ans_r[i]);
printf("\n");
return;
}
if(pos>10) return;
ans_l[++js_l]=tre[pos];
num+=tre[pos];
dfs(pos+1);
num-=tre[pos]*2;
js_l--;
ans_r[++js_r]=tre[pos];
dfs(pos+1);
num+=tre[pos];
js_r--;
dfs(pos+1);
}
void main()
{
dfs(0);
}
}
namespace substack3{
int tri[M],cnt,sz[M],zs[M],m,mm;
void main()
{
int W=n,s=1;
while(W)
{
tri[++cnt]=W%3;
W/=3;
}
for(int i=1;i<=cnt+1;i++)
{
if(tri[i]==1)
{
if(sz[mm]==s)
sz[mm]=s*3,zs[++m]=s;
else sz[++mm]=s;
}
if(tri[i]==2)
{
if(sz[mm]==s)
sz[mm]=s*3;
else zs[++m]=s,sz[++mm]=s*3;
}
s*=3;
}
// printf("%d ",sz[1]);
for(int i=1;i<=mm;i++) printf("%lld ",sz[i]);
printf("\n%lld ",n);
for(int i=1;i<=m;i++) printf("%lld ",zs[i]);
}
}
signed main()
{
// freopen("entertain.in","r",stdin);
// freopen("entertain.out","w",stdout);
n=read();
// tre[0]=1;
// for(int i=1;i<=50;i++) tre[i]=tre[i-1]*3;
// js_l=0,js_r=0;
// if(n<=10000) substack1::main();
// else substack2::main();
substack3::main();
return 0;
}
T2
Description
小 S 和小 Z 十分喜欢看网络写手“2 5 ”的小说,但由于需要付费才能阅读,而小 S 和小
Z 的零花钱有非常少,他们只能找小 X 靠黑科技侵入给网站,把小说给他们。
然而小 X 又非常的爱慕虚荣,他要小 S 和小 Z 到自己家里来取小说。
小 S、小 Z 和小 X 都居住在扬中市,扬中市共有 n 个小区,m 条主干道(假设每条主干
道都是双行线)。小 S 家住在 1 号小区,小 X 家住在 n 号小区。小 S 每经过一条主干道需要
耗费 z 点体力,但由于小 S 的人脉非常广,每当他到达一个小区,他都会和好友攀谈直到体
力回满。
由于小 Z 也希望能看到小说,所以他答应帮助小 S k 次,这 k 次小 S 经过主干道不需要
耗费体力。
由于小 S 生性懒惰,他希望耗费最少的体力到达小 X 家,请问他最少耗费多少体力?
注意:如果小 S 到小 X 家可以一路上都由小 Z 背着,那么体力上限为 0;
如果小 S 到不了小 X 家,小 S 会很伤心,体力上限为-1;
Solution
二分一个最大值,最短路check 一下。
由于数据不是很强,也可以跑分层图。
Code
/*
* @Author: smyslenny
* @Date: 2021.10.
* @Title:
* @Main idea:
*/
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <iomanip>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <vector>
#include <map>
#define ll long long
#define INF 0x3f3f3f3f
#define orz cout<<"LKP AK IOI\n"
#define MAX(a,b) (a)>(b)?(a):(b)
#define MIN(a,b) (a)>(b)?(a):(b)
using namespace std;
const int mod=1e9+7;
const int M=1e4+5;
int n,m,k,Ans=-1;
int read()
{
int x=0,y=1;
char c=getchar();
while(c<'0' || c>'9') {if(c=='-') y=0;c=getchar();}
while(c>='0' && c<='9') { x=x*10+(c^48);c=getchar();}
return y?x:-x;
}
struct ed{
int u,v,w,net;
}edge[M<<1];
int head[M],num;
void addedge(int u,int v,int w)
{
edge[++num].u=u;
edge[num].v=v;
edge[num].w=w;
edge[num].net=head[u];
head[u]=num;
}
struct node{
int u,dis;
bool operator < (const node &a) const {
return dis>a.dis;
}
};
priority_queue<node> q;
int dis[M],vis[M];
int check(int mid)
{
memset(dis,INF,sizeof(dis));
memset(vis,0,sizeof(vis));
dis[1]=0;
q.push(node{1,dis[1]});
while(!q.empty())
{
node x=q.top();
q.pop();
int u=x.u;
vis[u]=1;
for(int i=head[u];i;i=edge[i].net)
{
int v=edge[i].v,w=edge[i].w,dist=dis[u];
if(vis[v]) continue;
if(w>=mid) dist++;
if(dis[v]>dist)
{
dis[v]=dist;
q.push(node{v,dis[v]});
}
}
}
return dis[n]>=k+1;
}
int main()
{
freopen("novel.in","r",stdin);
freopen("novel.out","w",stdout);
n=read(),m=read(),k=read();
for(int i=1;i<=m;i++)
{
int u=read(),v=read(),w=read();
addedge(u,v,w);
addedge(v,u,w);
}
int l=0,r=1e6+100;
while(l<=r)
{
int mid=(l+r)>>1;
if(check(mid))
l=mid+1,Ans=mid;
else r=mid-1;
}
if(Ans>1e6) printf("-1\n");
else if(r<0) printf("0\n");
else printf("%d\n",Ans);
return 0;
}
T3
Description
他准备给两位奆老中的一个人绿叶配红花,另一个人红叶配绿花。
由于绿叶配红花大家说顺口了, 所以小 X 家楼下的花店里就有出售, 但红叶配绿花是小
X 口味独特的体现,花店里当然是不会有的,小 X 只能自行拼凑。
他家种了一棵枫树,现在有的枫叶是红色的,有的枫叶是黄色的,小 X 只要采摘红色的
枫叶。每片枫叶有一个年轻程度,他希望他采摘的枫叶的年轻程度总和越小越好。
这棵枫树有 n 个节点(从 0 开始编号),m 片叶子。他希望采摘到恰好 k 片红色叶子的
经过每个节点的年轻程度总和最小的生成树。
注意:保证数据有解。
Solution
wqs二分的板子题,当时我竟然没想到!
Code
/*
* @Author: smyslenny
* @Date: 2021.10.
* @Title:
* @Main idea:
*/
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <iomanip>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <vector>
#include <map>
#define ll long long
#define INF 0x3f3f3f3f
#define orz cout<<"LKP AK IOI\n"
#define MAX(a,b) (a)>(b)?(a):(b)
#define MIN(a,b) (a)>(b)?(a):(b)
using namespace std;
const int mod=1e9+7;
const int M=5e4+5;
int n,m,k,js,Ans;
int read()
{
int x=0,y=1;
char c=getchar();
while(c<'0' || c>'9') {if(c=='-') y=0;c=getchar();}
while(c>='0' && c<='9') { x=x*10+(c^48);c=getchar();}
return y?x:-x;
}
int du[M],f[M],tot,cnt;
int find(int x)
{
if(f[x]==x) return x;
return f[x]=find(f[x]);
}
struct ed{
int u,v,w,col;
}edge[M<<1],e[M<<1];
bool cmp(ed a,ed b)
{
if(a.w==b.w) return a.col<b.col;
return a.w<b.w;
}
bool check(int mid)
{
tot=cnt=0;
for(int i=1;i<=n;i++) f[i]=i;
for(int i=1;i<=m;i++)
{
e[i]=edge[i];
if(edge[i].col==0) e[i].w+=mid;
}
sort(e+1,e+1+m,cmp);
for(int i=1;i<=m;i++)
{
int fa=find(e[i].u),fb=find(e[i].v);
if(fa!=fb)
{
f[fa]=fb;
tot+=e[i].w;
if(!e[i].col) cnt++;
}
}
return cnt>=k;
}
//
int main()
{
// freopen("leaf.in","r",stdin);
// freopen("leaf.out","w",stdout);
n=read(),m=read(),k=read();
for(int i=1;i<=m;i++)
edge[i].u=read()+1,edge[i].v=read()+1,edge[i].w=read(),edge[i].col=read();
int l=-105,r=105;
while(l<=r)
{
int mid=(l+r)>>1;
if(check(mid))
l=mid+1,Ans=tot-k*mid;//减去我们二分的附加值
else r=mid-1;
}
printf("%d\n",Ans);
return 0;
}