蒟蒻感想
感觉今天的题真是难得过分
一道数据结构也不出
只出了一道堆,还是用stl过的…
不扯淡了,蒟蒻这就上题解
T1
洛谷题目传送门
我会尽力给大家找到非本校OJ的题目链接,当然很可能找不到,您们可以在评论区把链接发过来,我好补上
截个题面
杀马特是什么鬼
我们学校的题目描述不是这样的
此题大力贪心即可
对于比
≥c
≥
c
的钞票直接大力一张一张丢就好了
现在考虑面值比c小的钞票
是用贪心大法
怎么贪心呢
显然面值比较小的硬币可以“填缝”,创造比较大的价值,比如
c=61+5=6
c
=
6
1
+
5
=
6
,单独一张5虽然面值大也没有用
所以贪心的去取
先取大的面值
取不到c就结束了
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<limits.h>
#define MAXN 25
using namespace std;
struct shit
{
long long v,k;
}cow[MAXN];
bool mmp(shit a,shit b)
{
return a.v<b.v;
}
long long n,c,fuck[MAXN];
long long ans;
int main()
{
scanf("%lld%lld",&n,&c);
for(long long i=1;i<=n;i++)
scanf("%lld%lld",&cow[i].v,&cow[i].k);
sort(cow+1,cow+n+1,mmp);
long long left=1,right=1;
for(long long i=n;i;i--)
{
if(cow[i].v<c)
{
right=i;
break;
}
ans+=cow[i].k;
if(i==1)
{
cout<<ans<<endl;
return 0;
}
}
while(left<=right)
{
long long temp=0;
memset(fuck,0,sizeof(fuck));
for(long long i=right;i>=left;i--)
{
long long sum=(c-temp)/cow[i].v;
fuck[i]=min(sum,cow[i].k);
temp+=fuck[i]*cow[i].v;
if(temp>=c)break;
}
if(c-temp)
{
for(long long i=left;i<=right;i++)
{
if(cow[i].v>=c-temp&&cow[i].k-fuck[i])
{
fuck[i]++;
temp+=cow[i].v;
break;
}
}
}
if(temp<c)
{
printf("%lld\n",ans);
return 0;
}
long long t=INT_MAX;
for(long long i=left;i<=right;i++)if(fuck[i])t=min(t,cow[i].k/fuck[i]);
ans+=t;
for(long long i=left;i<=right;i++)if(fuck[i])cow[i].k-=t*fuck[i];
}
}
变量名有点怪,但我不想改了
T2
此题是本次考试最难的一题
T3,T4跟他一比简直水爆
我最后A的这道题,想了好长时间,WA了好几次(多亏了ACM赛制)
洛谷题目传送门
难度一下跳到了省选-
我一开始想到最大连续子段和,想要贪心
O(n)
O
(
n
)
做,但是只有28分QAQ
在此基础上改动
二分答案+大力最大连续子段和
对于一个二分得到的答案ans,我们怎么验证呢?
以及为什么要提到最大连续子段和呢?
首先,我们在有ans的情况下,可以算出每个数和ans的差
假设我们去掉的一段值为value,长度为len
那么对应原数列中的就为
value+ans×len
v
a
l
u
e
+
a
n
s
×
l
e
n
剩余数的平均值就为
tot−value−ans×lenn−len
t
o
t
−
v
a
l
u
e
−
a
n
s
×
l
e
n
n
−
l
e
n
把它和ans作比较
tot−value−ans×lenn−len−ans
t
o
t
−
v
a
l
u
e
−
a
n
s
×
l
e
n
n
−
l
e
n
−
a
n
s
=tot−value−ans×nn−len
=
t
o
t
−
v
a
l
u
e
−
a
n
s
×
n
n
−
l
e
n
所以验证剩余数的平均分是否
≤ans
≤
a
n
s
就验证一下
tot−value−ans×n≤0
t
o
t
−
v
a
l
u
e
−
a
n
s
×
n
≤
0
是否成立
显然value越大越妙
PS:这道破题卡精度,精度稍微要大一点
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define MAXN 100010
using namespace std;
int a[MAXN],n,tot;
double temp[MAXN];
inline bool check(double ans)
{
for(int i=1;i<=n;i++)temp[i]=(double)a[i]-ans;
double now=0,biggest=-10000000000;
for(int i=2;i<n;i++)
{
now+=temp[i];
biggest=max(biggest,now);
if(now<0)now=0.0000000;
}
return tot-biggest-ans*n<=0;
}
int main()
{
scanf("%d",&n);
double left=0,right=10000;
for(int i=1;i<=n;i++)scanf("%d",&a[i]),tot+=a[i];
while(right-left>=0.0001)
{
double mid=(left+right)/2;
if(check(mid))right=mid;
else left=mid+0.00001;
}
printf("%.3lf\n",(left+right)/2);
}
T3
这道题没有找到其他网站的链接
只能截题面了
此题巨傻无比
先按照分数排序
枚举中位数
是中位数的条件就是他的前面有
n2
n
2
个数,他的后面也有
n2
n
2
个数
我们维护一下在他前面取
n2
n
2
个数的最小花费,在他后面取
n2
n
2
个数的最小花费
再
O(n)
O
(
n
)
枚举
维护最小花费的操作要用堆实现
预计时间复杂度
O(nlog2n)
O
(
n
log
2
n
)
,一点都不虚
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define MAXN 100100
#define ll long long
using namespace std;
inline void read(ll &x)
{
ll s=0,w=1;
char c=getchar();
while(c<'0'||c>'9')
{
if(c=='-')w=-1;
c=getchar();
}
while(c>='0'&&c<='9')
{
s=(s<<3)+(s<<1)+c-'0';
c=getchar();
}
x=s*w;
}
struct node
{
ll s,q;
}cow[MAXN];
ll n,c,f,ans=-1,qianyiban[MAXN],houyiban[MAXN];
inline bool mmp(node a,node b)
{return a.s<b.s;}
priority_queue<ll>Q;
int main()
{
read(n),read(c),read(f);
for(int i=1;i<=c;i++)read(cow[i].s),read(cow[i].q);
sort(cow+1,cow+c+1,mmp);
while(!Q.empty())Q.pop();
for(int i=1;i<=c;i++)
{
if(Q.size()<n/2)Q.push(cow[i].q),qianyiban[i]=qianyiban[i-1]+cow[i].q;
else
{
if(cow[i].q<Q.top())
{
qianyiban[i]=qianyiban[i-1]-Q.top()+cow[i].q;
Q.pop();
Q.push(cow[i].q);
}
else qianyiban[i]=qianyiban[i-1];
}
}
while(!Q.empty())Q.pop();
for(int i=c;i>=1;i--)
{
if(Q.size()<n/2)Q.push(cow[i].q),houyiban[i]=houyiban[i+1]+cow[i].q;
else
{
if(cow[i].q<Q.top())
{
houyiban[i]=houyiban[i+1]-Q.top()+cow[i].q;
Q.pop();
Q.push(cow[i].q);
}
else houyiban[i]=houyiban[i+1];
}
}
for(int i=n/2+1;i+n/2<=c;i++)
{
if(qianyiban[i-1]+cow[i].q+houyiban[i+1]<=f)ans=max(ans,cow[i].s);
}
if(ans<0)
{
puts("-1");
return 0;
}
printf("%lld\n",ans);
}
T4
这个也没找到出处
这个没什么好说,退化版带权并查集,我并不会带权并查集
就是将一头牛i拆成i和i+n(i+n是i的对立面)
如果i和i+n在一个并查集里就是矛盾了
A说B说假话的条件A,B属于不同的集合(一真一假)
A说B说真话的条件A,B属于同一集合(两真或两假)
#include<iostream>
#include<cstdio>
#include<cstring>
#define MAXN 2010
using namespace std;
int fa[MAXN];
int find(int x)
{
if(fa[x]==x)return x;
fa[x]=find(fa[x]);
return fa[x];
}
inline void un(int x,int y)
{
x=find(x),y=find(y);
if(x^y)fa[y]=x;
}
int n,m,ans;
bool flag;
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=2*n;i++)fa[i]=i;
for(int i=1;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
char c=getchar();
while(c!='L'&&c!='T')c=getchar();
if(flag)continue;
if(c=='L')un(x,y+n),un(y,x+n);
else un(x,y),un(x+n,y+n);
if(find(x)==find(x+n)||find(y)==find(y+n))
{
flag=1;
ans=i-1;
}
}
if(!flag)ans=m;
printf("%d\n",ans);
}
差不多就这样了
如发现有任何错误请联系作者
仅代表DSFZ最低水平