NOIP 模拟15

20+25+0+100 寄了,前仨题每道题都只会了一半也是挺厉害的。

A.数字变换

每次操作后 \((a+b)\bmod p\) 的值不变,所以可以先判断 \((a+b)\bmod p\) 是否等于 \((c+d)\bmod p\),不等的话一定无解。

然后就只需要考虑 \(a\),当 \(a=c\) 时就找到了答案。每次可以将 \(a\gets 2a\bmod p\) 或者 \(a\gets (a-b)\bmod p\),后者相当于 \((2*a-(a+b))\bmod p\)

所以相当于操作 \(i\) 次后,\(a\) 可以变为 \((2^ia-x(a-b))\bmod p,x\in[0,2^i-1]\),因为你在第 \(j\) 次选择操作二最后会减掉 \(2^{i-j}\)\(k\),所以相当于每个二进制位都可以分开考虑,所以 \(x\in[0,2^i-1]\)

使 \(2^ia-x(a-b)\equiv c\pmod p\)\(x(a-b+p)\equiv 2^ia-c\pmod p\),枚举 \(i\) 求解不定方程,当最小正整数 \(x<2^i\)\(i\) 就是合法解,当 \(p\leq 2^i\) 次方时一定有解,复杂度 \(O(q\log p)\)

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<unordered_map>
#include<utility>
#include<queue>
#define int long long
using namespace std;
int p,T;
typedef pair<int,int> P;
unordered_map <int,int> h;
int exgcd(int a,int b,int &x,int &y)
{
    if(!b){x=1,y=0;return a;}
    int d=exgcd(b,a%b,y,x);
    y-=(a/b)*x;return d;
}
main()
{
    freopen("a.in","r",stdin);
    freopen("a.out","w",stdout);
    cin.tie(0),cout.tie(0);
    ios::sync_with_stdio(0);
    cin>>p>>T;
    while(T--)
    {
        int a,b,c,d,k;cin>>a>>b>>c>>d;k=(a+b)%p;
        if((c+d)%p!=(a+b)%p){cout<<"-1\n";continue;}
        if(a==c){cout<<"0\n";continue;}
        for(int i=1;;++i)
        {
            a=a*2%p;int now=(a-c+p)%p;
            int x,y;int d=exgcd(k,p,x,y);
            if(now%d) continue;x=(x*now%p+p)%p;
            if(x<(1<<i)){cout<<i<<'\n';break;}
        }
    }
    return 0;
}

B.均分财产

数据随机,所以直接前 \(n-25\) 个数依次考虑,当 \(sum\leq 0\) 时放到第一个集合中,使 \(sum\gets sum+a_i\)\(sum>0\) 反之。这样维护出来差值。

然后枚举后 \(25\) 个数放到哪个集合中,最后使差值变为 \(0\),由于数据随机所以大概率是可以的,但是我跑的时候需要 random_shuffle。找到解直接跳出,卡时输出 -1

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<time.h>
using namespace std;
const int MAXN=2e5+10;
int n,k,a[MAXN],c[MAXN],pos[MAXN];long long now;
void dfs(int x,long long sum,int k)
{
    if(clock()*1.0/CLOCKS_PER_SEC>0.98) cout<<"-1\n",exit(0);
    if(x>min(n,25))
    {
        if(!sum)
        {
            int cnta=0,cntb=0;
            for(int i=1;i<=n;++i)
            {
                if(c[i]==1) ++cnta;
                if(c[i]==2) ++cntb;
            }
            cout<<cnta<<' ';for(int i=1;i<=n;++i) if(c[i]==1) cout<<pos[i]<<' ';cout<<'\n';
            cout<<cntb<<' ';for(int i=1;i<=n;++i) if(c[i]==2) cout<<pos[i]<<' ';cout<<'\n';
            exit(0);
        }
        return ;
    }
    if(k) c[x]=0,dfs(x+1,sum,k-1);
    c[x]=1,dfs(x+1,sum+a[pos[x]],k);
    c[x]=2,dfs(x+1,sum-a[pos[x]],k);
    return ;
}
int main()
{
    freopen("b.in","r",stdin);
    freopen("b.out","w",stdout);
    cin.tie(0),cout.tie(0);
    ios::sync_with_stdio(0);
    cin>>n>>k;for(int i=1;i<=n;++i) cin>>a[i],pos[i]=i;
    srand(time(0)),rand();random_shuffle(pos+1,pos+1+n);
    if(n<=25) dfs(1,0,k),cout<<"-1\n",exit(0);
    for(int i=26;i<=n;++i)
    {
        if(now<=0) c[i]=1,now+=a[pos[i]];
        else c[i]=2,now-=a[pos[i]];
    }
    dfs(1,now,25),cout<<"-1\n",exit(0);
}

C.查询工资

感觉情况的考虑很复杂,自己不会比题解说的清楚了,不写了。

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
const int MAXN=8e5+10;
int T,n,k,fa[MAXN],siz[MAXN],f[MAXN];
vector <int> v[MAXN];
inline void clear()
{
    for(int i=1;i<=n;++i) v[i].clear();
    return ;
}
void dfs(int x,int fa=0)
{
    siz[x]=1,f[x]=0;
    int now=0;bool flag=false;
    for(int y:v[x])
    {
        if(y==fa) continue;
        dfs(y,x);siz[x]+=siz[y],f[x]+=f[y];
        if(siz[y]>=k+1) now=max(now,f[y]+1);
        if(!f[y]&&siz[y]>=2) flag=true;
    }
    flag&=(v[x].size()>=k);
    f[x]=max(f[x]+flag,now);return ;
}
inline void work()
{
    cin>>n>>k;
    for(int i=2;i<=n;++i)
        cin>>fa[i],v[fa[i]].push_back(i);
    dfs(1);cout<<f[1]<<'\n';return ;
}
int main()
{
    freopen("c.in","r",stdin);
    freopen("c.out","w",stdout);
    cin.tie(0),cout.tie(0);
    ios::sync_with_stdio(0);
    cin>>T;
    while(T--) work(),clear();
    return 0;
}

D.多项式题

\(f_i\) 为在 \(i\) 处断开,\(1\sim i\) 所有情况的乘积的和,\(S(i,j)\) 表示 \(i\)\(j\) 组成的数。

有转移:

\[\begin{aligned}f_i &=\sum\limits_{j=0}^{i-1} f_j\times S(j+1,i) \\&= \sum\limits_{j=0}^{i-1} f_j\times (S(j+1,i-1)*10+a_i) \\&=10\times \sum\limits_{j=0}^{i-2} f_j\times S(j+1,i-1)+a_i\times\sum\limits_{j=0}^{i-1} f_j \\&= 10\times f_{i-1}+a_i\times\sum\limits_{j=0}^{i-1} f_j \end{aligned} \]

前缀和优化,O(n)。

#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN=2e5+10,MOD=998244353;
int n;long long f[MAXN],a[MAXN],s[MAXN];
int main()
{
    freopen("d.in","r",stdin);
    freopen("d.out","w",stdout);
    cin.tie(0),cout.tie(0);
    ios::sync_with_stdio(0);
    cin>>n;s[0]=1;
    for(int i=1;i<=n;++i)
    {
        char ch;cin>>ch;a[i]=ch-48;
        f[i]=(f[i-1]*10%MOD+s[i-1]*a[i]%MOD)%MOD;
        s[i]=(s[i-1]+f[i])%MOD;
    }
    cout<<f[n]<<'\n';return 0;
}
posted @ 2023-11-09 17:56  int_R  阅读(52)  评论(0编辑  收藏  举报