20241001
赛时得分
| 题目 | A | B | C | D | 总分 | 排名 | 比例 |
|---|---|---|---|---|---|---|---|
| 满分 | 100 | 100 | 100 | 100 | 400 | 163 | 100% |
| 得分 | 50 | 57 | 20 | 0 | 127 | 89 | 54.6% |
A. 好伙伴数(100/100)
\(\text{30%}\) 得分做法,直接 \(n^2\) 枚举即可,但考试的时候写了一坨,甚至还挂了 \(\text{10pts}\)。
\(\text{60%}\) 得分做法,也就是只有两种数字的性质,我们分为只有 1、只有 2、1 和 2 都有这三种情况。对于分别只有一个数字的情况,我们直接选择乘法原理(等差数列也一样)统计答案数,对于两个数都有的情况,我们先乘法原理,再每个乘上其他情况的方案数总和即可。
挂一下赛时的 \(\text{50pts}\) 代码:
#include<bits/stdc++.h>
#define Std_Maker lhm
#define ll long long
using namespace std;
const int N=1e6+1;
ll n,a[11][N],c[11];
set<pair<ll,ll> > s;
string str[N];
bool ok=1;
void change(ll i,ll res,ll c)
{
for(int j=1;j<=c;j++)
{
if(a[res][j]==i) continue;
s.insert({min(a[res][j],i),max(a[res][j],i)});
}
a[res][++c]=i;
return;
}
int main()
{
freopen("friend.in","r",stdin);
freopen("friend.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>str[i];
for(int j=0;j<str[i].length();j++)
{
if(str[i][j]!='1' and str[i][j]!='2') ok=0;
}
}
if(ok==0)
{
for(int i=1;i<=n;i++)
{
for(int j=0;j<str[i].length();j++)
{
string num="";
num+=str[i][j];
ll res=stoi(num);
change(i,res,c[res]);
c[res]++;
}
}
cout<<s.size();
}
else
{
ll cnt1=0,cnt2=0,cnt3=0,ans=0;
for(int i=1;i<=n;i++)
{
bool vis1=0,vis2=0;
for(int j=0;j<str[i].length();j++)
{
if(str[i][j]=='1') vis1=1;
else if(str[i][j]=='2') vis2=1;
}
if(vis1==1 and vis2==0) cnt1++;
else if(vis1==1 and vis2==1) cnt3++;
else cnt2++;
}
ans+=cnt3*(cnt1+cnt2);
ans+=(1+cnt1-1)*(cnt1-1)/2;
ans+=(1+cnt2-1)*(cnt2-1)/2;
ans+=(1+cnt3-1)*(cnt3-1)/2;
cout<<ans;
}
return 0;
}
\(\text{100%}\) 得分做法,一种状态压缩的套路,就是二进制状压,我们考虑直接将一个数字的所含的数字表示成一个二进制形式,每一位上有对应数字就是 1,没有就是 0。然后我们会得到一个二进制数,再转化为十进制,就可以很简单得到一个转化后的压缩数,不难发现所有的压缩数最大也只能达到 \(2^{10}=1024\),所以我们就统计个数,然后放心 \(\mathcal{O}(1024^2)\) 枚举即可,对于一样的性质直接乘法原理,剩下只要按位与的结果是 1 就可以乘起来。
#include<bits/stdc++.h>
#define Std_Maker lhm
#define ll long long
using namespace std;
const int N=1e6+1;
ll n,a[N],cnt[N],ans;
ll convert(ll x)
{
ll res=0;
bool vis[11];
memset(vis,0,sizeof(vis));
while(x)
{
vis[x%10]=1;
x/=10;
}
for(int i=0;i<=9;i++)
{
if(vis[i]==1) res+=pow(2,i);
}
return res;
}
int main()
{
freopen("friend.in","r",stdin);
freopen("friend.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
cnt[convert(a[i])]++;
}
for(int i=0;i<=1024;i++)
{
for(int j=i;j<=1024;j++)
{
if(i==j) ans+=cnt[i]*(cnt[i]-1)>>1;
else if(i&j) ans+=cnt[i]*cnt[j];
}
}
cout<<ans;
return 0;
}
B. 武装士兵(57/100)
\(\text{20%}\) 得分做法,是 \(A_i=0\) 这一档部分分,我们直接将所有价格从小到大排序,直接能选多少选多少就行了,最大价格一定是 0,因为士兵自己没钱。
\(\text{50%}\) 得分做法,再加上 \(S=0\) 这一档,考虑我们将所有价格扔到一个 multiset 里,然后按照人手里的钱数从大到小 lower_bound,如果在最开始就是,并且不等于你的钱数,就说明你买不起,过掉。其他的如果 find 结果是 1,就直接买,如果 lower_bound 不是开头,我们走一下 lower_bound 然后左移一位就是能买的起的最大值了。这时候我们注意要统计一下最大钱数就行了。
\(\text{57%}\) 得分做法,我的决策其实是有问题的,但是精度还挺可观的,迷迷糊糊水过了两个点。
#include<bits/stdc++.h>
#define Std_Maker lhm
#define ll long long
using namespace std;
const int N=1e5+1;
ll n,m,s,a[N],b[N];
bool ok=0;
bool cmp(ll a,ll b)
{
return a>b;
}
int main()
{
freopen("soldier.in","r",stdin);
freopen("soldier.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n>>m>>s;
for(int i=1;i<=n;i++)
{
cin>>a[i];
if(a[i]!=0) ok=1;
}
for(int i=1;i<=m;i++) cin>>b[i];
if(ok==0)
{
sort(b+1,b+m+1);
ll sum=0;
for(ll i=1;i<=m;i++)
{
sum+=b[i];
if(sum>s)
{
cout<<min(i-1,n)<<" 0";
return 0;
}
}
cout<<n<<" 0";
return 0;
}
if(s==0)
{
sort(a+1,a+n+1,cmp);
ll cnt=0,ans=0;
multiset<ll> val;
for(int i=1;i<=m;i++) val.insert(b[i]);
for(int i=1;i<=n;i++)
{
if(val.find(a[i])==val.end() and val.lower_bound(a[i])==val.begin()) continue;
else
{
if(val.find(a[i])!=val.end())
{
val.erase(val.find(a[i]));
cnt++;
ans=max(ans,a[i]);
}
else
{
cnt++;
ans=max(ans,*--val.lower_bound(a[i]));
val.erase(--val.lower_bound(a[i]));
}
}
}
cout<<cnt<<" "<<ans;
return 0;
}
else
{
sort(a+1,a+n+1,cmp);
ll cnt=0,ans=0;
multiset<ll> val;
for(int i=1;i<=m;i++) val.insert(b[i]);
for(int i=1;i<=n;i++)
{
if(val.find(a[i])==val.end() and val.lower_bound(a[i])==val.begin())
{
s-=(*val.lower_bound(a[i])-a[i]);
if(s>=0)
{
cnt++;
val.erase(val.lower_bound(a[i]));
}
}
else
{
if(val.find(a[i])!=val.end())
{
val.erase(val.find(a[i]));
cnt++;
}
else
{
cnt++;
val.erase(--val.lower_bound(a[i]));
}
}
}
cout<<cnt<<" "<<0;
return 0;
}
return 0;
}
C. 送蛋糕(50/100)
\(\text{20%}\) 得分做法,我们直接 next_permutation 枚举全排列,依题意算一下最大收益即可。
#include<bits/stdc++.h>
#define Std_Maker lhm
#define ll long long
using namespace std;
const int N=2e5+1,inf=2147483647;
ll n,m,t[N],l[N],k,tk,lk,a[N],maxn=-inf,ans,tim;
int main()
{
freopen("cake.in","r",stdin);
freopen("cake.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>t[i]>>l[i];
for(int i=1;i<=n;i++) a[i]=i;
do
{
ans=0,tim=0;
for(int i=1;i<=n;i++)
{
tim+=l[a[i]];
ans+=(t[i]-tim);
}
maxn=max(maxn,ans);
}while(next_permutation(a+1,a+n+1));
cout<<maxn<<endl;
while(m--)
{
cin>>k>>tk>>lk;
t[k]=tk,l[k]=lk;
for(int i=1;i<=n;i++) a[i]=i;
maxn=-inf;
do
{
ans=0,tim=0;
for(int i=1;i<=n;i++)
{
tim+=l[a[i]];
ans+=(t[i]-tim);
}
maxn=max(maxn,ans);
}while(next_permutation(a+1,a+n+1));
cout<<maxn<<endl;
}
return 0;
}
\(\text{40%}\) 做法,终究还是败给了贪心。考虑贪心,我们应该优先做的任务是耗时短的任务(正确性还没证出来,但他就是对的)。然后直接模拟就完事了。
老师,我家 next_permutation 为什么比不过他家的贪心。
#include<bits/stdc++.h>
#define Std_Maker lhm
#define ll long long
using namespace std;
const int N=2e5+1;
ll n,m,k,tk,lk,ans,tim;
struct lhm
{
ll t,l;
}a[N],b[N];
bool cmp(lhm a,lhm b)
{
if(a.l==b.l) return a.t<b.t;
return a.l<b.l;
}
int main()
{
freopen("cake.in","r",stdin);
freopen("cake.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>a[i].t>>a[i].l;
b[i].t=a[i].t;
b[i].l=a[i].l;
}
sort(a+1,a+n+1,cmp);
for(int i=1;i<=n;i++)
{
tim+=a[i].l;
ans+=(a[i].t-tim);
}
cout<<ans<<endl;
while(m--)
{
ans=0,tim=0;
cin>>k>>tk>>lk;
b[k].t=tk,b[k].l=lk;
for(int i=1;i<=n;i++)
{
a[i].t=b[i].t;
a[i].l=b[i].l;
}
sort(a+1,a+n+1,cmp);
for(int i=1;i<=n;i++)
{
tim+=a[i].l;
ans+=(a[i].t-tim);
}
cout<<ans<<endl;
}
return 0;
}
\(\text{50%}\) 得分做法,做 \(L_i\) 相等的那一档部分分。其实说实话会了 40 分再做这档就非常简单了,我们只需要在线修改记录的 \(ans\),随便就能把修改操作复杂度降到 \(\mathcal{O}(m)\)。
#include<bits/stdc++.h>
#define Std_Maker lhm
#define ll long long
using namespace std;
const int N=2e5+1;
ll n,m,k,tk,lk,ans,tim;
bool ok=1;
struct lhm
{
ll t,l;
}a[N],b[N];
bool cmp(lhm a,lhm b)
{
if(a.l==b.l) return a.t<b.t;
return a.l<b.l;
}
int main()
{
freopen("cake.in","r",stdin);
freopen("cake.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>a[i].t>>a[i].l;
b[i].t=a[i].t;
b[i].l=a[i].l;
if(a[i].l!=a[1].l) ok=0;
}
sort(a+1,a+n+1,cmp);
for(int i=1;i<=n;i++)
{
tim+=a[i].l;
ans+=(a[i].t-tim);
}
cout<<ans<<endl;
if(ok==1)
{
while(m--)
{
cin>>k>>tk>>lk;
ans+=(tk-b[k].t);
b[k].t=tk,b[k].l=lk;
cout<<ans<<endl;
}
return 0;
}
else
{
while(m--)
{
ans=0,tim=0;
cin>>k>>tk>>lk;
b[k].t=tk,b[k].l=lk;
for(int i=1;i<=n;i++)
{
a[i].t=b[i].t;
a[i].l=b[i].l;
}
sort(a+1,a+n+1,cmp);
for(int i=1;i<=n;i++)
{
tim+=a[i].l;
ans+=(a[i].t-tim);
}
cout<<ans<<endl;
}
}
return 0;
}

浙公网安备 33010602011771号