天天看點

牛客國慶集訓派對Day4 - G、I、H

G - 區間權值

題目描述

小 Bo 有 n 個正整數 a1..an,以及一個權值序列 w1…wn,現在他定義

牛客國慶集訓派對Day4 - G、I、H

現在他想知道

牛客國慶集訓派對Day4 - G、I、H

的值,需要你來幫幫他

你隻需要輸出答案對 109+7 取模後的值

輸入描述:

第一行一個正整數 n
第二行 n 個正整數 a1..an
第三行 n 個正整數 w1..wn
           

輸出描述:

輸出答案對 109+7 取模後的值
           

示例1

輸入

複制

3
1 1 1
1 1 1
           

輸出

複制

10
           

備注:

1≤ n≤ 3x 105
1≤ ai≤ 107
1≤ wi≤ 107
           

注意:取模的時候不要ans+=balabala%mod,這樣隻是給等号後的數取模了,沒有給ans加後的值取模,會爆的嘤嘤嘤qwq

代碼如下:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#include<queue>
#include<cmath>
#include<set>
#define ll long long
using namespace std;
const int N=300005,mod=1e9+7;
ll sumA[N],sumB1[N],sumB[N],W[N],a[N];
int main(){
    int n;
    scanf("%d",&n);
    memset(sumA,0,sizeof(sumA));
    memset(sumB,0,sizeof(sumB));
    memset(sumB1,0,sizeof(sumB1));
    for(int i=1;i<=n;i++){
        scanf("%lld",&a[i]);
        sumA[i]=(sumA[i-1]+a[i])%mod;
    }
    for(int i=1;i<=n;i++){
        sumB[i]=(a[i]*i%mod+sumB[i-1])%mod;
        sumB1[i]=(a[n-i+1]*i%mod+sumB1[i-1])%mod;
    }
    for(int i=1;i<=n;i++){
        scanf("%lld",&W[i]);
    }
    ll tmp,ans;
    int k;
    if(n==1)ans=W[1]*a[1];
    else ans=(sumA[n]*W[1]%mod+sumA[n]*W[n]%mod)%mod;
    for(int r=2;r<n;r++){
        if(2*r-1<=n)k=r;
        else{
            k=min(r-1,n-r+1);
        }
        tmp=((sumB[k-1]+sumB1[k-1])%mod+((sumA[n-k+1]-sumA[k-1])+mod)%mod*k%mod)%mod;
        ans=(ans+tmp*W[r]%mod)%mod;
    }
    printf("%lld\n",ans);
}
           

I - 連通塊計數

題目描述

小 A 有一棵長的很奇怪的樹,他由 n 條鍊和 1 個點作為根構成,第 i 條鍊有 ai 個點,每一條鍊的一端都與根結點相連。

現在小 A 想知道,這棵長得奇怪的樹有多少非空的連通子樹,你隻需要輸出答案對 998244353 取模的值即可

輸入描述:

第一行一個正整數 n
第二行 n 個正整數 a1…an
           

輸出描述:

輸出答案對 998244353 取模後的值
           

示例1

輸入

複制

2
1 1
           

輸出

複制

6
           

備注:

1≤ n≤ 105
1≤ ai≤ 107
           

思路:這是個思維題啊,還挺不錯的

當沒有根的時候:bi=(a[i]+1)*a[i]/2     把b1到bn加起來

當有根的時候(a[1]+1)*(a[2]+1)*…(a[n]+1)

不考慮根的情況好了解,當有根節點的時候,每一條鍊從根開始,一共有1,2,3…1+a[i]種情況,把每個鍊的情況乘起來就好啦~

代碼如下:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#include<queue>
#include<cmath>
#include<set>
#define ll long long
using namespace std;
const int N=300005,mod=998244353;

ll mod_pow(ll a,ll b){
    ll res=1;
    while(b){
        if(b&1)res=res*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return res;
}

int main(){
    int n;
    ll a;
    scanf("%d",&n);
    ll res1=1,res2=0;
    for(int i=1;i<=n;i++){
        scanf("%lld",&a);
        res2=(res2+a*(a+1)%mod*mod_pow(2,mod-2))%mod;
        res1=res1*(a+1)%mod;
    }
    printf("%lld\n",(res1+res2)%mod);
}
           

H -  樹鍊博弈

題目描述

給定一棵 n 個點的樹,其中 1 号結點是根,每個結點要麼是黑色要麼是白色

現在小 Bo 和小 Biao 要進行博弈,他們兩輪流操作,每次選擇一個黑色的結點将它變白,之後可以選擇任意多個(可以不選)該點的祖先(不包含自己),然後将這些點的顔色翻轉,不能進行操作的人輸

由于小 Bo 猜拳經常輸給小 Biao,他想在這個遊戲上扳回一城,現在他想問你給定了一個初始局面,是先手必勝還是後手必勝

輸入描述:

第一行一個正整數 n
第二行 n 個整數 w1..wn,wi∈ {0,1},wi=1 表示第 i 個結點一開始是黑點,否則是白點
接下來 n-1 行,每行兩個正整數 u,v 表示一條樹邊 (u,v)
           

輸出描述:

如果先手必勝,輸出First ,否則輸出Second
           

示例1

輸入

複制

2
1 0
1 2
           

輸出

複制

First
           

備注:

1≤ n≤ 1000
           

思路:

(1)若每層的黑點數都為偶數那麼先手必敗,因為先手改一層的黑點為白點,然後操作完,後手改相同層的黑點為白點,然後做和先手相同的操作,那麼祖先相當于沒變,每層都是偶數那麼一定能保證先手到無路可走的狀态。

(2)反之,先手勝,先手先把深度最大的奇數層的變為偶數,然後對其祖先操作,使得每層的黑點數都是偶數,接着後手操作,先手做(1)操作,後手敗。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#include<queue>
#include<cmath>
#include<set>
#define ll long long
using namespace std;
const int N=1005,mod=998244353;
int a[N];
struct pro{int from,to,next;};
pro edge[2*N];
int cnt=0,head[N],depth[N];

void addEdge(int from,int to){
    edge[cnt].from=from;
    edge[cnt].to=to;
    edge[cnt].next=head[from];
    head[from]=cnt++;
}

void dfs(int pre,int now,int dep){
    depth[dep]+=a[now];
    for(int i=head[now];i!=-1;i=edge[i].next){
        int v=edge[i].to;
        if(v==pre)continue;
        dfs(now,v,dep+1);
    }
}

int main(){
    int n;
    scanf("%d",&n);
    memset(head,-1,sizeof(head));
    memset(depth,0,sizeof(depth));
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    int s,t;
    for(int i=1;i<n;i++){
        scanf("%d%d",&s,&t);
        addEdge(s,t);
        addEdge(t,s);
    }
    dfs(0,1,1);
    int ans=0;
    for(int i=1;i<=n;i++)if(depth[i]%2==1)ans=1;
    if(ans)printf("First\n");
    else printf("Second\n");
}
           

判斷的時候

if(depth[i]%2==1){ans=1;break;}

加個break就隻能過70%不知道為什麼