G - 区间权值
题目描述
小 Bo 有 n 个正整数 a1..an,以及一个权值序列 w1…wn,现在他定义
现在他想知道 的值,需要你来帮帮他
你只需要输出答案对 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%不知道为什么