2023年暑假集训总结/6.28

6-28

T1二分哥

  Um_nik 是一个很强的 Codeforces 选手。

  Petr 也是一个很强的 Codeforces 选手。

  对于某个排列,我们定义一次“交换”为选择两个不同的位置将他们交换。他们两个人各自拿到一个长度为 n 的初始升序的排列,随后 Um_nik 会将这个排

  列“交换”7n + 1 次,而 Petr 会将这个排列“交换”3n 次。

  给你一个进行若干次交换后的排列,你需要判断这个排列是由谁进行操作得到的。

  O(n)构造出一个能使序列还原的方案,将操作数的奇偶性与3x和7x+1比较即可。

  std:

#include<bits/stdc++.h>
using namespace std;
int t,n,op,a[2500005],p[2500005],cnt;
#include <vector>
struct Generator{
    std::vector<int> num;
    bool vis[2500005];
    int cnt[2500005];
    typedef unsigned int ui;
    ui a,b,x;ui getnum()
    { 
        return x=a*x+b;
    }
    std::vector<int> get_permutation(int n,ui a_,ui b_)
    {
        a=a_;
        b=b_;
        x=0;
        std::vector<int> per;
        for(int i=1;i<=n;i++)
        {
            per.emplace_back(getnum()%n+1);
            cnt[per.back()]++;
        }
        for(int i=1;i<=n;i++)
        {
            cnt[i]+=cnt[i-1];
        }
        for(int i=0;i<n;i++) per[i]=cnt[per[i]]--;
        for(int i=1;i<=n;i++) cnt[i]=0;
        return per;
    }
}gen;
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin>>t;
    while(t--)
    {
        cnt=0;
        cin>>n>>op;
        if(op==0)
        {
            for(int i=1;i<=n;i++)
            {
                cin>>a[i];
                p[a[i]]=i;
            }
        }
        if(op==1)
        {
            unsigned sda,sdb;
            cin>>sda>>sdb;
            auto t=gen.get_permutation(n,sda,sdb);
            for(int i=1;i<=n;i++)
            {
                a[i]=t[i-1];
                p[a[i]]=i;
            }
        }
        for(int i=1;i<=n;i++)
        {
            if(a[i]!=i)
            {
                int t=a[i];
                swap(a[i],a[p[i]]);
                swap(p[i],p[t]);
                cnt++;
            }
        }
        if((cnt&1)==((n*3)&1)) cout<<"Petr\n";
        else cout<<"Um_nik\n";
    }
}

T2匹配

 哈娜有一棵树。哈娜喜欢一些稳定的匹配,因此哈娜希望这棵树的最大匹配唯一。显然有些情况下原来的树可能不满足这个性质,因此哈娜想在树上断开几条边,使得断边之后的树(其实应该叫森林了)最大匹配唯一。你需要帮助哈娜求出有多少种删边方案使得最大匹配唯一。称两种删边方案不同,当且仅当存在某条边,他在两种方案中一个被保留,一个被删除。答案对 998244353 取模

  优化⼀下状态设计,记分别表⽰以 为⼦树的答案且 是孤⽴点/与某个⼉⼦匹配/与⽗亲匹配的⽅案数。分别求出转移(转移详见代码),最后答案就是froot,0+froot,1。

  std:

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod=998244353;
int n,nxt[600005],head[300005],go[600005],k,f[300005][3];
void add(int u,int v)
{
    nxt[++k]=head[u];
    head[u]=k;
    go[k]=v;
}
int qpow(int a,int b)
{
    if(b==0) return 1;
    int g=qpow(a,b/2);
    g=g*g%mod;
    if(b&1) g=g*a%mod;
    return g;
}
int inv(int x)
{
    return qpow(x,mod-2);
}
void dfs(int x,int fa)
{
    f[x][0]=f[x][2]=1;
    for(int i=head[x];i;i=nxt[i])
    {
        int g=go[i];
        if(g==fa) continue ;
        dfs(g,x);
        f[x][0]*=f[g][0]+f[g][1];
        f[x][1]+=f[g][2]*inv(f[g][0]+2*f[g][1]);
        f[x][2]*=f[g][0]+2*f[g][1];
        f[x][0]%=mod;
        f[x][1]%=mod;
        f[x][2]%=mod;
    }
    f[x][1]*=f[x][2];
    f[x][1]%=mod;
}
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin>>n;
    for(int i=1;i<n;i++)
    {
        int u,v;
        cin>>u>>v;
        add(u,v);
        add(v,u);
    }
    dfs(1,0);
    cout<<(f[1][0]+f[1][1])%mod;
}

T3质数

  给你一个正整数 n,你需要找出一个区间 [l, r],满足 [l, r] 内所有质数的和等于 n,

  若无解,请输出 NIE。

  本题采用了 Special Judge,你的答案正确当且仅当:

    • 若输入无解,你的程序只输出了 NIE。

    • 若输入有解,你的程序输出了一个区间 [l, r],且区间内所有质数的和等于 n。

  考试的时候用了20分钟左右打了个暴力拿了30分,正解是先考虑筛法求出L以内的质数,但当L过大时,筛法到这⾥已经⾛到死路了,过⼤的 不⽀持我们这样⼲。观察到⼀个性质,假如我们筛法的上限是 ,那么如果在 以内没有找到⼀个合法的⽅案的话,最终答案的右端点⼀定⼤于 。继续推⼀下你可以发现,如果答案右端点⼤于 ,那么答案中质数的数量最多只有个!我们之前在算法⼀中的优化就可以上场了, 先枚举答案中的质数个数 ,在 附近暴⼒双指针。由于 以内的质数个数是级别的,于是我们粗略估计我们暴⼒的范围⼤约在t ln n左右,当然你也不需要真的⼀个⼀个判素数,利⽤我们提前筛好的⼩质数,去把每次暴⼒的区间内的质数筛出来,这样可以有更快的效率

  (这道题没调出来,也没有别人的代码)

 

posted @ 2023-07-02 20:51  pigeon160  阅读(30)  评论(0编辑  收藏  举报