洛咕11月月赛部分题解 By cellur925

听说是你谷史上最水月赛?我不听我最菜

T1:终于结束的起点 月天歌名好评

给你一个模数 \(M\),请你求出最小的

\(n > 0\),使得\(fib(n)\) \(mod\) \(m=0\),\(fib(n+1)\) \(mod\) \(m=1\)。

数学题,开始还想打表验证下,但是我不会告诉你我打表的时候没有很及时地取膜,然后中间有结果溢出,耽误了很长时间,企图找了很久规律。结果发现暴力就能过。hhh。

这个故事告诉我们要及时取膜

#include<cstdio>
#include<algorithm>

using namespace std;
typedef long long ll;

ll m;
ll f[7000001];

int main()
{
    scanf("%lld",&m);
    f[0]=0;f[1]=1;
    for(ll i=2;;i++)
    {
        (f[i]=f[i-1]+f[i-2])%=m;
        if(f[i-1]==0&&f[i]==1)
        {
            printf("%lld\n",i-1);
            return 0;
        }
    }
    return 0;
}

T2:跳跳!

对于\(20\)%的数据,直接全排列枚举。

对于\(50\)%的数据,开始还想状压一下,结果交后发现超时,是我自己傻了!!企图用\(O(2^n*n*2)\)过\(n<=20\)!!!其实是四亿hhh。

本来还觉得自己想不出正解了,但是\(Mathison\)巨佬说他\(O(n)\)写出了...然后自己又冷静分析了下,发现是个非常简单的贪心==。

可以重构一个序列:先对原序列排序,每次先选择剩余序列中的最大值,再选最小值直到原序列为空。这个序列就是最优排列。

最后交的时候求稳分别交了全排列/状压/yy的正解,结果状压被卡T掉了30分:(。可能脸太黑了

这个故事告诉我们,一定算好时间复杂度

#include<cstdio>
#include<algorithm>

using namespace std;
typedef long long ll;

int n,fake,tot;
ll ans,f[1200000][25];
int h[1000],a[100],b[100];
int seq[1000],tmp[1000];

void review()
{
    ll qwq=0;
    for(int i=1;i<=n;i++)
        qwq+=1ll*(h[a[i-1]]-h[a[i]])*(h[a[i-1]]-h[a[i]]);
    ans=max(ans,qwq);
}

bool cmp(int a,int b)
{
    return a>b;
}

void dfs(int x)
{
    if(x==n+1)
    {
        review();
        return ;
    }
    for(int i=1;i<=n;i++)
        if(!b[i])
        {
            b[i]=1;
            a[x]=i;
            dfs(x+1);
            b[i]=0;
            a[x]=0;
        }
}

int main()
{
    scanf("%d",&n);
    if(n<=10)
    {
        for(int i=1;i<=n;i++) scanf("%d",&h[i]);
        dfs(1);
        printf("%lld\n",ans);
        return 0;
    }
/*  if(n<=20)
    {
        for(int i=0;i<n;i++) scanf("%d",&h[i]);
        fake=(1<<n)-1;
        for(int i=0;i<n;i++)
            f[(1<<i)][i]=1ll*h[i]*h[i];
        for(int i=0;i<=fake;i++)
            for(int j=0;j<n;j++)//枚举当前在哪
            {
                if(!(i&(1<<j))) continue;
                for(int k=0;k<n;k++)//枚举上一步在哪
                {
                    if(k==j||!(i&(1<<k))) continue;
                    f[i][j]=max(f[i][j],f[i^(1<<j)][k]+1ll*(h[j]-h[k])*(h[j]-h[k]));
                }
            }
        for(int i=0;i<n;i++) ans=max(ans,f[fake][i]);
        printf("%lld\n",ans);
        return 0;
    }*/
    else
    {
        for(int i=1;i<=n;i++) scanf("%d",&seq[i]);
        seq[++n]=0;
        sort(seq+1,seq+1+n,cmp);
        int i=1,j=n;
        while(tot<n)
        {
            tmp[++tot]=seq[j],j--;
            tmp[++tot]=seq[i],i++;
        }
        for(int i=2;i<=n;i++)
            ans+=1ll*(tmp[i]-tmp[i-1])*(tmp[i]-tmp[i-1]);
        printf("%lld\n",ans);
    }
    return 0;
}

T3:咕咕咕

\(n<=20\)的数据范围让我从头到尾都以为是状压,并在这个思路上死磕了很久...最后手码前三个点搞到了30分hhh。

其实是可以想出一个\(O(4^n)\)的状压暴力的:枚举子集。首先我们确定对于状态\(i\),它的子集\(j\)一定在\(0\)~\(i\)中,若\(i\)&\(j\)==\(j\),那么可以确定\(j\)是\(i\)的子集。

没有写=w=,下面是lwz dalao的程序==

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#define MAXN 2147483647
#define ll long long

using namespace std;
const int mod=998244353;
int n,m;
ll sum;
int a[2001000];
ll f[2001000],p[20010000];
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        char s[30];
        scanf("%s",s+1);
        int ret=0;
        for(int j=1;j<=n;j++)
            ret+=(s[j]-‘0‘)*(1<<n-j);
        int x;
        scanf("%d",&x);
        a[ret]=x%mod;
    }
    f[0]=a[0];//f记录到状态的花费
    p[0]=1;//p记录此状态出现的次数
    for(int i=1;i<(1<<n);i++)
    {
        for(int j=0;j<i;j++)
            if((i&j)==j){
                p[i]+=p[j];//记录自己的次数
                (f[i]+=f[j])%=mod;//加上子集的花费
            }
        (f[i]+=a[i]%mod*p[i]%mod)%=mod;//加上自己
    }
    printf("%lld\n",f[(1<<n)-1]%mod);
    return 0;
}

正解其实是..数学题?我们完全抛开状压dp的限制,考虑最后的答案其实是可以统计每个带权状态所有可能出现的次数,再乘上最后的权值就行了。

而如何算出现的次数?考虑乘法原理:把这些状态看成中间状态,分别算从0到它的次数、它到满状态的次数,二者相乘。

"具有相同1/0个数的状态出现次数一样”,开始并不能发现这个性质:(。其实出现01的位置并不重要重要的是数量,所以我们可以预处理出每个方案的出现次数。

设\(f[i]\)为填\(i\)个1的方案数,设最后一次填了\(j\)个,那么\(f[i]+=f[i-j]*C(i,j)\)。其中\(j\)从1到\(i\)循环。

这个故事告诉我们,不要一条路走到黑,有时你觉得十分正确的算法方向也可能不是正解,要灵活啊\(qwq\)。而且要多膜膜,没坏处的。

#include<cstdio>
#include<algorithm>
#include<cstring>

using namespace std;
typedef long long ll;
ll moder=998244353;

int n,m;
char op[100];
ll val,ans,C[50][50],f[50];

void prework()
{
    C[0][0]=1;C[1][1]=1;
    for(int i=1;i<=20;i++) C[i][0]=1,C[i][i]=1;
    for(int i=1;i<=20;i++)
        for(int j=1;j<=i;j++)
            (C[i][j]=C[i-1][j]+C[i-1][j-1])%=moder;
    f[0]=1;
    for(int i=1;i<=20;i++)
        for(int j=1;j<=i;j++)
            (f[i]=f[i]+f[i-j]*C[i][j])%=moder;
}

int main()
{
    scanf("%d%d",&n,&m);
    prework();
    for(int i=1;i<=m;i++)
    {
        scanf("%s",op);
        int cnt=0;
        for(int j=0;j<n;j++)
            if(op[j]==‘1‘) cnt++;
        scanf("%lld",&val);
        (ans+=val*f[cnt]%moder*f[n-cnt]%moder)%=moder;
    }
    printf("%lld\n",ans);
    return 0;
}

T4:不围棋

本想搞到10分的,我输出的是1 1 -1 -1;其实应该直接输出-1 -1.因为放到\((1,1)\)后,自己就没有“气”了。嘤。

原文地址:https://www.cnblogs.com/nopartyfoucaodong/p/9906045.html

时间: 2024-09-30 18:35:56

洛咕11月月赛部分题解 By cellur925的相关文章

csu-2018年11月月赛Round2-div2题解

csu-2018年11月月赛Round2-div2题解 A(2193):昆虫繁殖 Description 科学家在热带森林中发现了一种特殊的昆虫,这种昆虫的繁殖能力很强.每对成虫过x个月产y对卵,每对卵要过两个月长成成虫.假设每个成虫不死,第一个月只有一对成虫,且卵长成成虫后的第一个月不产卵(过X个月产卵),问过Z个月以后,共有成虫多少对?0=<X<=20,1<=Y<=20,X=<Z<=50 Input 单组数据 x,y,z的数值 Output 过Z个月以后,共有成虫对

csu-2018年11月月赛Round2-div1题解

csu-2018年11月月赛Round2-div1题解 A(2191):Wells的积木游戏 Description Wells有一堆N个积木,标号1~N,每个标号只出现一次 由于Wells是手残党,所以每次只能取出一块积木放在积木顶层 现在Wells想知道至少需要操作几次可以把积木堆成从顶至底标号升序 不论什么都很菜的Wells显然不知道怎么做 所以作为人生赢家的你义不容辞的决定帮助可怜的Wells Input 第一行一个正整数N 接下来N行,从顶至底描述每块积木的标号 Output 输出一行

洛谷11月月赛round.1

太感动了#2 thwfhk 240 (801ms) 100 100 40 又一张明信片,话说10月的怎么还没收到 P2246 SAC#1 - Hello World(升级版) 题目背景 一天,智障的pipapi正在看某辣鸡讲义学程序设计. 题目描述 在讲义的某一面,他看见了一篇文章.这篇文章由英文字母(大小写均有).数字.和空白字符(制表/空格/回车)构成. pipapi想起了他最近刚刚学会写的Hello World程序.他非常好奇,这篇文章中,“HelloWorld”作为子序列到底出现过多少次

FOJ 11月月赛题解

抽空在vjudge上做了这套题.剩下FZU 2208数论题不会. FZU 2205 这是个想法题,每次可以在上一次基础上加上边数/2的新边. 1 #include <iostream> 2 #include <vector> 3 #include <algorithm> 4 #include <string> 5 #include <string.h> 6 #include <stdio.h> 7 #include <queue

洛谷10月月赛Round.1| P3400 仓鼠窝[单调栈]

题目描述 萌萌哒的Created equal是一只小仓鼠,小仓鼠自然有仓鼠窝啦. 仓鼠窝是一个由n*m个格子组成的行数为n.列数为m的矩阵.小仓鼠现在想要知道,这个矩阵中有多少个子矩阵!(实际上就是有多少个子长方形嘛.)比如说有一个2*3的矩阵,那么1*1的子矩阵有6个,1*2的子矩阵有4个,1*3的子矩阵有2个,2*1的子矩阵有3个,2*2的子矩阵有2个,2*3的子矩阵有1个,所以子矩阵共有6+4+2+3+2+1=18个. 可是仓鼠窝中有的格子被破坏了.现在小仓鼠想要知道,有多少个内部不含被破

洛谷-语文成绩-[有奖]洛谷5月月赛:kkksc03的三大神器

题目背景 Background语文考试结束了,成绩还是一如既往地有问题. 题目描述 Description语文老师总是写错成绩,所以当她修改成绩的时候,总是累得不行.她总是要一遍遍地给某些同学增加分数,又要注意最低分是多少.你能帮帮她吗? //这又跟神器有什么关系呢?神说:呵呵. //因为n和p的范围比较大 建议C++选手使用scanf读入.//同时建议写读入优化....//最后一个点,亲测pas读入800+ms,c/C++的scanf 1200+ms,所以这个点的时限改为2s 输入输出格式 I

11月月赛

A-HDU1087   http://acm.hdu.edu.cn/showproblem.php?pid=1087 相当于从数组a中找一个最优序列,我们设要找的序列为A,序列A满足两个条件,一是A中的元素大小满足单调递增,二是A中所有元素的和最大,输出这个最大值. 令f[i]表示以第i个元素结尾的A序列的最大值,则有f[i]=MAX{ f[j]+a[i]  |  j<i }   ans=MAX{f[i]}; 1 *#include<bits/stdc++.h> 2 using name

code+11月月赛

T1 SB题 1 #include<cstdio> 2 #include<cstring> 3 #include<cmath> 4 #include<ctime> 5 #include<iostream> 6 #include<algorithm> 7 #include<queue> 8 #include<set> 9 #define inf (0x7fffffff) 10 #define l(a) ((a)&

FZU 11月月赛D题:双向搜索+二分

/* 双向搜索感觉是个不错的技巧啊 */ 题目大意: 有n的物品(n<=30),平均(两个人得到的物品差不能大于1)分给两个人,每个物品在每个人心目中的价值分别为(vi,wi) 问两人心目中的价值差最小是多少. 分析: 直接暴搜目测会超时 想到先搜索前一半,用数组a[0][i]保存第一个人在前半段取 i 个物品两个人的差的所有情况: 再搜索后一半保存两个人的差的相反数,用相同的规则保存在a[1][]中. 要想总差最小只需要 a[0][i]-a[1][num-i] (num=n/2或 n/2+1)