pb0207考试

20210620总结

T1:

题意:

一共有 \(n\) 个数,这些数可以组成 \(2^n-1\) 个组合,在全部组合中,我们在其中挑选不超过 \(m\) 个数,问总共有多少种组合。

思路:

这个题一开始是枚举组合的情况,但是这样有点难算,我们因此转换思路,枚举数字。

例如 \(A,B,C\) 我们希望出现 \(A\) ,那么就有 \(A\) , \(AB\) , \(AC\) , \(ABC\) 四种情况成立。

我们推导一下,对于出现的数字,我们可以表示成 $$\sum^m_{i=1} C^i_n$$

因为这个数字必须出现,那么其他数字就可以随机排列,一共有 \(2^{n-i}\) 个( \(i\) 是出现的数字个数)。

就这样,公式推导成 $$\sum^m_{i=1} C^i_n * 2^{n-i}$$

(本来这个公式十分优美,但是我考试的时候想多了用组合数去算......只得了30分)

我们可以预处理\(\sum^m_{i=1}C^i_n\)\(C_n^1=n,C_n^2=n*(n-1)/2....\) 以此类推,算出从 \(1-m\) 的阶乘的逆元,然后乘积即可,式子为:

inv[0]=inv[1]=1;
for(int i=2;i<=maxn;i++) inv[i]=(mod-mod/i)*inv[mod%i]%mod;

因此每一项的答案就是: $$ans=(ans+inv[i] \times mul(n \times (n-1) \times...(n-i+1)))%mod$$

代码就不放了。

T2:

题意:

一共有 \(n\) 个物品,每个物品有重量。有几个容量为 \(k\) 的背包,求需要的背包数量-1。

思路:

一眼背包\(dp\) ,注意从大到小优化:

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
int w[25];
ll a[25],n,k;
bool dfs(int x,int bag){
    for(int i=1;i<=bag&&i<=x;i++){
        if(w[x]+a[i]<=k){
            a[i]+=w[x];
            if(x==n) return true;
            if(dfs(x+1,bag)) return true;
            a[i]-=w[x];
        }
    }
    return false;
}
bool cmp(int x,int y){
    return x>y;
}
int main()
{
    freopen("lighthouse.in","r",stdin);
    freopen("lighthouse.out","w",stdout);
    cin>>n>>k;
    for(int i=1;i<=n;i++) scanf("%d",&w[i]);
    sort(w+1,w+n+1,cmp);
    for(int i=1;i<=n;i++){
        memset(a,0,sizeof(a));
        if(dfs(1,i)){
            cout<<i-1<<endl;
            break;
        }
    }
    //system("pause");
    return 0;
}

T3

题意:

一共有 \(n\) 个数,求两两乘积为整数的数对个数。

思路:

这个题一开始看有思路,但是后来不知道如何解决,就写了个20分的特判。

考完发现思路是正确的,就是有一步的处理有一些问题。

我们先考虑把小数转化成整数。用一个 \(tag\) 记录一下小数位数,这里我们使用 \(char\) 类型进行读入,因为 \(float\) 会丢失精度。

小数要想成为整数,必须有 \(2,5\) 存在。我们对于一个数对,可以记录其所含有的 \(2,5\) 的因数的次幂。

假设两个数 \(x,y\) ,各含有的 \(2,5\) 因数次幂为 \(a,b,c,d\) ,那么就要求 \(a+b\ge0,c+d\ge0\)

因为小数位数最多只有 \(9\) 位,因此我们通过一个二位数组 \(cnt[i][j]\) 记录 \(2,5\) 的幂次为 \(i,j\) 的个数的数字。

输入一个新的数 \(x\) 后,进行枚举从 \(i——10,j——10\) ,答案加和即可。

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long 
const int N=205;
char s[N];
ll n,t5,t2,ans=0;
ll cnt[N][N],val=0,lim2,lim5;
void calc(ll &cnt2,ll &cnt5){
    int len=0,tag=0,flag=1; cnt2=cnt5=val=0;
    scanf("%s",s+1);
    len=strlen(s+1);
    for(int i=1;i<=len&&s[i]!='.';i++) val=val*10+(s[i]-'0'),flag++;
    flag++;
    for(int i=flag;i<=len;i++) val=val*10+(s[i]-'0'),tag++;
    while(val%5==0&&val) val/=5,++cnt5;
    while(val%2==0&&val) val/=2,++cnt2;
    cnt5-=tag,cnt2-=tag;//退位
    lim2=max(lim2,cnt2+50);lim5=max(lim5,cnt5+30);
}
int main()
{
    freopen("check.in","r",stdin);
    freopen("check.out","w",stdout);
    cin>>n;
    for(int i=1;i<=n;i++){
        calc(t2,t5);
        t2+=50,t5+=30;
        for(int i=60-t5;i<=lim5;i++) //桶暴力枚举
            for(int j=100-t2;j<=lim2;j++) ans+=cnt[i][j];
        cnt[t5][t2]++;
    }
    cout<<ans<<endl;
    return 0;
}

T4:

题意:

给你 \(n\) 个点和 \(n-1\) 条边和每个点的权值,求出从每个节点到其他节点的逆序对个数。

思路:

一眼看换根 \(dp\) ,但是空有思路,没法写,因此考试只做出来部分分。

求子树中权值比当前节点小的点,运用子树差分和树状数组。

然后我们考虑知道了 \(f_x\),对于 \(x\) 的子节点 \(y\)\(f_x\) 怎么向 \(f_y\) 转移。

那么在 \(sontree[x]\) 之外的点会额外考虑 \(y\) 的贡献,也就是子树外的权值比 \(c_y\) 小的,这部分贡献要加上。

\(sontree[x]\)之内的点不会再经过 \(x\),那么就是要求在子树内比 \(c_x\) 小的,这部分贡献要消除。

这一步也可以用子树差分和树状数组求。

因为子树差分没学所以先搁置着,到时候再说。

代码:

#include<bits/stdc++.h>
using namespace std;
#define int long long 
#define lowbit(x) x&-x
const int N=1e5+5;
int nxt[N<<1],ver[N<<1],head[N<<1],tot;
int n,m,ans;
int val[N],tr[N],b[N],sizes[N],son[N];
void add(int x,int y){
    ver[++tot]=y;
    nxt[tot]=head[x];
    head[x]=tot;
}
void query(int x,int y){
    for(;x<=m;x+=lowbit(x)) tr[x]+=y;
}
int ask(int x){
    int res=0;
    for(;x;x-=lowbit(x)) res+=tr[x];
    return res;
}
void dfs(int x,int fa){
    sizes[x]=1;
    int last=son[x]=ask(val[x]-1);query(val[x],1);
    for(int i=head[x];i;i=nxt[i]){
        int y=ver[i];
        if(y==fa) continue;
        dfs(y,x);
        int res=ask(val[x]-1)-son[x];
        sizes[x]+=sizes[y];
        son[x]+=res;
        ans+=res*(n-sizes[y]);
    }
    son[x]=son[x]-last;
}
signed main()
{
    freopen("carnival.in","r",stdin);
	freopen("carnival.out","w",stdout);
    cin>>n;m=n;
    for(int i=1;i<=n;i++) scanf("%lld",&val[i]),b[i]=val[i];

    sort(b+1,b+1+m);
    m=unique(b+1,b+1+m)-(b+1);
    for(int i=1;i<=n;i++){
        int x=lower_bound(b+1,b+1+m,val[i])-b; val[i]=x;
    }

    for(int i=1,x,y;i<n;i++){
        scanf("%lld%lld",&x,&y);
        add(x,y);add(y,x);
    }
    dfs(1,0);
    for(int i=1;i<=n;i++) ans+=sizes[i]*(ask(val[i]-1)-son[i]);
    cout<<ans<<endl;
    //system("pause");
    return 0;
}

\[from——pb0207 \]

posted @ 2021-06-20 20:05  Evitagen  阅读(59)  评论(0编辑  收藏  举报