T1 ことりのおやつ(小鸟的点心)
题意:给定一个无向有权图,通过一条边的时间为边权,每过去一单位时间,每一个点都会积累 (q) 毫米雪,初始雪厚 (h_{i}),雪积累到 (l_{i}) 以上就不能行走(起点,终点不算).求出 (s) 到 (t) 的最短时间.
有限制条件的最短路,只需在跑 (Dijkstra) 或 (SPFA) 的时候稍微判断一下即可.
#include <cstdio>
#include <queue>
#define inf (210000000000ll)
typedef long long ll;
inline ll rd(){
ll x=0,p=1;
char a=getchar();
while((a<48||a>57)&&a!='-')a=getchar();
if(a=='-')p=-p,a=getchar();
while(a>47&&a<58)x=(x<<1)+(x<<3)+(a&15),a=getchar();
return x*p;
}
inline ll min(ll x,ll y){return x<y?x:y;}
const int N=100002,M=500002;
struct Edge{
int to,next;ll w;
}edge[M<<1];
int head[N],cnt;
int n,m,s,t;
ll g,q;
ll h[N],l[N];
inline void add(int f,int t,ll w){
edge[++cnt].next=head[f];
edge[cnt].to=t;
edge[cnt].w=w;
head[f]=cnt;
}
ll dis[N];int vis[N];
struct node{
int cur;ll dis;
node(int C=0,ll D=0){cur=C,dis=D;}
bool operator < (const node &y)const{
return y.dis<dis;
}
};
inline ll dijk(int s){
std::priority_queue<node> Q;
for(int i=1;i<=n;i++)dis[i]=inf;
dis[s]=0;
Q.push(node(s));
while(!Q.empty()){
int u=Q.top().cur;Q.pop();
if(vis[u]||h[u]+q*dis[u]>l[u])continue;
vis[u]=1;
for(int i=head[u];i;i=edge[i].next){
int v=edge[i].to;
if(dis[v]>dis[u]+edge[i].w)dis[v]=dis[u]+edge[i].w,Q.push(node(v,dis[v]));
}
}
return dis[t]<=g?dis[t]:-1;
}
int main(){
freopen("oyatsu.in","r",stdin);
freopen("oyatsu.out","w",stdout);
n=rd(),m=rd(),s=rd(),t=rd(),g=rd(),q=rd();
for(int i=1;i<=n;i++){
h[i]=rd(),l[i]=rd();
if(i==s||i==t)h[i]=-inf;
}
for(int i=1;i<=m;i++){
int u=rd(),v=rd();ll w=rd();
add(u,v,w),add(v,u,w);
}
ll ans=dijk(s);
if(ans>0)printf("%lld
",ans);
else puts("wtnap wa kotori no oyatsu desu!");
return 0;
}
T2 教主的魔法
题意:维护序列,支持区间加,查询区间大于等于一个数的个数.
基础分块,每块维护一个单调的 (vector),边角暴力,块内二分.
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <vector>
#define rg register
typedef long long ll;
inline int rd(){
int x=0,p=1;
char a=getchar();
while((a<48||a>57)&&a!='-')a=getchar();
if(a=='-')p=-p,a=getchar();
while(a>47&&a<58)x=(x<<1)+(x<<3)+(a&15),a=getchar();
return x*p;
}
const int N=1000002,S=1002;
int n,q,size,block;
int bl[N],L[N],R[N],add[N],a[N];
std::vector<int> v[S];
inline void upd(int k){
v[k].clear();
for(rg int i=L[k];i<=R[k];i++)v[k].push_back(a[i]);
std::sort(v[k].begin(),v[k].end());
}
inline void update(int l,int r,int k){
if(bl[l]==bl[r]){
for(rg int i=l;i<=r;i++)a[i]+=k;
upd(bl[l]);
return;
}
for(rg int i=l;i<=R[bl[l]];i++)a[i]+=k;upd(bl[l]);
for(rg int i=L[bl[r]];i<=r;i++)a[i]+=k;upd(bl[r]);
for(rg int i=bl[l]+1;i<bl[r];i++)add[i]+=k;
}
inline int query(int l,int r,int k){
int ans=0;
if(bl[l]==bl[r]){
for(rg int i=l;i<=r;i++)ans+=(a[i]+add[bl[l]]<k);
return ans;
}
for(rg int i=l;i<=R[bl[l]];i++)ans+=(a[i]+add[bl[l]]<k);
for(rg int i=L[bl[r]];i<=r;i++)ans+=(a[i]+add[bl[r]]<k);
for(rg int i=bl[l]+1;i<bl[r];i++){
int val=k-add[i],x;
x=std::lower_bound(v[i].begin(),v[i].end(),val)-v[i].begin();
ans+=x;
}
return ans;
}
int main(){
freopen("magic.in","r",stdin);
freopen("magic.out","w",stdout);
n=rd(),q=rd();
size=sqrt(n),block=n/size+(n%size!=0);
for(rg int i=1;i<=block;i++)L[i]=(i-1)*size+1,R[i]=i*size;
R[block]=n;
for(rg int i=1;i<=n;i++)a[i]=rd(),bl[i]=(i-1)/size+1;
for(rg int i=1;i<=block;i++){
for(rg int j=L[i];j<=R[i];j++)v[i].push_back(a[j]);
std::sort(v[i].begin(),v[i].end());
}
while(q--){
char s[3];int l,r,k;
scanf("%s",s);
l=rd(),r=rd(),k=rd();
if(s[0]=='M')update(l,r,k);
else printf("%d
",r-l+1-query(l,r,k));
}
return 0;
}
可能要吸氧气才能过
T3 [BJOI2018]求和
题意:给定一棵树,多组询问,求两点之间所有点的深度的 (k) 次方和.
可以推出:对于两个点,它们之间存在的深度是连续的一段(一个点是它们的 (LCA) )或两段(两点中没有点是它们的 (LCA) ).故可以预处理 (i^{k}) 的前缀和,使用倍增求 (LCA) 就可以了.
#include <cstdio>
typedef long long ll;
inline int rd(){
int x=0,p=1;
char a=getchar();
while((a<48||a>57)&&a!='-')a=getchar();
if(a=='-')p=-p,a=getchar();
while(a>47&&a<58)x=(x<<1)+(x<<3)+(a&15),a=getchar();
return x*p;
}
inline void swap(int &x,int &y){
int t=x;
x=y,y=t;
}
const int N=300002;
const ll mod=998244353;
inline ll fpow(ll b,ll p){
ll ans=1,tmp=b;
while(p){
if(p&1)ans=ans*tmp%mod;
tmp=tmp*tmp%mod;
p>>=1;
}
return ans;
}
struct Edge{
int to,next;
}edge[N<<1];
int head[N],cnt;
int n,q;
int dep[N],f[N][22];
ll x[N][52],s[N][52];
inline void init(int n){
for(int i=1;i<=n;i++){
x[i][0]=1;
for(int j=1;j<=50;j++)x[i][j]=x[i][j-1]*i%mod;
}
for(int k=1;k<=50;k++)
for(int i=1;i<=n;i++)
s[i][k]=(s[i-1][k]+x[i][k])%mod;
}
inline ll query(int l,int r,int k){
if(l==0)l=1;
return (s[r][k]-s[l-1][k]+mod)%mod;
}
inline void add(int f,int t){
edge[++cnt].next=head[f];
edge[cnt].to=t;
head[f]=cnt;
}
inline void dfs(int u,int ft){
dep[u]=dep[ft]+1,f[u][0]=ft;
for(int i=head[u];i;i=edge[i].next){
int v=edge[i].to;
if(v==ft)continue;
dfs(v,u);
}
}
inline 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])u=f[u][i],v=f[v][i];
return f[u][0];
}
int main(){
freopen("sum.in","r",stdin);
freopen("sum.out","w",stdout);
n=rd();
init(n);
for(int i=1;i<n;i++){
int u=rd(),v=rd();
add(u,v),add(v,u);
}
dep[0]=-1;
dfs(1,0);
for(int j=1;j<=20;j++)
for(int i=1;i<=n;i++)
f[i][j]=f[f[i][j-1]][j-1];
q=rd();
while(q--){
int u=rd(),v=rd(),k=rd();
int l=lca(u,v);ll ans;
if(dep[u]<dep[v])swap(u,v);
if(l==v)ans=query(dep[l],dep[u],k);
else ans=((query(dep[l],dep[u],k)+query(dep[l]+1,dep[v],k))%mod);
printf("%lld
",ans);
}
return 0;
}