2020.11.1 校测解题报告
作者:互联网
2020.11.1 校测
十一月份的第一次校测就这么没了qaq。。。
-
得分情况
总分:\(10 pts\)
-
T1 期望得分 100 实际得分 0
-
T2 期望得分 30 实际得分 10
-
T3 期望得分 10 实际得分 0
-
这T1,T2不是水题么??NM为啥会爆炸
T1
铺设道路
-
思路:
这是一个贪心
假设现在有一个坑,但旁边又有一个坑。
你肯定会选择把两个同时减 \(1\) ;
那么小的坑肯定会被大的坑“带着”填掉。
大的坑也会减少 \(a[i]-a[i-1]\) 的深度,可以说是“免费的”;
所以这样贪心是对的;
所以就可以执行 如果 \(a[i]>a[i-1]\) ,计数器 \(sum+=a[i]-a[i-1]\) ;
-
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<stack>
#define ll long long
#define InF 0x7fffffff
#define Max 10e5
#define Min -10e5
using namespace std;
/*==========================================快读*/
int read()
{
int x=0,f=1;
char c=getchar();
while(c<'0'||c>'9')
{
if(c=='-')
f=-1;
c=getchar();
}
while(c>='0'&&c<='9')
{
x=(x<<3)+(x<<1)+(c^48);
c=getchar();
}
return x*f;
}
/*======================================定义变量*/
int n,d[100010],ans;
/*====================================自定义函数*/
/*========================================主程序*/
int main()
{
n=read();
for(int i=1;i<=n;i++)
{
d[i]=read();
if(d[i]>d[i-1])
ans+=d[i]-d[i-1];
}
printf("%d",ans);
return 0;
}
T2
货币系统
-
思路:
定义 \(f[a[i]]\) 表示当前的货币是否可以凑出。
当f=1时证明他不会出现在的货币系统里。
然后对于每个已经确定的不在新系统里的面值将其跳过
剩下的就是没跳过的,把这些的数量给统记下来,赋成true,避免重复统计。
状态转移方程式 \(f[j]=f[j]|f[j-a[i]]\) ,对于每个 \(a[i]\) ,枚举 \(a[i]+1\) 到 \(a[n]\) 的区间,这个区间里的每一个数的 \(f\) 是否为 \(1\) 就是它减去他前面的数后是否为 \(true\)(注意枚举区间下界是 \(a[i]+1\) 不是 \(a[i]\) )
这样到最后不是true的都被加上然后重新赋值成 \(true\) ,所有的数的f都是 \(true\) ,所以就不用而且没法重新扫一遍然后计数了
-
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#define ll long long
using namespace std;
/*====================================快读*/
int read() {
int x=0,f=1;
char c=getchar();
while(c<'0'||c>'9') {
if(c=='-')
f=-1;
c=getchar();
}
while(c>='0'&&c<='9') {
x=(x<<3)+(x<<1)+(c^48);
c=getchar();
}
return x*f;
}
/*======================================定义变量*/
int T;
int n,a[30030];
bool f[30030];
int ans;
/*====================================自定义函数*/
//bool su(int a)
//{
// for(int i=2;i<=sqrt(a);i++)
// if(a%i==0)
// return false;
// return true;
//}
/*========================================主程序*/
int main() {
// freopen("money.in","r",stdin);
// freopen("money.out","w",stdout);
T=read();
while(T--) {
ans=0;
memset(f,false,sizeof(f));
n=read();
for(int i=1; i<=n; i++)
a[i]=read();
sort(a+1,a+n+1);
f[a[1]]=false;
for(int i=1; i<=n; i++)
{
if(f[a[i]]) continue;
ans++;//
f[a[i]]=true;//
for(int j=a[i]+1;j<=a[n];j++) //
f[j]=f[j]|f[j-a[i]];
}
// for(int i=1;i<=n;i++)
// if(!f[a[i]])
printf("%d\n",ans);
}
return 0;
}
T3
赛道修建
-
思路:
你的任务是设计一种赛道修建的方案,使得修建的 m 条赛道中长度最小的赛道长度最大(即 m 条赛道中最短赛道的长度尽可能大)
长度最小的最大?二分?!根据题意寻找使最小的赛道长度最大的决策方案,题目就转化为了在一棵树上寻找 \(m\) 条小于等于 \(k\) 的路径。
二分那个修建的 \(m\) 条赛道中长度最小的赛道的长度 \(k\) (求树上最长路详见 吕sir 的双dfs法和树上DP法),然后判断 。
如何判断?对于每个结点,把所有传上来的值 \(val\) 放进一个 \(multiset\) ,其实这些值对答案有贡献就两种情况:
-
\(val≥k\)
-
\(vala+valb≥k\)
那么第一种情况可以不用放进 \(multiset\) ,直接答案 \(+1\) 就好了。第二种情况就可以对于每一个最小的元素,在 \(multiset\) 中找到第一个 \(≥k\) 的数,将两个数同时删去,最后把剩下最大的值传到那个结点的父亲。
为什么这种解法是正确的,有没有可能对于有些情况直接传最大的数会使答案更大?
当然不会。这个数即使很大也只能对答案贡献加 \(1\) ,在其没传上去的时候可以跟原来结点的值配对,也只能对答案贡献加 \(1\) 。 -
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<stack>
#define ll long long
#define InF 0x7fffffff
#define Max 10e5
#define Min -10e5
using namespace std;
/*==========================================快读*/
inline int read()
{
register int x=0,f=1;
char ch=getchar();
while(!isdigit(ch))
{
if(ch=='-')
f=-1;
ch=getchar();
}
while(isdigit(ch))
{
x=(x<<3)+(x<<1)+ch-'0';
ch=getchar();
}
return (f==1)?x:-x;
}
/*======================================定义变量*/
const int maxn=50000+10;
int n,m,head[maxn],tot,ans,up;
struct node{
int to,next,val;
}e[maxn<<1];
multiset<int> s[maxn];
multiset<int>::iterator it;
/*====================================自定义函数*/
inline void add(int x,int y,int w)
{
e[++tot].to=y;
e[tot].val=w;
e[tot].next=head[x];
head[x]=tot;
}
int dfs(int x,int fa,int k)
{
s[x].clear();
int val;
for(int i=head[x],y;i;i=e[i].next){
y=e[i].to;
if(y==fa)
continue;
val=dfs(y,x,k)+e[i].val;
if(val>=k)
ans++;
else
s[x].insert(val);
}
int Max=0;
while(!s[x].empty())
{
if(s[x].size()==1)
return max(Max,*s[x].begin());
it=s[x].lower_bound(k-*s[x].begin());
if(it==s[x].begin()&&s[x].count(*it)==1)
it++;
if(it==s[x].end())
{
Max=max(Max,*s[x].begin());
s[x].erase(s[x].find(*s[x].begin()));
}
else
{
ans++;
s[x].erase(s[x].find(*it));
s[x].erase(s[x].find(*s[x].begin()));
}
}
return Max;
}
int check(int k)
{
ans=0;
dfs(1,0,k);
if(ans>=m)
return 1;
return 0;
}
int dfs1(int x,int fa)
{
int sum1=0,sum2=0;
for(int i=head[x],y;i;i=e[i].next)
{
y=e[i].to;
if(y==fa)
continue;
sum2=max(sum2,dfs1(y,x)+e[i].val);
if(sum1<sum2)
swap(sum1,sum2);
}
up=max(up,sum1+sum2);
return sum1;
}
/*========================================主程序*/
int main()
{
n=read(),m=read();
int x,y,w;
for(int i=1;i<n;i++)
{
x=read(),y=read(),w=read();
add(x,y,w);
add(y,x,w);
}
dfs1(1,0);
int l=1,r=up,mid;
while(l<r)
{
mid=l+r+1>>1;
if(check(mid))
l=mid;
else
r=mid-1;
}
printf("%d\n",l);
return 0;
}
总结:
十一月第一次校测就这么没了。。。
着实不甘心。
- 查缺补漏了许多
线性 \(DP\) ,树上 \(DP\) ,差分,树上最长路,\(and\) \(so\) \(on\) 。
- 其实 \(T3\) 有好多种办法骗分,可惜时间安排不合理,最后没有时间了。。。
标签:赛道,val,int,校测,while,解题,2020.11,include,define 来源: https://www.cnblogs.com/Frather/p/14003268.html