2018“百度之星”程序設計大賽 - 復賽


沒有兄弟的舞會

 
 Accepts: 928
 
 Submissions: 2446
 Time Limit: 2000/1000 MS (Java/Others)
 
 Memory Limit: 65536/65536 K (Java/Others)
Problem Description

度度熊、光羽、帶勁三個人是好朋友。

度度熊有一棵nn個點的有根樹,其中1號點為樹根。除根節點之外,每個點都有父節點,記ii號點的父節點為fa[i]fa[i]。

度度熊稱點ii和點jj是兄弟(其中i \neq jij)當且僅當fa[i]=fa[j]fa[i]=fa[j]。

ii個點的權值為A_iAi​​。現要求選出一個點集,該點集合法當且僅當點集中至多只有一對兄弟。

度度熊想知道,在所有可行的點集中,權值和最大以及最小的點集權值和分別是多少?

Input

第一行一個數,表示數據組數TT。

每組數據第一行一個整數nn;第二行n-1n1個數,表示fa[2],fa[3],..,fa[n]fa[2],fa[3],..,fa[n];第三行nn個數,表示A_iAi​​。

數據組數T=100,滿足:

  • 1 \le n \le 10^51n105​​
  • -10^9 \le A_i \le 10^9109​​Ai​​109​​
  • 1 \le fa[i] < i1fa[i]<i

其中90%的數據滿足n \le 1000n1000。

Output

每組數據輸出一行,每行包含兩個數,分別表示權值和的最大值和最小值。

Sample Input
2
5
1 1 2 2 
-4 -4 -1 -2 -5 
5
1 1 3 2 
-1 -4 2 0 -2 
Sample Output
0 -15
2 -7

這個對我來說有點hard啊,我比較傻,要選的是點集,不一定是子樹,所以即使是樹形dp也不是全都可以選的,所以可以直接sort之后取一個就好了

另外一個直接找就好了

#include<stdio.h>
#include<bits/stdc++.h>
using namespace std;
#define lson l,(l+r)/2,rt<<1
#define rson (l+r)/2+1,r,rt<<1|1
#define dbg(x) cout<<#x<<" = "<< (x)<< endl
#define pb push_back
#define fi first
#define se second
#define ll long long
#define sz(x) (int)(x).size()
#define pll pair<long long,long long>
#define pii pair<int,int>
#define pq priority_queue
const int N=1e5+5,MD=1e9+7,INF=0x3f3f3f3f;
const ll LL_INF=0x3f3f3f3f3f3f3f3f;
const double eps=1e-9,e=exp(1),PI=acos(-1.);
vector<ll>G[N];
ll a[N],x;
int main()
{
    ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    int T,n;
    cin>>T;
    while(T--)
    {
        cin>>n;
        for(int i=2; i<=n; i++)cin>>a[i];
        for(int i=1; i<=n; i++)cin>>x,G[a[i]].push_back(x);
        ll ma=0,mi=0,minx=1e9,maxx=-1e9;
        for(int i=0; i<=n; i++)
        {
            sort(G[i].begin(),G[i].end());
            int len=G[i].size();
            if(len)
            {
                ma=max(ma,ma+G[i][len-1]),mi=min(mi,mi+G[i][0]);
                if(len>1)minx=min(minx,G[i][1]),maxx=max(maxx,G[i][len-2]);
            }
        }
        mi=min({mi,mi+minx,0LL}),ma=max({ma,maxx+ma,0LL});
        cout<<ma<<" "<<mi<<"\n";
        for(int i=0; i<=n; i++)G[i].clear();
    }
    return 0;
}

帶勁的and和

 
 Accepts: 791
 
 Submissions: 2435
 Time Limit: 2000/1000 MS (Java/Others)
 
 Memory Limit: 65536/65536 K (Java/Others)
Problem Description

度度熊專門研究過“動態傳遞閉包問題”,他有一萬種讓大家爆蛋的方法;但此刻,他只想出一道簡簡單單的題——至繁,歸於至簡。

度度熊有一張n個點m條邊的無向圖,第ii個點的點權為v_ivi​​。

如果圖上存在一條路徑使得點ii可以走到點jj,則稱i,ji,j是帶勁的,記f(i,j)=1f(i,j)=1;否則f(i,j)=0f(i,j)=0。顯然有f(i,j) = f(j,i)f(i,j)=f(j,i)。

度度熊想知道求出: \sum_{i=1}^{n-1} \sum_{j=i+1}^{n} f(i,j) \times \max(v_i, v_j) \times (v_i \& v_j)i=1n1​​j=i+1n​​f(i,j)×max(vi​​,vj​​)×(vi​​&vj​​)

其中\&&是C++中的and位運算符,如1&3=1, 2&3=2。

請將答案對10^9+7109​​+7取模后輸出。

Input

第一行一個數,表示數據組數TT。

每組數據第一行兩個整數n,mn,m;第二行nn個數表示v_ivi​​;接下來mm行,每行兩個數u,vu,v,表示點uu和點vv之間有一條無向邊。可能有重邊或自環。

數據組數T=50,滿足:

  • 1 \le n,m \le 1000001n,m100000
  • 1 \le v_i \le 10^91vi​​109​​。

其中90%的數據滿足n,m \le 1000n,m1000。

Output

每組數據輸出一行,每行僅包含一個數,表示帶勁的and和。

Sample Input
1
5 5
3 9 4 8 9 
2 1
1 3
2 1
1 2
5 2
Sample Output
99

其實就是每一個聯通塊要算貢獻,這個聯通塊是可以用二進制壓縮的,所以並查集找出聯通塊直接暴力就好了

#include<stdio.h>
#include<bits/stdc++.h>
using namespace std;
#define lson l,(l+r)/2,rt<<1
#define rson (l+r)/2+1,r,rt<<1|1
#define dbg(x) cout<<#x<<" = "<< (x)<< endl
#define pb push_back
#define fi first
#define se second
#define ll long long
#define sz(x) (int)(x).size()
#define pll pair<long long,long long>
#define pii pair<int,int>
#define pq priority_queue
const int N=1e5+5,MD=1e9+7,INF=0x3f3f3f3f;
const ll LL_INF=0x3f3f3f3f3f3f3f3f;
const double eps=1e-9,e=exp(1),PI=acos(-1.);
int fa[N],v[N];
vector<int>G[N];
int find(int x)
{
    return x==fa[x]?x:(fa[x]=find(fa[x]));
}
void la(int x,int y)
{
    x=find(x),y=find(y);
    if(x!=y)fa[x]=y;
}
int bit[31];
ll lb(vector<int>v)
{
    sort(v.begin(),v.end());
    ll ans=0,sum[31]= {0};
    for(auto t:v)
        for(int j=0; j<30; j++)
        {
            if(t&bit[j])
            {
                ans=(ans+t*1LL*sum[j]%MD*bit[j]%MD)%MD;
                sum[j]++;
            }
        }
    return ans;
}
int main()
{
    ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    for(int i=0; i<30; i++)bit[i]=1<<i;
    int T,n,m;
    cin>>T;
    while(T--)
    {
        cin>>n>>m;
        for(int i=1; i<=n; i++)cin>>v[i];
        for(int i=1; i<=n; i++)fa[i]=i;
        for(int i=0,u,v; i<m; i++)
            cin>>u>>v,la(u,v);
        for(int i=1; i<=n; i++)G[find(i)].push_back(v[i]);
        int ans=0;
        for(int i=1; i<=n; i++)
        {
            if(G[i].size()==0)continue;
            ans=(ans+lb(G[i]))%MD;
        }
        cout<<ans<<"\n";
        for(int i=1; i<=n; i++)G[i].clear();
    }
    return 0;
}

序列期望

 
 Accepts: 545
 
 Submissions: 1163
 Time Limit: 2000/1000 MS (Java/Others)
 
 Memory Limit: 65536/65536 K (Java/Others)
Problem Description

"看似隨機,實則早已注定"——光羽

度度熊有nn個隨機變量x_1,x_2,...,x_nx1​​,x2​​,...,xn​​。給定區間[l_1, r_1],...,[l_n, r_n][l1​​,r1​​],...,[ln​​,rn​​],變量x_ixi​​的值會等概率成為區間[l_i, r_i][li​​,ri​​]中的任意一個整數。

顯然這nn個隨機變量的值會有一共\prod_{i=1}^{n} (r_i - l_i + 1)i=1n​​(ri​​li​​+1) 種情況,且每種情況出現的概率為\prod_{i=1}^{n} \frac{1}{r_i - l_i + 1}i=1n​​ri​​li​​+11​​ 。

對於某種情況,令h= \max{ x_1,x_2,...,x_n}h=max{x1​​,x2​​,...,xn​​},定義這種情況的權值為:\prod_{i=1}^{n} (h - x_i + 1)i=1n​​(hxi​​+1).

度度熊想知道權值的期望是多少?請將答案對10^9 + 7109​​+7取模后輸出。

PS:不清楚期望是啥?為什么不問問神奇的百度呢?

Input

第一行一個數,表示數據組數TT。

每組數據第一行一個整數nn;接下來nn行,每行兩個數,表示l_ili​​和r_iri​​。

數據組數T=100,滿足:

  • 1 \le n \le 1001n100
  • 1 \le l_i \le r_i \le 10^41li​​ri​​104​​

其中70%的數據滿足r_i \le 100ri​​100。

Output

每組數據輸出一行,每行僅包含一個數,表示期望。

假設答案為\frac{p}{q}qp​​,請輸出p \times q^{-1} ~mod~10^9+7p×q1​​ mod 109​​+7,此處q^{-1}q1​​為qq的逆元。

Sample Input
2
3
2 5
2 4
2 5
3
1 1
2 3
1 1
Sample Output
875000012
500000010
Hint
第二組數據的解釋:序列只有兩種情況(1,2,1)和(1,3,1),權值分別為2*1*2=4和3*1*3=9,答案為(4+9)/2,在模域下為500000010。
這個題目看起來好像不可做,其實就是簡單的期望,我們只要想到權值的統計方法就好了
顯然我們可以枚舉最大的區間
卡常勸退,比賽都能過的啊
#include<stdio.h>
#include<algorithm>
typedef long long ll;
const int MD=1e9+7;
int l[105],r[105];
ll w[105],v[10002];
int main()
{
    v[0]=v[1]=1;
    for(int i=2; i<10002; i++)v[i]=v[MD%i]*(MD-MD/i)%MD;
    int T,n,m;
    scanf("%d",&T);
    while(T--)
    {
        int L=0,R=0;
        ll ans=0;
        scanf("%d",&n);
        for(int i=0; i<n; i++)scanf("%d%d",&l[i],&r[i]),L=std::max(L,l[i]),R=std::max(R,r[i]);
        auto la=[&](long long x, long long y)
        {
            return (x+y)*(y-x+1)%MD*v[2]%MD;
        };
        for(int i=L; i<=R; i++)
        {
            ll t=1;
            w[n]=1;
            for(int j=0,x,y; j<n; j++)
            {
                x=std::max(2,i-r[j]+1),y=i-l[j]+1;
                w[j]=la(x,y);
            }
            for(int j=n-1; j>=0; j--)w[j]=w[j]*w[j+1]%MD;
            for(int j=0,x,y; j<n; j++)
            {
                if(r[j]>=i)ans=(ans+t*w[j+1])%MD;
                x=std::max(1,i-r[j]+1),y=i-l[j]+1;
                t=t*la(x,y)%MD;
            }
        }
        for(int i=0; i<n; i++)ans=ans*v[r[i]-l[i]+1]%MD;
        printf("%lld\n",ans);
    }
    return 0;
}

 前面一定是2的倍數,為什么要乘上逆元,sb了,這樣就不被卡常了

#include<stdio.h>
#include<algorithm>
typedef long long ll;
const int MD=1e9+7;
int l[105],r[105];
ll w[105],v[10002];
ll la(ll x,ll y)
{
    return (x+y)*(y-x+1)/2%MD;
}
int main()
{
    v[0]=v[1]=1;
    for(int i=2; i<10002; i++)v[i]=v[MD%i]*(MD-MD/i)%MD;
    int T,n,m;
    scanf("%d",&T);
    while(T--)
    {
        int L=0,R=0;
        ll ans=0;
        scanf("%d",&n);
        for(int i=0; i<n; i++)scanf("%d%d",&l[i],&r[i]),L=std::max(L,l[i]),R=std::max(R,r[i]);
        for(int i=L; i<=R; i++)
        {
            ll t=1;
            w[n]=1;
            for(int j=0,x,y; j<n; j++)
            {
                x=std::max(2,i-r[j]+1),y=i-l[j]+1;
                w[j]=la(x,y);
            }
            for(int j=n-1; j>=0; j--)w[j]=w[j]*w[j+1]%MD;
            for(int j=0,x,y; j<n; j++)
            {
                if(r[j]>=i)ans=(ans+t*w[j+1])%MD;
                x=std::max(1,i-r[j]+1),y=i-l[j]+1;
                t=t*la(x,y)%MD;
            }
        }
        for(int i=0; i<n; i++)ans=ans*v[r[i]-l[i]+1]%MD;
        printf("%lld\n",ans);
    }
    return 0;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM