The 2021 CCPC Weihai Onsite(vp补题记录,A,J,D,G)

Dashboard - The 2021 CCPC Weihai Onsite - CodeforcesCodeforces. Programming competitions and contests, programming communityhttps://codeforces.com/gym/103428

A. Goodbye, Ziyin!

题意:问你在一棵无根树中,选一个点作为根,有多少个点满足被选为根节点后这棵树是二叉树.

思路:首先明确,如果某个点的子节点数量>3那么是不可能成立的.以这个为前提,若是所有点子节点都<=3,那么就是当某个点的子节点数量<=2,该点就可以被作为根节点.否则肯定不是二叉树

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N =1e6+10,mod=998244353;
int h[N],tot=0;
struct node
{
    int to,ne;
}edge[2*N];
void add(int x,int y)
{
    edge[++tot].to=y;
    edge[tot].ne=h[x];
    h[x]=tot;
    return ;
}
void solve()
{
    memset(h,-1,sizeof h);
    int n,x,y;
    cin>>n;
    for(int i=1;i<n;i++)
    {
        cin>>x>>y;
        add(x,y);
        add(y,x);
    }
    int f=0,ans=0;
    for(int i=1;i<=n;i++)
    {
        int num=0;
        for(int j=h[i];j!=-1;j=edge[j].ne)
            num++;
        if(num>3)
            f=1;
        if(num<=2)
            ans++;
    }
    if(f==0)
        cout<<ans<<endl;
    else
        cout<<"0"<<endl;
}
signed main()
{
    cin.tie(0);
    cout.tie(0);
    ios::sync_with_stdio(0);
    solve();
    return 0;
}

J. Circular Billiard Table

题意:如图给你一个入射角度数,反射规则如图二,问你反射几次后回到起点

思路:

 把圆心和入射点连接起来,在吧圆心和下一次反射的点连接起来,经过鸡舍知识可以推导出相当于每次从圆心到球和桌子接触点组成的边境旋转了如上图表示的角度,该角度很容易计算,就是2\ss\beta.所以问题简化成求2\beta和360的最小公倍数.

#include <iostream>
#include <algorithm>
using namespace std;
#define ll long long
#define ull __int128
inline __int128 scan()
{
    __int128 x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-')
            f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
         x=x*10+ch-'0';
         ch=getchar();
    }
    return x*f;
}
void print(__int128 x)
{
    if(x < 0)
    {
        x = -x;
        putchar('-');
    }
    if(x > 9) print(x/10);
    putchar(x%10 + '0');
}
void solve()
{
    ull a,b;
    a = scan(),b = scan();
    ull x = 2 * a,y = 360 * b;
    ull ans = x * y / __gcd(x , y) / x - 1;//注意,会爆longlong
    print(ans);
    puts("");
    return ;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)solve();
    return 0;
}

D. Period

题意:给你一个字符串和q次询问,每次询问输入一个i,问第i个字符被改为'#'之后的贡献,贡献的计算就是计算所有这个字符串的前缀和后缀有多少个互相匹配.那么我们就直接枚举它的前缀和对应迁都的后缀,用字符串哈希来互相比较,再去开一个数组记录一个值:下标的含义是当前缀/后缀匹配的最长长度是多少,我们遍历一遍长度,用字符串哈希来比较该长度时是否两个子串相等,是的话置为1,然后前缀和一遍,这个时间下标为i里面的数组的含义就是长度<=i的时候有多少对前后缀相等.当我们把某个位置的字母改成了#,那么前缀>=这个位置的长度就不成立(因为是从第一个开始的前缀和最后一个结束的后缀).直接ans[i-1]即可.

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int pp1=131,pp2=13331,mod1=1000000007,mod2=998244353;
const int N =1e6+10,mod=998244353;
string p;
int hs1[N],hs2[N],p1[N],p2[N];
int ans[N];
void hash_init()
{
    p1[0]=1,p2[0]=1;
    for(int i=1;i<p.size();i++)
    {
        hs1[i]=(hs1[i-1]*pp1%mod1+p[i])%mod1;
        hs2[i]=(hs2[i-1]*pp2%mod2+p[i])%mod2;
        p1[i]=p1[i-1]*pp1%mod1;
        p2[i]=p2[i-1]*pp2%mod2;
    }
    return;
}
int gethash1(int l,int r)
{
    return (hs1[r]-hs1[l-1]*p1[r-l+1]%mod1+mod1)%mod1;
}
int gethash2(int l,int r)
{
    return (hs2[r]-hs2[l-1]*p2[r-l+1]%mod2+mod2)%mod2;
}
void solve()
{
    int n,x;
    cin>>p;
    p=" "+p;
    hash_init();
    for(int len=1;len<p.size()-1;len++)
    {
        int l1=1,r2=p.size()-1;
        int r1=l1+len-1,l2=r2-len+1;
        if(gethash1(l1,r1)==gethash1(l2,r2)&&gethash2(l1,r1)==gethash2(l2,r2))
            ans[len]=1;
        ans[len]+=ans[len-1];
    }
    cin>>n;
    while(n--)
    {
        cin>>x;
        int ll=x-1,rr=p.size()-1-x;
        cout<<min(ans[ll],ans[rr])<<'\n';
    }
}
signed main()
{
    cin.tie(0);
    cout.tie(0);
    ios::sync_with_stdio(0);
    solve();
    return 0;
}

G. Shinyruo and KFC

 题意:有n种食物,每种有ai个,食物总数不会超过1e5.我们把所有事物全部分发完,分给k个队伍.每个队伍一种食物最多得到一个,计算一下k=1,2,3...m的方案数.

思路:一开始我们直接确认,如果某一种食品数比队伍数多,因为要分完并且每队每种食物至多拿一个,那么这样肯定不合法,输出0.也就是说,如果数量最多的某种食物的数量要是大于人数直接输出0.

然后就是直接计算c(i,num[j]),这里的i是指k第几次询问,num[j]则是遍历m个食物每一个食物有多少个,这个的含义就是有num[j]个食物分给i个队伍的方法,根据乘法原理

\prod_{j=1}^{k}c(i,num[j])就是每一次询问为i(1<=i<=k)的方案数.当然这样子进行计算,超时了,就想到优化,既然物品总数只有1e5,那么如果每一种物品数量都不一样,那么最坏每种队伍的数量情况也只需要计算sqrt(n)次,如果包含相同的话就用vector记录下所有不同的种类的数量,并且用一个数组记录他们出现了多少次.如果出现多次只需要用一个快速幂就行.(假设有3次出现,计算3个c(i,num[j])的乘积,也就是快速幂即可).时间复杂度为O(m*sqrt(n))

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N =1e5+10,mod=998244353;
int n,m;
int fact[N],infact[N];
int qmi(int x,int y)
{
    int res=1;
    while(y)
    {
        if(y&1)
            res=res*x%mod;
        x=x*x%mod;
        y>>=1;
    }
    return res;
}
void init()
{
    fact[0]=infact[0]=1;
    for(int i=1;i<N;i++)
    {
        fact[i]=fact[i-1]*i%mod;
        infact[i]=infact[i-1]*qmi(i,mod-2)%mod;
    }
}
int C(int a,int b)
{
    return fact[a]*infact[b]%mod*infact[a-b]%mod;
}
vector<int>goods;
int sum[N];
void solve()
{
    init();
    int maxx=0,x;
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        cin>>x;
        if(!sum[x])
        {
            maxx=max(x,maxx);
            goods.push_back(x);
        }
            sum[x]++;
    }
    for(int i=1;i<=m;i++)
    {
        int ans=1;
        if(i<maxx)
        {
            cout<<"0"<<endl;
            continue;
        }
        for(int j=0;j<goods.size();j++)
            ans=ans*qmi(C(i,goods[j]),sum[goods[j]])%mod;
        cout<<ans<<endl;
    }
    return ;
}
signed main()
{
    cin.tie(0);
    cout.tie(0);
    ios::sync_with_stdio(0);
    solve();
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值