牛客練習賽64題解

題目鏈接

A.怪盜-1412

題意:
n1m4k2有n個1,m個4,k個2
1412組成一個序列問最多能有多少子序列是1412
題解:
11112222111144擺成11…1122…2211…1144……的樣子
使這樣能使得每個數都有貢獻
1根據和一樣,兩數差最小乘積最大可以知道平分1即可
四個位置個數相乘

#include<bits/stdc++.h>
using namespace std;
int main(){
    int _;
    scanf("%d",&_);
    while(_--){
        long long n,m,k;
        scanf("%lld%lld%lld",&n,&m,&k);
        printf("%lld\n",(n/2)*m*k*(n-n/2));
    }
}

B.Dis2

題意:
2給一棵樹,問每個點有多少和他距離爲2的點
題解:
2對於每個點來說,距離爲2的點,就是這個點的子節點的子節點
統計一下這個點相連的點,每個點的度數
但由於是雙向邊,需要去除自己的貢獻
AC代碼

/*
    Author:zzugzx
    Lang:C++
    Blog:blog.csdn.net/qq_43756519
*/
#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define endl '\n'
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int mod=1e9+7;
//const int mod=998244353;
const double eps = 1e-10;
const double pi=acos(-1.0);
const int maxn=1e6+10;
const ll inf=0x3f3f3f3f;
const int dir[4][2]={{0,1},{1,0},{0,-1},{-1,0}};

ll ans[maxn];
vector<int> g[maxn];


int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int n;cin>>n;
    for(int i=1,u,v;i<n;i++){
        cin>>u>>v;
        g[u].pb(v);
        g[v].pb(u);
    }
    for(int u=1;u<=n;u++){
        ll ans=0;
        for(auto v:g[u]){
            if(v==u)continue;
            ans+=g[v].size()-1;
        }
        cout<<ans<<endl;
    }
    return 0;
}


C.序列卷積之和

題意:
n給一個長度爲n的數組
l=1nr=lni=lrj=iraiaj mod 1e9+7求\sum_{l=1}^n\sum_{r=l}^n\sum_{i=l}^r\sum_{j=i}^ra_i*a_j~mod~1e9+7
題解:
n<=2e5n<=2e5,很大
暴力求解就不用想了,考慮一下怎麼樣化簡
自己舉個例子感受一下,不要太多數
41234我這裏舉4個數,1,2,3,4
1然後先走前兩個確定,結果是1
然後第一個不變,第二個累加區間加
11+12+22得到1*1+1*2+2*2
11+12+13+22+23+33再變一次1*1+1*2+1*3+2*2+2*3+3*3

11+12+13+14+22+23+24+33+34+441*1+1*2+1*3+1*4+2*2+2*3+2*4+3*3+3*4+4*4
1這樣第一個累加爲1的時候跑完了
我們可以化簡一下
1(1+1+2+1+2+3+1+2+3+4)1*(1+1+2+1+2+3+1+2+3+4)
2(2+2+3+2+3+4)2*(2+2+3+2+3+4)
3(3+3+4)3*(3+3+4)
444*4
然後可以想象一下,或者大概再往後下一點就能發現
2第一個累加爲2的時候,其實就是這式子裏的後三個
3爲3的時候是後兩個
這規律就找到了
西我們只要把這些括號裏的東西算出來和數相乘,最後求一下後綴的後綴就是答案了
ini+1括號裏的可以用個數算,我們可以發現第i個數一共有n-i+1個
然後我們用後綴跑一下每個括號裏每個數的個數乘對應的值
西sumi這樣括號裏的東西算出來了,假設是sum_i
i=1naisumi1\sum_{i=1}^na_i*sum_i就是第一個累加爲1時候的結果
i=2naisumi2\sum_{i=2}^na_i*sum_i就是第一個累加爲2時候的結果
1n所以這個時候我們就需要求一下後綴的後綴算出來第一個累加爲1到n全部的和
便但其實有更簡便的方法,我們直接記個數
iaisumii假設下標爲i的a_i*sum_i我們用的次數應該是i次
i1n所以i從1到n直接將每個值乘他的貢獻次數就是最終答案

如果你感覺這樣的規律不好看出來
那你可以考慮,去掉兩層內部的累加,用代碼輸出一下找一下規律
這個規律應該也很好看出,其實就是我在上面說的規律
但是會更加直觀
AC代碼

/*
    Author:zzugzx
    Lang:C++
    Blog:blog.csdn.net/qq_43756519
*/
#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define endl '\n'
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int mod=1e9+7;
//const int mod=998244353;
const double eps = 1e-10;
const double pi=acos(-1.0);
const int maxn=1e6+10;
const ll inf=0x3f3f3f3f;
const int dir[4][2]={{0,1},{1,0},{0,-1},{-1,0}};
ll Pow(ll a, ll b){
	ll ans = 1;
	while(b > 0){
		if(b & 1){
			ans = ans * a % mod;
		}
		a = a * a % mod;
		b >>= 1;
	}
	return ans;
}
ll a[maxn],s1[maxn];


int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
        cin>>a[i];
    for(int i=n;i;i--)
        s1[i]=(s1[i+1]+a[i]*(n-i+1)%mod)%mod;
    ll ans=0;
    for(int i=1;i<=n;i++)
        ans=(ans+i*a[i]%mod*s1[i]%mod)%mod;;
    cout<<ans;
    return 0;
}


D.寶石裝箱

題意:
nnn個寶石放進n個盒子
iai如果第i種寶石不能放到a_i盒子有多少種方案
mod 998244353mod~998244353
題解:
i如果是直接寫,我們需要考慮對於第i種寶石
aia_i盒子是否已經被放了寶石,最後得出的會是兩種情況
我們很難統計這兩種情況
那就倒着寫,先隨意放,看看其中有多少不合法的方案
n!總方案很好求,就是n!
現在需要求的是不合法的方案
不合法的方案就是某個盒子放了不該放的寶石
i我們需要統計最後有用的就是i個盒子放了不該放的寶石的方案
ii但是恰好i個我們很難求出,我們可以用至少有i個盒子放了不該放的寶石
i如果是至少i個盒子,那麼肯定有重複的方案
這時候就可以用的容斥原理了,奇數加偶數減,容斥原理就不多說
i現在開始開始說怎麼求至少i個盒子放了不該放的
其實就是統計出哪幾個位置,有幾個不該放的寶石
不該放的寶石個數,就是這一個點的方案數
對於多個位置就是相乘起來
由於統計的是需要將所有寶石放入,對於其他寶石也需要進行放入
那麼就是乘剩餘沒有選擇的個數的階乘,我們已經選擇的是確定不合法的
使01+這一過程我們可以使用01揹包+滾動數組實現
dp[j]=dp[j1]a[i]狀態轉移很簡單,就是dp[j]=dp[j-1]*a[i]
求出來的是我們確定不合法的盒子數
然後剩下的就是把他乘上對應的階乘形成完整方案,然後進行容斥去重即可
最後用總數減去這個算出來的數
AC代碼

/*
    Author:zzugzx
    Lang:C++
    Blog:blog.csdn.net/qq_43756519
*/
#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define endl '\n'
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
//const int mod=1e9+7;
const int mod=998244353;
const double eps = 1e-10;
const double pi=acos(-1.0);
const int maxn=1e6+10;
const ll inf=0x3f3f3f3f;
const int dir[4][2]={{0,1},{1,0},{0,-1},{-1,0}};

ll fac[maxn],dp[maxn],a[maxn];

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int n;cin>>n;fac[0]=1;
    for(int i=1,x;i<=n;i++){
        cin>>x;
        a[x]++;
        fac[i]=fac[i-1]*i%mod;
    }
    dp[0]=1;
    for(int i=1;i<=n;i++)
        for(int j=i;j>=0;j--)
            dp[j+1]=(dp[j+1]+dp[j]*a[i]%mod)%mod;
    ll ans=fac[n],res=0;
    for(int i=1,p=1;i<=n;i++,p=-p)
        res=(res+fac[n-i]*dp[i]*p%mod+mod)%mod;
    cout<<(ans-res+mod)%mod;
    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章