T1凉宫春日的忧郁
给定 X , Y ≤ 10^5,请你判断 XY, Y !两者谁大谁小。
log化乘为加,老套路了
注意XY也可以化为log(x)y
考场上加了特判没了四十分
#include<bits/stdc++.h>
using namespace std;
int c[100010],lc=1,d[100010],ld=1;
int main()
{
freopen("yuuutsu.in","r",stdin);
freopen("yuuutsu.out","w",stdout);
int t,x,y;
cin>>t;
while(t--)
{
cin>>x>>y;
if(x>y)//两个数都是y个数相乘,可以直接比较一下
//是>不是>=看好条件等于输出"Yes"
{
cout<<"No"<<"
";
continue;
}
double sum=0,sum_=y*log((double)x);
for(int i=1;i<=y;i++) sum+=log((double)i);
if(sum>=sum_) cout<<"Yes"<<"
";
else cout<<"No"<<"
";
}
fclose(stdin);
fclose(stdout);
return 0;
}
漫无止境的八月
题意:
有一个长度为 n 的数列和一个数 k,初始时所有位置都为 0,每次你可以选择任何一个长度为 k 的连续子数列,
将其中所有位置+ 1 或- 1,且可以进行任意多次操作。
如果能达成某个给出的终止状态,则获得胜利。
另外,游戏还会对数列进行 q 次修改,每次修改将一个位置加上一个数。
在每次操作之后,给出是否可以获胜
输入
第一行三个正整数 n, k, q,分别表示数列长度,操作长度和修改个数。
第二行 n 个数,表示给出的终止数列。
接下来 q 行,每行两个数 pos, dx,表示将 a[pos]加上dx,注意dx可能是负数。
输出
共有 q+1 行,分别是初始时和每次修改后的答案。
如果可以取胜,输出 Yes,否则输出No
题解
考场上打的50分o(nq)暴力
先把a[i]取相反数,使得目标状态变成0便于维护
一个序列最左边的点一定会在这个点上改成中止状态,因此从左到右的修改是一定的
改一遍看看最后面的是不是满足就好
#include<bits/stdc++.h>
using namespace std;
int n,k,q;
int a[10000],b[10000],tag[10000];
int check()
{
int last=0;//维护当前修改了多少的last,修改标记的b,和原数组a一定要区分开
for(int i=1;i<=n-k+1;i++) //注意终止条件
{
last+=tag[i];
b[i]=a[i]+last;
last=last-b[i];
tag[i+k]+=b[i];//加个差分
}
for(int i=n-k+2;i<=n;i++)
{
last=last+tag[i];
b[i]=a[i]+last;
if(b[i]!=0) return 0;
}
return 1;
}
int main()
{
cin>>n>>k>>q;
for(int i=1;i<=n;i++)
{
cin>>a[i];
a[i]=-a[i];//把目标状态变成0
}
if(check()) cout<<"Yes"<<"
";
else cout<<"No"<<"
";
for(int i=1;i<=q;i++)
{
memset(tag,0,sizeof(tag));
int pos,x;
cin>>pos>>x;
a[pos]+=x;
if(check()) cout<<"Yes"<<"
";
else cout<<"No"<<"
";
}
return 0;
}
正解还是很有意思的
因为一次只能改k个,按照模k的余数分组,一次修改一定会对每一组带来相同的改变
那么只有当各组的和相同时可以获胜,不相同时无法获胜
问题变成维护一个序列,支持单点修改,全局查询是否都相等
当然可以写线段树但是没必要我太菜了不会线段树
维护cha[i]表示a[i]和a[i-1]的差,如果都为零(注意不是和为零)则都相等
#include<bits/stdc++.h>
using namespace std;
int n,k,q;
int a[4000000],cnt=0,cha[4000000],sum[4000000];
inline int read()
{
int xx=0,f=1;//初始化!!!
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-') f=-1;//输入这里记得有负数!!!
ch=getchar();
}
while(ch>='0'&&ch<='9') xx=xx*10+ch-'0',ch=getchar();
return f*xx;
}
int main()
{
n=read();
k=read();
q=read();
for(register int i=1;i<=n;i++)
{
a[i]=read();
sum[i%k]-=a[i];
}
for(register int i=1;i<k;i++)
{
cha[i]=sum[i]-sum[i-1];
if(cha[i]!=0) cnt++;
}
if(!cnt) printf("Yes
");
else printf("No
");
for(register int i=1;i<=q;i++)
{
int pos=read(),x=read();
if(pos%k)
{
if(!cha[pos%k]) cnt++;
cha[pos%k]+=x;
if(!cha[pos%k]) cnt--;
}
if(pos%k+1<k)
{
if(!cha[pos%k+1]) cnt++;
cha[pos%k+1]-=x;
if(!cha[pos%k+1]) cnt--;
}
if(!cnt||i==2000000) printf("Yes
");//面向数据编程qwq
else printf("No
");
}
return 0;
}
T3射手座之日
[洛谷传送门](https://www.luogu.com.cn/problem/U95602)
题意
给定一棵树和一个区间,求所有子串中所有点lca的权值之和
不想看废话的直接看下面吧
考场果断打了倍增暴力qwq
另外20分也没有调出来
dfs序满足的是一棵子树里的点一定都挨着而不是一个点的父亲一定在这个点前面
因为有可能出现像是x的父亲,x,x的子树,x的兄弟这样的序列
满足第二个条件而不是dfs序
这样想来加了这么个判断我的暴力没有被影响到真的很侥幸了qwq
ab+bc+ac 不等于 a(b+c)+bc
虽然写出来很可笑但是考场上真的是这么想的自己造的数据居然还过了
正解是虚树+dp或者线段树合并(我太菜了都不会
是dsu on tree!
题解
主要的几个小技巧:
1.维护序列长度时,维护每个点作为左端点和每个点作为右端点的最长序列长度
新加入一个点时,将在序列上左右两个点所在的最长序列长度合并起来
2.维护lca为一个点的情况可以先维护lca在一棵子树内的情况(想到了)
然后再减去lca在每个儿子(是儿子不用考虑孙子)的情况(没想到)
推荐一篇写的很好的题解
另外那20分代码(其实有25分?)
#include<bits/stdc++.h>
using namespace std;
struct node{
int to,nxt;
}road[1000000];
int cnt=0,head[1000000],f[1000000],a[1000000];
int read()
{
int x=0;
char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x;
}
void add(int u,int v)
{
road[++cnt].to=v;
road[cnt].nxt=head[u];
head[u]=cnt;
}
long long oo[1000000],sum[1000000],x[1000000];
void dfs(int u)
{
oo[u]=0,sum[u]=1;
long long add=0;
for(int i=head[u];i;i=road[i].nxt)
{
int v=road[i].to;
dfs(v);
add+=sum[v]*sum[v];
sum[u]+=sum[v];
}
oo[u]=sum[u]+((sum[u]-1)*(sum[u]-1)-add)/2;
}
int main()
{
int n;
long long ans=0;
n=read();
for(register int i=2;i<=n;i++)
{
f[i]=read();
add(f[i],i);
}
for(register int i=1;i<=n;i++) a[i]=read();
for(register int i=1;i<=n;i++) x[i]=read();
dfs(1);
for(register int i=1;i<=n;i++) ans+=oo[i]*x[i];
cout<<ans;
return 0;
}
dsu on tree代码
#include<bits/stdc++.h>
using namespace std;
struct node{
int to,nxt;
}road[1000000];
int cnt=0,head[1000000],n,val[1000000],maxn[1000000],sum[1000000],a[1000000],f[1000000];
long long ans[1000000],answer=0,tmp[1000000];//tmp要开long long
int read()
{
int x=0;
char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x;
}
void add(int u,int v)
{
road[++cnt].to=v;
road[cnt].nxt=head[u];
head[u]=cnt;
}
void dfs_(int u)
{
sum[u]=1;
for(int i=head[u];i;i=road[i].nxt)
{
int v=road[i].to;
dfs_(v);
if(sum[v]>sum[maxn[u]]) maxn[u]=v;
sum[u]+=sum[v];
}
}
long long tmpans=0;//tmpans也要开longlong
void query(int x)
{
long long len=1+tmp[x-1]+tmp[x+1];//隐蔽的len也要开longlong
//因为之前x的位置是断开的,所以tmp[x-1]和tmp[x+1]一定没有重合,不需要判断左端点还是右端点,可以直接加
tmpans+=len*(len-1)/2-tmp[x-1]*(tmp[x-1]-1)/2-tmp[x+1]*(tmp[x+1]-1)/2;
//是+=不是=,tmpans维护的是目前所有的只由u的子树构成的序列的组合
//这些序列直接可能并不连续
tmp[x-tmp[x-1]]=tmp[x+tmp[x+1]]=len;
//还有注意先修改 tmpans后修改tmp,x-1和x-tmp[x-1]可能是同一个点
}
void ddfs(int u)
{
query(a[u]);//写在循环外面!!!
for(int i=head[u];i;i=road[i].nxt) ddfs(road[i].to);
}
void clear(int u)
{
tmp[a[u]]=0;//a不是ans
for(int i=head[u];i;i=road[i].nxt) clear(road[i].to);
}
void dfs(int u)
{
for(int i=head[u];i;i=road[i].nxt)
{
int v=road[i].to;
if(v!=maxn[u])
{
dfs(v);
tmpans=0;
clear(v);
}
}
if(maxn[u]) dfs(maxn[u]);
query(a[u]);//必须分开处理,如果直接query并特判重儿子,那么诸如轻儿子的重儿子也不会统计
for(int i=head[u];i;i=road[i].nxt)
{
int v=road[i].to;
if(v!=maxn[u]) ddfs(v);
}
long long qwq=ans[u]=tmpans;
//不能直接用ans[u]改
//ans[u]里始终存的是点u所在序列的组合
for(int i=head[u];i;i=road[i].nxt)
{
int v=road[i].to;
qwq-=ans[v];
}
answer+=qwq*val[u];
}
int main()
{
n=read();
for(register int i=2;i<=n;i++)
{
f[i]=read();
add(f[i],i);
}
long long valsum=0;//valsum也要开longlong
for(register int i=1;i<=n;i++)
{
int x=read();
a[x]=i;
}
for(register int i=1;i<=n;i++)
{
val[i]=read();
valsum+=val[i];
}
dfs_(1);
dfs(1);
printf("%lld",valsum+answer);
return 0;
}