16 前两题
传奇挂分王之195pts再创新高 挂红温了
玩具谜题
模拟,可以展环为链(挂到只切了这道)
#include<bits/stdc++.h>
using namespace std;
int read(){
char ch;int x=0,f=1;
while(!isdigit(ch=getchar())){
if(ch=='-') f=-1;
}
while(isdigit(ch)){
x=(x<<1)+(x<<3)+(ch-'0');
ch=getchar();
}
return x*f;
}const int N=1e5+5;
bool face[N];char job[N][15];
int n,m,a,s,now,lst[N<<1];
int main(){
freopen("toy.in","r",stdin);
freopen("toy.out","w",stdout);
n=read(),m=read();for(int i=1;i<=n;++i) lst[i]=i,lst[i+n]=i;
for(int i=1;i<=n;++i) face[i]=read(),scanf("%s",job[i]+1);now=1;
while(m--){
a=read(),s=read();
if(a^face[now]) now=lst[now+s];
else now=lst[now+n-s];
}printf("%s",job[now]+1);
return 0;
}
组合数问题
预处理,直接模k,然后二维前缀和(二维前缀和挂了)。
#include<bits/stdc++.h>
using namespace std;
int read(){
char ch;int x=0,f=1;
while(!isdigit(ch=getchar())){
if(ch=='-') f=-1;
}
while(isdigit(ch)){
x=(x<<1)+(x<<3)+(ch-'0');
ch=getchar();
}
return x*f;
}
int t,k,n,m,res[2005][2005],rt[2005][2005];
int main(){
freopen("problem.in","r",stdin);
freopen("problem.out","w",stdout);
t=read(),k=read();
for(int i=0;i<=2002;++i) rt[i][0]=rt[i][i]=1;
for(int i=1;i<=2002;++i){
for(int j=1;j<=i;++j){
rt[i][j]=(rt[i-1][j-1]+rt[i-1][j])%k;
res[i][j]=res[i-1][j]-res[i-1][j-1]+res[i][j-1];
if(rt[i][j]==0) ++res[i][j];
}res[i][i+1]=res[i][i];
}
while(t--){
n=read(),m=read();
if(n<m) printf("%d\n",res[n][n]);
else printf("%d\n",res[n][m]);
}
return 0;
}
天天爱跑步
dfs挂了
暴力代码
#include<bits/stdc++.h>
using namespace std;
int read(){
char ch;int x=0,f=1;
while(!isdigit(ch=getchar())){
if(ch=='-') f=-1;
}
while(isdigit(ch)){
x=(x<<1)+(x<<3)+(ch-'0');
ch=getchar();
}
return x*f;
}
const int N=3e5+5;
int n,m,vis[N],ti[N],f[N][22],path[N];
int st,ed,dep[N],dfn[N],ans[N],mid,fl;
vector<int> tr[N];
int lca(int u,int v){
if(dep[u]<dep[v]) swap(u,v);
for(int i=20;i>=0;--i){
if(dep[f[u][i]]>=dep[v]) u=f[u][i];
}
if(u==v) return u;
for(int i=20;i>=0;--i){
if(f[u][i]!=f[v][i]&&dep[u]==dep[v]) u=f[u][i],v=f[v][i];
}
return f[u][0];
}
void pre(int now,int fa){
f[now][0]=fa;
for(int i=1;i<=20;++i) f[now][i]=f[f[now][i-1]][i-1];
dep[now]=dep[fa]+1;
for(int to:tr[now]){
if(to!=fa) pre(to,now);
}
}
void work(int pos,int fr,int step){
if(fl) return;
if(fr==pos){path[step]=pos,dfn[pos]=dfn[path[step-1]]+1;
for(int i=1;i<=step;++i){
if(ti[path[i]]==dfn[path[i]]-1) ans[path[i]]++;
}fl=1;return;
}path[step]=pos,dfn[pos]=dfn[path[step-1]]+1;
for(int to:tr[pos]){
if(!vis[to]) vis[to]=1,work(to,fr,step+1);
}path[step]=0,dfn[pos]=0;
}
int main(){
freopen("running.in","r",stdin);
freopen("running.out","w",stdout);
n=read(),m=read();int a,b;
for(int i=1;i<n;++i){
scanf("%d %d",&a,&b);tr[a].push_back(b),tr[b].push_back(a);
}dep[1]=1,pre(1,0);
for(int i=1;i<=n;++i) ti[i]=read();
while(m--){
st=read(),ed=read(),mid=lca(st,ed);
if(mid==st||mid==ed){
fl=0;vis[st]=1;work(st,ed,1);
memset(vis,0,sizeof(vis));memset(dfn,0,sizeof(dfn));
}
else{vis[st]=1;
fl=0;work(st,mid,1);
for(int i=1;i<=n;++i){
if(dfn[i]) dfn[i]--;
}
fl=0;work(mid,ed,1);
memset(dfn,0,sizeof(dfn));memset(vis,0,sizeof(vis));
}
}
for(int i=1;i<=n;++i) printf("%d ",ans[i]);
return 0;
}
谁爱调谁调,反正我不调
好像有什么线段树合并、启发式合并、动态开点权值线段树、Splay、树剖、树状数组、链表、二分、扫描线,但是我都不会
于是思考树上差分
首先路径可以表示为
考虑如果在玩家的视角去看,会无法优化,所以站在观察者的视角(全局)去考虑什么样的玩家会对观察者做出贡献。
首先声明这篇题解的重点:
-
统计答案只和
有关,计算答案只和 有关。我们巧妙地通过值相等把答案存在桶里,然后用每个观察员去取它。理解这一点才能理解这一题!!! -
设计一个顺序以统计、清空答案,使子树之间互不影响。这里一般正常的是
的顺序。 -
理解差分贡献。
我们讨论一下:
设观察员在
一、如果
则当
二、如果
设
三、如果
上面两个任意一个都可以处理,就用第一个吧……
注意到能对
现在我们思考统计子树贡献。
不能直接对于每个根求哪些点对
所以我们用一个桶记录答案。考虑一棵子树怎么统计答案,显然是在进入根节点之前和回溯回来以后桶内的差值。所以我们对于每个
考虑怎么累加答案
我们上面说到了,起点、终点满足怎么样的条件才能对
其实考虑我们没有必要知道值,我们只需要在搜索时对于每个点都算一遍,以这个点可能产生的贡献对两个桶(上行和下行)进行更新。所以我们要记录以每个点为起点的路径的条数和以每个点为结尾的边。我们现在在树上
考虑如何清空子树贡献
我们可以在回溯时,利用LCA对所有开头和结尾的贡献值进行暴力还原。因此我们需要记录以每个点为lca的边。具体地,因为我们记录的是边
注意事项
下行时可能为下标可能为负,所以整体加上N。
Code
#include<bits/stdc++.h>
using namespace std;
int read(){
char ch;int x=0,f=1;
while(!isdigit(ch=getchar())){
if(ch=='-') f=-1;
}
while(isdigit(ch)){
x=(x<<1)+(x<<3)+(ch-'0');
ch=getchar();
}
return x*f;
}
const int N=3e5+5;
int n,m,ti[N],f[N][22],bu1[N<<1],bu2[N<<1];
int st[N],ed[N],dep[N],ans[N],dis[N],cnt[N];
vector<int> tr[N],t[N],LCA[N];
int lca(int u,int v){
if(dep[u]<dep[v]) swap(u,v);
for(int i=20;i>=0;--i){
if(dep[f[u][i]]>=dep[v]) u=f[u][i];
}
if(u==v) return u;
for(int i=20;i>=0;--i){
if(f[u][i]!=f[v][i]&&dep[u]==dep[v]) u=f[u][i],v=f[v][i];
}
return f[u][0];
}
void pre(int now,int fa){
f[now][0]=fa;
for(int i=1;i<=20;++i) f[now][i]=f[f[now][i-1]][i-1];
dep[now]=dep[fa]+1;
for(int to:tr[now]){
if(to!=fa) pre(to,now);
}
}
void dfs(int now){
int a=bu1[ti[now]+dep[now]],b=bu2[ti[now]-dep[now]+N];
for(int to:tr[now]){
if(to!=f[now][0]) dfs(to);
}bu1[dep[now]]+=cnt[now];
for(int i:t[now]) bu2[dis[i]-dep[ed[i]]+N]++;
ans[now]+=bu1[ti[now]+dep[now]]-a+bu2[ti[now]-dep[now]+N]-b;
for(int i:LCA[now]) bu1[dep[st[i]]]--,bu2[dis[i]-dep[ed[i]]+N]--;
return;
}
int main(){
freopen("running.in","r",stdin);
freopen("running.out","w",stdout);
n=read(),m=read();int a,b;
for(int i=1;i<n;++i){
scanf("%d %d",&a,&b);tr[a].push_back(b),tr[b].push_back(a);
}dep[1]=1,f[1][0]=1,pre(1,0);int mid;
for(int i=1;i<=n;++i) ti[i]=read();
for(int i=1;i<=m;++i){
st[i]=read(),ed[i]=read(),mid=lca(st[i],ed[i]),dis[i]=dep[st[i]]+dep[ed[i]]-2*dep[mid];
cnt[st[i]]++;t[ed[i]].push_back(i);LCA[mid].push_back(i);
if(dep[mid]+ti[mid]==dep[st[i]]) --ans[mid];
}dfs(1);
for(int i=1;i<=n;++i) printf("%d ",ans[i]);
return 0;
}
蚯蚓
没排序
考虑砍掉的蚯蚓是单调递减的,所以三个单调队列维护一下原序列,砍掉的前半部分,砍掉的后半部分,记录一下偏移量,就做完了。
证明看
#include<bits/stdc++.h>
using namespace std;
int read(){
char ch;int x=0,f=1;
while(!isdigit(ch=getchar())){
if(ch=='-') f=-1;
}
while(isdigit(ch)){
x=(x<<1)+(x<<3)+(ch-'0');
ch=getchar();
}
return x*f;
}queue<long long> a,b,c;
const int N=7e6+5;
const long long inf=1e15;
int n,m,p,u,v,t,po,d[N];
long long cut,cut1,cut2;
bool cmp(int a,int b){
return a>b;
}
int main(){
freopen("earthworm.in","r",stdin);
freopen("earthworm.out","w",stdout);
n=read(),m=read(),p=read(),u=read(),v=read(),t=read();
for(int i=1;i<=n;++i) d[i]=read();
sort(d+1,d+n+1,cmp);//我造
for(int i=1;i<=n;++i) a.push(d[i]);
for(int i=1;i<=m;++i){
cut=-inf;if(!a.empty()) cut=a.front(),po=1;
if(!b.empty()){if(cut<b.front()) cut=b.front(),po=2;}
if(!c.empty()){if(cut<c.front()) cut=c.front(),po=3;}
if(po==1) a.pop();else if(po==2) b.pop();else c.pop();
cut+=i*p-p;cut1=cut*u/v,cut2=cut-cut1;cut1-=i*p,cut2-=i*p;
b.push(cut1),c.push(cut2);
if(i%t==0) printf("%lld ",cut);
}printf("\n");
for(int i=1;i<=n+m;++i){
cut=-inf;if(!a.empty()) cut=a.front(),po=1;
if(!b.empty()){if(cut<b.front()) cut=b.front(),po=2;}
if(!c.empty()){if(cut<c.front()) cut=c.front(),po=3;}
if(po==1) a.pop();else if(po==2) b.pop();else c.pop();
if(i%t==0) printf("%lld ",cut+m*p);
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现