暑假第一次模拟赛

取整

场上心路:

开考十五分钟左右写下了以下:

总问卷数已知,现在还给了一些问卷的结果。

所以已知结果的问卷的百分比之和是已知的、确定的。(如果剩下的问卷不再选择这种语言的话)
在分数意义下,每份问卷是什么语言其实无所谓,那么重点显然在四舍五入的机制。
于是我们很容易可以看出:
对于小数位,我们可以贪心地优先补全0.5%以下要舍掉的。越接近0.5%越优先。
那么,当我们完成了“补全”这件事情之后,如果还有剩余问卷,
那么我们可以评估问卷若为新的语言,则还可以增加多少百分比。

之后十分自信觉得这个贪心嘴得很好似乎没什么问题,就算不是正解也能水到一些分。

虽然最后震惊地听qx讲课发现真的是正解qwq

犹豫了一下抱着水分的心态抬手开写,但是一直觉得不可能这么简单。

于是:啊我知道了就像7.10的ABC那样,这题有一堆不可精确计算的除法,aqx不会要卡精度吧ono

遂殚精竭虑优化精度。(100-->2-->*n)

勤勤恳恳兢兢业业企图优化掉每个可能炸我精度让我WA的东西,做高端暴力人。(大雾)

这种精益求精的伟大精神以及极其烂怂的代码能力为我创造了一座屎山以及一个两个多小时的零分。(泣)

场后补题代码:
#include <bits/stdc++.h>
//#define int long long
#define maxn 200005
#define tgjd 1e-8 //提高精度
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
int T,n,m,a[maxn];
int yu,ans,tot;
pair<int,int> v[maxn];//要补全的 已经有的

bool check(int x) {return (double)100.0*x/n-100*x/n>=0.5-tgjd;}

void doit(int x) {for(int i=1;i<=yu;++i) if(check(i+a[x])) {v[++tot]={i,x}; break;} }

void work()
{
     ans=tot=0; a[++m]=0;
     for(int i=1;i<=m;++i) if(!check(a[i])) doit(i);
     sort(v+1,v+1+tot);
     for(int i=1;i<=tot;++i)
     {
         if(v[i].first>yu) break;
         if(!a[v[i].second]) while(v[i].first<=yu) a[++m]=v[i].first, yu-=v[i].first; 
         //直接用yu中的开一门新语言
         else yu-=v[i].first, a[v[i].second]+=v[i].first;
     }
     a[++m]=yu; 
     for(int i=1;i<=m;++i) ans+=(100.0*a[i]/n+0.5);
}

void init()
{
    cin>>T;
    for(int k=1;k<=T;++k)
    {
        cin>>n>>m; yu=n; 
        for(int i=1;i<=m;++i) {cin>>a[i]; yu-=a[i];}
        work();
        cout<<"Case #"<<k<<": "<<ans<<'\n';
    }
}

signed main()
{
     // freopen("round1.in","r",stdin);
     // freopen("out.txt","w",stdout);
     ios::sync_with_stdio(false);
     std::cin.tie(0);
     init();

return 0;

}
一些细节:

1. 关于work函数为什么开头要初始化 a[++m]=0; ?

这个问题想了好久都没懂qwq,其实是因为要考虑如果用剩余的问卷直接建一堆更划算的情况。

因为函数最后还有一个 a[++m]=yu; ,所以一直不太懂为什么前面要有那个东西。

其实捏,就是嘴出来的做法中的这句话!!!

当我们完成了“补全”这件事情之后,如果还有剩余问卷,

那么我们可以评估问卷若为新的语言,则还可以增加多少百分比。

首先先建一堆是first为需要的,second为0 。

然后放进去搜就好啦!sort一下,如果新建给去补其他的更划算那就直接新建。

但是其实这个东西其实并不必须加入sort,在最后把其他堆该补的都补完了之后,把剩下的再搞个while摊派一下就好,效果应该是一样的~

  1. 最好记得要用 #define tgjd 1e-8 然后在比大小的时候给 0.5 减去 tgjd,这样可以有效避免挂精度问题。且好想好写,不会像我赛时的那个一样为了提精度自己乱搞大寄特寄qwq
反思与总结:

哎呀这个xsy就是屑啦。

咳咳,说正经的。首先是发现了自己在码力方面的严 重 不 足 ,太寄了这也qwq

然后捏,就是关于思路。嘴出做法了一定一定一定要想清楚。

Think twice, code once.


打怪兽

场上心路:

没什么说的,就是菜罢了。

题意完全理解错了。乐了。

关于做法……我始终觉得是博弈,且打暴力始终企图递归()

场后补题代码:
#include <bits/stdc++.h>
//#define int long long
#define maxn 1000005
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
int n,x,y,a[maxn];
int bob[maxn],alc[maxn];
priority_queue<int> q;
int now=1,tot;

void init()
{
    cin>>n>>x>>y;
    for(int i=1;i<=n;++i) cin>>a[i];
}

void work()
{
    for(int i=1;i<=n;++i) 
    {
        bob[i]=ceil((double)a[i]/y); 
        alc[i]=ceil((double)(a[i]-(bob[i]-1)*y)/x);
    }
    for(int i=1;i<=n;++i)
    {
        now+=bob[i]; 
        q.push(alc[i]+1); tot+=alc[i]; ++tot;
        while(tot>now) {tot-=q.top(); q.pop();}
    }
    cout<<q.size();
}

signed main()
{
    ios::sync_with_stdio(false);
    std::cin.tie(0);
    init(); work();

    return 0;
}
一些细节:

注意记得写 (double) ?

除此之外想不到什么别的了。就这样吧。

反思与总结:

无。就是菜。


路标

场上心路:

开题一眼反应过来要预处理出来每个点的M值和N值,然后瞪了一会感觉双指针能跑,时间可能会炸但是暴力水点分还是可以的(虽然最后并没有水到qwq

写的时候仔细思考了一下,感觉应该把端点和M或者N捆绑,然后顺着往后搜。

……码力太烂写挂了qwq。(默默爬走。

场后补题代码:
#include <bits/stdc++.h>
//#define int long long
#define maxn 200005
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
int n,d,a,b,M[maxn],N[maxn];
int l=1,r,flm,fln;
map<int,int> vism,visn;
map<pair<int, int>,int> vismn;
int ans,tot,ret1,ret2;

void init()
{
    cin>>n;
    for(int i=1;i<=n;++i) {cin>>d>>a>>b; M[i]=d+a; N[i]=d-b;}
}

void work()
{
    for(r=1;r<=n;++r)
    {
        ++vism[M[r]], ++visn[N[r]], ++vismn[{M[r],N[r]}];
        if(M[r-1]!=M[r]) flm=r-1; if(N[r-1]!=N[r]) fln=r-1;
        while(l<=flm && l<=fln && l<r)
        {
            ret1=vism[M[r]]+visn[N[flm]]-vismn[{M[r],N[flm]}];
            ret2=vism[M[fln]]+visn[N[r]]-vismn[{M[fln],N[r]}];
            if(ret1==r-l+1 || ret2==r-l+1) break;
            --vism[M[l]],--visn[N[l]],--vismn[{M[l],N[l]}]; ++l;
        }
        if(r-l+1==ans) ++tot;
        if(r-l+1>ans) ans=r-l+1,tot=1;
    }
}

void out() {cout<<ans<<" "<<tot<<'\n';}

signed main()
{
    ios::sync_with_stdio(false);
    std::cin.tie(0);
    init(); work(); out();

    return 0;
}
一些细节:

fln 和 flm 只能放在循环外初始化为 r-1,但是不能为了方便初始化为 N[r-1] 和 M[r-1]。否则循环中 vis 的变化无法影响 ret1 和 ret2,导致结果错误。

反思与总结:

注意细节。

看到计数题要想想能不能开桶、能不能用map维护。(真的好使!)


[砍树](user_login - 33OJ)

场上心路:

场上没什么心路,看见期望就跑路。(大雾)

场后补题代码:
#include <bits/stdc++.h>
#define int long long
#define maxn 5005
#define mod 998244353
#define il inline
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
int n,u,v;
basic_string<int> e[maxn];
int siz[maxn];
int inv,invnow,fact=1;
int dp[maxn][maxn][3];
int tp[maxn][3];
int ans;

void add(int &x,int y) {y%=mod; x=(x+y)%mod;}

void dfs(int now,int fa)
{
    dp[now][1][0]=dp[now][0][1]=dp[now][0][2]=1; ++siz[now];
    for(int to:e[now])
    {
        if(to!=fa)
        {
            dfs(to,now);
            int sum=siz[now]+siz[to]+1; 
            for(int i=0;i<=sum;++i) tp[i][0]=tp[i][1]=tp[i][2]=0; //独立成链
            for(int i=siz[now];i>=0;--i) for(int j=siz[to];j>=0;--j)
            {
                add(tp[i+j][2],dp[now][i][2]*dp[to][j][0]);

                add(tp[i+j][1],dp[now][i][2]*dp[to][j][1]);
                add(tp[i+j][1],dp[now][i][1]*dp[to][j][0]);

                add(tp[i+j+1][0],dp[now][i][1]*dp[to][j][1]);
                add(tp[i+j][0],dp[now][i][0]*dp[to][j][0]);
            }
            for(int i=0;i<=sum;++i) 
                dp[now][i][0]=tp[i][0],dp[now][i][1]=tp[i][1],dp[now][i][2]=tp[i][2];
            siz[now]+=siz[to];
        }
    }
    for(int i=0;i<=siz[now];++i) add(dp[now][i][0],dp[now][i][2]); //一直没连。
}

il int qpow(int x,int t)
{
    int ret=1;
    while(t)
    {
        if(t&1) ret=ret*x%mod;
        x=x*x%mod; t>>=1;
    }
    return ret;
}

il void work()
{
    inv=qpow(n*(n+1)/2%mod,mod-2); invnow=inv;
    dfs(1,1);
    for(int i=1;i<=n;++i)
    {
        fact=fact*i%mod; 
        int now=dp[1][i][0];
        now=now*fact%mod; now=now*invnow%mod;
        ans=(ans+now)%mod;
        invnow=invnow*inv%mod;
    }
    ++ans; cout<<ans;
}

il void init()
{
    cin>>n;
    for(int i=1;i<n;++i) {cin>>u>>v; e[u]+=v; e[v]+=u;}
}

signed main()
{
    ios::sync_with_stdio(false);
    std::cin.tie(0);
    init(); work();

    return 0;
}
一些细节:

各种麻烦。取模要小心。中间转移的时候眼神一定要好,脑子要清楚。

顺便附上szp的题解,写得很清晰。

反思与总结:

学 OI 就是图一乐,不如赶紧找个电子厂上班去。

这种题啊,第一次见叫神仙题,第二次见叫套路题,第三次就叫傻逼题。

第一次不会做实属正常,第二次不会做那是你菜,第三次要是再想不出来,那你简直菜得扣脚。

——某神仙教练aqx

仔细分析,看到期望不要害怕,试着化一下算一下。

推dp要训练培养转移的思维,以及熟练掌握消去一维的方法。over.

posted @ 2022-07-13 23:04  筱星Shea  阅读(45)  评论(0编辑  收藏  举报