【LGR-148-Div.3】洛谷基础赛 #1 & MGOI Round I
T1
简单题,题面十分清晰,就是给我们\(n\),要求使\(2^m<n\)成立的最小偶数\(m\)。(要注意\(log_2N=m,m|2\)的情况)
#include <bits/stdc++.h>
#define ll long long
#define re register
using namespace std;
const int N=800, INF=0x3f3f3f3f;
ll n;
int main(){
cin>>n;
ll k=log(n)/log(2);
if(k%2!=0)k=k-1;
else if(pow(2,k)==n)k=k-2;
cout<<k<<endl;
return 0;
}
T2
有一点意思,很明显的模拟题,但是一定要细致。比如,第一次,我认为只需对于每一个客人,将他所想要的幕布和当前幕布放到函数判断即可(错误):
int f(char k,char want){
if(k==want)return 0;
if(k=='W'){
if(want=='B')return 1;
else return 2;
}else if(k=='B'){
if(want=='W')return 1;
else return 1;
}else if(k=='R'){
if(want=='W')return 1;
else return 1;
}
}
但是,其实每一步操作,都存在所想要的幕布是否已经上升这一问题,上升与否是必须考虑的。稍加思索,发现可以将每一个幕布设成bool
数组,为一则已经下降,为零则仍为上升。将目标幕布前的幕布值相加,在加上自身取反操作后的值(以为下降为一,而这时无需操作。为零时,反而需要操作)。
#include <bits/stdc++.h>
#define ll long long
#define re register
using namespace std;
const int N=800, INF=0x3f3f3f3f;
int n;
bool f['Z'];//1 down;0 up
int ans=0;
int main(){
cin>>n;
f['R']=f['B']=f['W']=1;
for(int i=1;i<=n;i++){
char x;cin>>x;
if(x=='W'){
ans+=(!f['W']);
f['W']=1;
}
if(x=='B'){
ans+=f['W']+(!f['B']);
f['W']=0;
f['B']=1;
}
if(x=='R'){
ans+=f['W']+f['B']+(!f['R']);
f['W']=f['B']=0;
f['R']=1;
}
}
cout<<ans<<endl;
return 0;
}
T3
一道图论题。不知道正解是什么(悲。想法为:正着模拟有点困难,因为无法确定当初的魔力值和生命,所以果断反向搜索。注意到求的是最小值,自然想到最短路。我们将每条边的权值变为走这条路的生命消耗值,然后跑一遍dijkstra即可。赛时,遇到若干问题:
- 我在
build
函数中已经改变了\(i.w\)的值,但是到dij
函数就又回到原来的了(后来新建数组存储了) - 内存爆炸,只能跑部分的\(n\)。
后,注意到Sub3的特殊,解决。
30Pts代码:
#include <bits/stdc++.h>
#define ll long long
#define re register
using namespace std;
const int N=2000+5, INF=0x3f3f3f3f;
ll n,m,s,t;
bool vis[N];
bool vis_edge[N][N];
struct node{
ll v,w;
};
ll magic[N];
vector<node> G[N];
ll a[N][N];
void build(){
queue<ll> Q;
Q.push(t);
magic[t]=1;
while(Q.size()){
ll x=Q.front();Q.pop();
for(auto i:G[x]){
if(!vis_edge[i.v][x]&&!vis_edge[x][i.v]){
vis_edge[i.v][x]=1;
vis_edge[x][i.v]=1;
//cout<<x<<" To "<<i.v<<" from "<<i.w<<" to ";
i.w=i.w/magic[x];
a[i.v][x]=a[x][i.v]=i.w;
//cout<<i.w<<endl;
magic[i.v]=magic[x]+1;
Q.push(i.v);
}
}
}
}
struct Heapnode{
ll v,dis;
bool operator>(const Heapnode& a)const{return dis>a.dis;}
};
priority_queue<Heapnode,vector<Heapnode>,greater<Heapnode> > Q;
ll d[N];
void dij(ll nd){
memset(vis,0,sizeof(vis));
memset(d,INF,sizeof(d));
Q.push({nd,0});
d[nd]=0;
while(Q.size()){
ll u=Q.top().v;Q.pop();
if(vis[u])continue;
vis[u]=1;
for(auto i:G[u]){
ll x=i.v,y=a[u][x];
if(!vis[x]&&d[u]+y<d[x]){
d[x]=d[u]+y;
Q.push({x,d[x]});
}
}
}
}
int main(){
cin>>n>>m>>s>>t;
for(int i=1;i<=m;i++){
ll x,y,z;cin>>x>>y>>z;
G[x].push_back({y,z});
G[y].push_back({x,z});
}
if(n<=2000){
build();
/*for(int i=1;i<=n;i++){
cout<<"第"<<i<<"个点:"<<endl;
for(int j=1;j<=n;j++){
cout<<"To "<<j<<" is "<<a[i][j]<<endl;
}
}*/
dij(t);
cout<<d[s]<<endl;
}else{
bool f=0;
for(auto i:G[t])if(i.w==0){
f=1;break;
}
if(f)cout<<0;
else cout<<1;
}
return 0;
}
正解有人说DP。但是如果用bfs或者dij都需要考虑某点重复走路用生命值换取魔力值,从而走到更远的地方的情况。很明显,我没有考虑,很明显,不会了(等题解。
T4
题目清晰,不绕。Sub1明显是纯暴力分,用01来储存各个结点状态,然后模拟。中间剪了一些枝,但是无济于事。暴力分拿下,后面不会。
#include <bits/stdc++.h>
#define ll long long
#define re register
using namespace std;
const int N=100+5, INF=0x3f3f3f3f;
int n,k;
int a[N];
bool zt[N];//1 激活;2 未激活
/*bool pd(){
int t=0;
for(int i=1;i<=n;i++)if(zt[i])t++;
return t>=k;
}*/
int minn=INF;
int Maxn(int x){
bool fleft=0,fright=0;
int m,dis,m2,dis2;
for(int i=x;i>=1;i--){
if(zt[i]){
fleft=1;m=a[i];dis=x-i;break;
}
}
if(!fleft){
for(int i=n;i>=x;i--){
if(zt[i]){
m=a[i];dis=x+n-i;break;
}
}
}
for(int i=x;i<=n;i++){
if(zt[i]){
fright=1;m2=a[i];dis2=i-x;break;
}
}
if(!fright){
for(int i=1;i<=x;i++){
if(zt[i]){
m2=a[i];dis2=n-x+i;break;
}
}
}
if(m>m2)return m*dis;
else return m2*dis2;
}
void f(int dep,int num1){
if(num1+n-dep+1<k)return;
if(dep>n){
if(num1>=k){
int ans=0;
for(int i=1;i<=n;i++){
if(zt[i])ans+=a[i]*a[i];
else ans+=Maxn(i);
}
minn=min(minn,ans);
}
return ;
}
for(int i=0;i<=1;i++){
zt[dep]=i;
if(i==1)f(dep+1,num1+1);
else f(dep+1,num1);
}
}
int main(){
cin>>n>>k;
for(int i=1;i<=n;i++)cin>>a[i];
f(1,0);
cout<<minn<<endl;
return 0;
}
用时方面:
T1:6.82min
T2:34.67min
T3:2.37h
T4:2.98h
总分:100+100+30+13=243
rk:348
传送门