D - Count GCD 容斥,D-点分治分点_牛客练习赛105 ,1516C - Baby Ehab Partitions Again dp

97 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第21天,点击查看活动详情

 D - Count GCD 容斥

b[i]一定要是a[i]的倍数,a[i-1]也一定要是a[i]的倍数,而且gcd(\frac{b[i]}{a[i]},\frac{a[i-1]}{a[i]})=1,这样才会保证最大公约数是a[i]而不是a[i]的倍数,上面那个式子也就是要求1~m/a[i]中有多少是与a[i-1]/a[i]互质的,这个用容斥原理来算就可以了,加一个记忆化就能过了,因为a[i]是递减的,a[i-1]/a[i]的值其实很少

算法数学笔记(1) 容斥原理 - 知乎 (zhihu.com)

CodeTON Round 3 (Div. 1 + Div. 2) D (数学 容斥) - 知乎 (zhihu.com)

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
const int mod=998244353;
const int inf=1e18;
const int N = 4e5+100;
const double eps=1e-8;
int qpow(int a,int b)
{
    int res=1;
    while(b)
    {
        if(b&1) res=res*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return res;
}
int sgn(double x)
{
    if(fabs(x)<eps) return 0;
    else if(x<0) return -1;
    else return 1;
}
int getinv(int a){return qpow(a,mod-2LL);}
vector<int>prime;
void pr(int n)
{
    prime.clear();
    for(int i=2;i*i<=n;i++)
    {
        if(n%i==0)
        {
            prime.push_back(i);
            while(n%i==0) n/=i;
        }
    }
    if(n>1) prime.push_back(n);
}
int t,n,m,a[N],num,sum;
void dfs(int pos,int mul,int len)
{
    if(pos==prime.size())
    {
        if(len)
        {
            if(len&1) sum+=num/mul;
            else sum-=num/mul;
        }
        return;
    }
    dfs(pos+1,mul,len);
    dfs(pos+1,mul*prime[pos],len+1);
}
map<pair<int,int>,int>mp;
int que(int L,int k)
{
    if(mp.count({L,k})) return mp[{L,k}];
    pr(k);
    //cout<<"L="<<L<<" k="<<k<<" siz="<<prime.size()<<endl;
    num=L,sum=0;
    dfs(0,1,0);
    return mp[{L,k}]=(L-sum);
}
signed main()
{
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    //freopen("in.txt","r",stdin);
    cin>>t;
    mp.clear();
    while(t--)
    {
        cin>>n>>m;
        for(int i=1;i<=n;i++) cin>>a[i];
        int ans=1;
        for(int i=2;i<=n;i++)
        {
            if(a[i-1]%a[i]!=0){ans=0;break;}
            ans=ans*que(m/a[i],a[i-1]/a[i])%mod;
            //cout<<ans<<" "<<i<<endl;
        }
        cout<<ans<<endl;
    }
    system("pause");
    return 0;
}

D-点分治分点_牛客练习赛105 (nowcoder.com) 

根据边权排序后一条一条的加边,如果这个点是可以连到s的就是扩展这个点,以这个点扩展的所有点价值都是当前边的w,这一定是最优的,因为是按边权从大到小排的序

牛客练习赛105【出题人题解】 - 知乎 (zhihu.com)

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
const int mod=998244353;
const int inf=1e18;
const int N = 4e5+100;
const double eps=1e-10;
int qpow(int a,int b)
{
    int res=1;
    while(b)
    {
        if(b&1) res=res*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return res;
}
int sgn(double x)
{
    if(fabs(x)<eps) return 0;
    else if(x<0) return -1;
    else return 1;
}
bool integer(double s)
{
    if(abs(round(s)-s)<eps) return 1;
    return 0;
}
int getinv(int a){return qpow(a,mod-2LL);}
int head[N],cnt;
struct Edge
{
    int from,to,w,next;
    bool operator<(const Edge& other) const
    {
        return w>other.w;
    }
}e[N],E[N];
void addedge(int from,int to,int w)
{
    e[++cnt].next=head[from];
    e[cnt].to=to;
    e[cnt].w=w;
    head[from]=cnt;
}
int n,m,s,ans[N],vis[N];
void dfs(int u,int w)
{
    ans[u]=w;vis[u]=1;
    cout<<u<<" "<<w<<endl;
    for(int i=head[u];i;i=e[i].next)
    {
        int j=e[i].to;
        if(vis[j]) continue;
        dfs(j,w);
    }
}
signed main()
{
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    //freopen("in.txt","r",stdin);
    cin>>n>>m>>s;
    vis[s]=1;
    for(int i=1;i<=m;i++) cin>>E[i].from>>E[i].to>>E[i].w;
    sort(E+1,E+m+1);
    for(int i=1;i<=n;i++) ans[i]=-1;
    for(int i=1;i<=m;i++)
    {
        auto &[u,v,w,next]=E[i];
        addedge(u,v,w);
        if(vis[u]==1&&vis[v]==0) dfs(v,w);
    }
    for(int i=1;i<=n;i++) cout<<ans[i]<<" ";
    system("pause");
    return 0;
}

1516C - Baby Ehab Partitions Again dp

可以发现如果一开始是可以分成两个子数组的话,最多只要删一个就可以了,那么如何去验证当前数组或者删了一个元素后的数组是否可以呢,背包就可以,但是注意转移的时候只能通过合法的容量来转移,,,

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
const int mod=998244353;
const int inf=1e18;
const int N = 4e5+100;
const double eps=1e-10;
int qpow(int a,int b)
{
    int res=1;
    while(b)
    {
        if(b&1) res=res*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return res;
}
int sgn(double x)
{
    if(fabs(x)<eps) return 0;
    else if(x<0) return -1;
    else return 1;
}
bool integer(double s)
{
    if(abs(round(s)-s)<eps) return 1;
    return 0;
}
int getinv(int a){return qpow(a,mod-2LL);}
int n,a[105],f[N];
signed main()
{
    //ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    //freopen("in.txt","r",stdin);
    cin>>n;
    int sum=0;
    for(int i=1;i<=n;i++) cin>>a[i],sum+=a[i];
    for(int i=1;i<=sum;i++) f[i]=-1;
    if(sum&1)
    {
        cout<<0<<endl;
        system("pause");
        return 0;
    }
    f[0]=0;
    for(int i=1;i<=n;i++)
    for(int j=sum;j>=a[i];j--)
    if(f[j-a[i]]!=-1) f[j]=max(f[j],f[j-a[i]]+a[i]);//不合法是不可以的
    if(f[sum/2]==-1)
    {
        cout<<0<<endl;
        system("pause");
        return 0;
    }
    int ma=0;
    for(int i=1;i<=n;i++)
    {
        if(((sum-a[i])&1))
        {
            cout<<"1\n"<<i<<endl;
            system("pause");
            return 0;
        }
        int res=sum-a[i];
        for(int j=1;j<=sum;j++) f[j]=-1;
        for(int k=1;k<=n;k++)
        {
            if(i==k) continue;
            for(int j=res;j>=a[k];j--)
            if(f[j-a[k]]!=-1) f[j]=max(f[j],f[j-a[k]]+a[k]);
            //注意下标要是k了
        }
        //cout<<f[res/2]<<" "<<i<<" "<<sum<<endl;
        if(f[res/2]==-1)
        {
            cout<<"1\n"<<i<<endl;
            system("pause");
            return 0;
        }
    }
    //cout<<"1\n1\n"<<endl;
    system("pause");
    return 0;
}