省选联测 (7~13)
省选联测7 水题#
做过的第二道将询问分块的题。
考虑将询问分块后,处理每个查询时扫一遍前面的修改对查询造成的影响。发现可以将链分成两部分,一部分是被修改过的,一部分没有,设分界点为
设处理的节点为
对于被染色的,我们可以直接从后往前扫,扫的时候特殊处理。
对于重构的话,意识从后往前扫修改,发现修改过的不会再修改,那么就可以
总复杂度
不会树分块。
code
%:pragma GCC optimize(3)
%:pragma GCC optimize("Ofast")
%:pragma GCC optimize("inline")
#include<bits/stdc++.h>
using namespace std;
const int N=2*1e5+10;
int c[N];
struct asd{
int op,u,c;
}a[N];
int head[N],nex[N*2],ver[N*2],tot=0;
void add(int x,int y){
ver[++tot]=y,nex[tot]=head[x],head[x]=tot;
}
int fa[N],d[N],top[N],siz[N],son[N];
void dfs1(int x,int fat){
d[x]=d[fat]+1;
siz[x]=1;
for(int i=head[x];i;i=nex[i]){
int y=ver[i];
if(y==fat) continue;
dfs1(y,x);
siz[x]+=siz[y];
if(siz[son[x]]<siz[y]) son[x]=y;
}
}
void dfs2(int x,int tp){
top[x]=tp;
if(son[x]) dfs2(son[x],tp);
for(int i=head[x];i;i=nex[i]){
int y=ver[i];
if(y!=fa[x] && y!=son[x]){
dfs2(y,y);
}
}
}
int ask_lca(int x,int y){
int fx=top[x],fy=top[y];
while(fx!=fy){
if(d[fx]>d[fy]){
x=fa[fx],fx=top[x];
}
else{
y=fa[fy],fy=top[y];
}
}
if(d[x]<d[y]) return x;
else return y;
}
int B=900,I=900;
int ls[N],rs[N],pos[N];
vector<int> rk[N];
int dep[N],mx[N];
int f[N];
int ans[N];
int __lca[5000][5000];
bool flat[N],vis[N];
int kl[N];
void dfs(int x){
dep[d[x]]=c[x];
int mx1=mx[c[x]];
mx[c[x]]=d[x];
int t1;
if(mx1){
t1=mx1/B;
if(mx1%B) t1++;
f[t1]--;
}
int t2=d[x]/B;
if(d[x]%B) t2++;
f[t2]++;
if(rk[x].size()){
int cnt=rk[x].size();
for(int i=0;i<cnt;i++){
int op=rk[x][i];
int mxd=0;
for(int i=ls[pos[op]];i<op;i++){
if(a[i].op==1){
int lca=__lca[i-ls[pos[op]]][op-ls[pos[op]]];
mxd=max(mxd,d[lca]);
}
}
int sum=0;
for(int i=1;i<=t2;i++) sum+=f[i];
int t3=mxd/B;
if(mxd%B) t3++;
for(int i=1;i<=t3;i++) sum-=f[i];
int qwq=0;
int up=min(d[x],B*t3);
for(int i=mxd+1;i<=up;i++){
int col=dep[i];
if(mx[col]<=up && !vis[col]){
sum++;
vis[col]=1;
kl[++qwq]=col;
}
}
for(int i=1;i<=qwq;i++) vis[kl[i]]=0;
int p=0;
for(int i=op;i>=ls[pos[op]];i--){
if(a[i].op==1){
int lca=__lca[i-ls[pos[op]]][op-ls[pos[op]]];
int col=a[i].c;
if(d[lca]<=p) continue;
else{
p=d[lca];
if(mx[col]<=mxd && !vis[col]){
sum++;
vis[col]=1;
kl[++qwq]=col;
}
}
}
}
for(int i=1;i<=qwq;i++) vis[kl[i]]=0;
ans[op]=sum;
}
}
for(int i=head[x];i;i=nex[i]){
int y=ver[i];
if(y==fa[x]) continue;
dfs(y);
}
if(mx1) f[t1]++;
f[t2]--;
mx[c[x]]=mx1;
dep[x]=0;
}
inline int read(){
int x(0);bool f(0);char ch=getchar();
for(;ch<'0'||ch>'9';ch=getchar()) f^=ch=='-';
for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+(ch^48);
return f?x=-x:x;
}
inline void write(int x){
x<0?x=-x,putchar('-'):0;static short Sta[50],top(0);
do{Sta[++top]=x%10;x/=10;}while(x);
while(top) putchar(Sta[top--]|48);
putchar('\n');
}
signed main(){
freopen("simple.in","r",stdin);
freopen("simple.out","w",stdout);
int T;
T=read();
for(int kkk=1;kkk<=T;kkk++){
int n,q;
n=read(),q=read();
for(int i=2;i<=n;i++){
fa[i]=read();
add(fa[i],i);
}
dfs1(1,0);
dfs2(1,1);
for(int i=1;i<=n;i++) c[i]=read();
for(int i=1;i<=q;i++){
a[i].op=read();
if(a[i].op==1) a[i].u=read(),a[i].c=read();
else a[i].u=read();
}
int t=0;
for(int i=1;i*B<=q;i++){
ls[i]=B*(i-1)+1;
rs[i]=B*i;
t=i;
}
if(rs[t]<q) t++,ls[t]=rs[t-1]+1,rs[t]=q;
for(int i=1;i<=t;i++)
for(int j=ls[i];j<=rs[i];j++)
pos[j]=i;
for(int op=1;op<=t;++op){
int idx=0;
for(int i=ls[op];i<=rs[op];++i){
if(a[i].op==2) rk[a[i].u].push_back(i);
for(int j=i+1;j<=rs[op];j++){
int x=a[i].u,y=a[j].u;
__lca[i-ls[op]][j-ls[op]]=__lca[j-ls[op]][i-ls[op]]=ask_lca(x,y);
}
}
dfs(1);
int qwq=0;
for(int i=rs[op];i>=ls[op];i--){
if(a[i].op==2) rk[a[i].u].clear();
if(a[i].op==1){
int tmp=a[i].u;
int col=a[i].c;
while(tmp){
if(flat[tmp]) break;
flat[tmp]=1;
kl[++qwq]=tmp;
c[tmp]=col;
tmp=fa[tmp];
}
}
}
for(int i=1;i<=qwq;i++) flat[kl[i]]=0;
}
for(int i=1;i<=t;i++){
for(int j=ls[i];j<=rs[i];j++){
if(a[j].op==2) write(ans[j]);
}
}
for(int i=1;i<=tot;i++) head[i]=0;
memset(son,0,sizeof(int)*(n+1));
memset(d,0,sizeof(int)*(n+1));
memset(top,0,sizeof(int)*(n+1));
tot=0;
}
}
NOIP2023模拟20联测41 红楼 ~ Eastern Dream #
给出一个长度为
1 x l k
对于所有满足
2 l r
求
数组下标从
题解:
考虑根号分治,对于
对于
对于重构,直接做两次前缀和更新就可以。
code
#include<bits/stdc++.h>
using namespace std;
const int N=2*1e5+10;
int a[N];
struct asd{
int op,x,y;
int k;
int l,r;
}b[N];
long long f[700][700];
long long sum[700][700];
long long qz[N];
long long ans[N];
int pos[N],l[N],r[N];
long long c[N],sum1[N],d[N],g[N];
inline int read(){
int x(0);char ch=getchar();
for(;ch<'0'||ch>'9';ch=getchar()){}
for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+(ch^48);
return x;
}
inline void write(int x){
x<0?x=-x,putchar('-'):0;static short Sta[50],top(0);
do{Sta[++top]=x%10;x/=10;}while(x);
while(top) putchar(Sta[top--]|48);
putchar('\n');
}
signed main(){
freopen("scarlet.in","r",stdin);
freopen("scarlet.out","w",stdout);
int n,m;
n=read(),m=read();
for(int i=1;i<=n;++i){
a[i]=read();
qz[i]=qz[i-1]+a[i];
}
int B=sqrt(n);
int t=sqrt(m);
for(int i=1;i<=t;++i){
l[i]=(i-1)*t+1;
r[i]=i*t;
}
if(r[t]<m) t++,l[t]=r[t-1]+1,r[t]=m;
for(int i=1;i<=t;++i)
for(int j=l[i];j<=r[i];++j)
pos[j]=i;
for(int p=1;p<=m;++p){
b[p].op=read();
if(b[p].op==1){
b[p].x=read(),b[p].y=read(),b[p].k=read();
if(b[p].x<=B){
int lim=min(b[p].x-1,b[p].y);
for(int i=0;i<=lim;i++) f[b[p].x][i]+=b[p].k;
sum[b[p].x][0]=f[b[p].x][0];
for(int i=1;i<=b[p].x-1;i++) sum[b[p].x][i]=sum[b[p].x][i-1]+f[b[p].x][i];
}
else{
for(int i=1;i<=n;i+=b[p].x){
c[i]+=b[p].k;
if(i+b[p].y+1>n) break;
c[i+b[p].y+1]-=b[p].k;
}
}
}
else{
b[p].l=read(),b[p].r=read();
for(int i=1;i<=B;++i){
long long wr=sum[i][(b[p].r-1)%i]+(b[p].r-1)/i*sum[i][i-1];
int ls=b[p].l-1;
long long wl=sum[i][(ls-1)%i]+(ls-1)/i*sum[i][i-1];
ans[p]+=(wr-wl);
}
ans[p]+=(sum1[b[p].r]-sum1[b[p].l-1]);
for(int i=r[pos[p]-1]+1;i<p;++i){
if(b[i].op==1){
if(b[i].x>B){
long long w=1ll*(min(b[i].x-1,b[i].y)+1)*b[i].k;
long long wr=1ll*min((b[p].r-1)%b[i].x+1,b[i].y+1)*b[i].k+(b[p].r-1)/b[i].x*w;
int ls=b[p].l-1;
long long wl=1ll*(min((ls-1)%b[i].x,b[i].y)+1)*b[i].k+(ls-1)/b[i].x*w;
ans[p]+=(wr-wl);
}
}
}
}
if(r[pos[p]]==p){
for(int i=1;i<=n;++i){
c[i]=c[i-1]+c[i];
d[i]=d[i]+c[i];
sum1[i]=sum1[i-1]+d[i];
}
memset(c,0,sizeof(long long)*(n+1));
}
if(b[p].op==2) printf("%lld\n",ans[p]+qz[b[p].r]-qz[b[p].l-1]);
}
}
省选联测9 战争模拟器#
首先考虑暴力
复杂度
事实上我们发现并不需要保证最大值为
我们只需要求
最后考虑在上面的基础上考虑改变
复杂度
code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1005;
const int inf=LONG_LONG_MAX-10;
int q[N][N],sum[N][N];
int K[N];
int V[N][N],C[N][N];
struct asd{
int v,c;
}b[N][N*30];
int n;
int ask(int l,int r,int p){
return sum[p][r]-sum[p][p-1]-sum[l-1][r]+sum[l-1][p-1];
}
int f[N][N];
int st[N][N*30];
int top[N];
bool amp(asd a,asd b){
return a.v<b.v;
}
int X(int i,int j){
int kl=b[i][j].v;
return kl;
}
int Y(int i,int j){
int kl=b[i][j].c;
return kl;
}
signed main(){
freopen("war.in","r",stdin);
freopen("war.out","w",stdout);
scanf("%lld",&n);
for(int i=1;i<=n;++i)
for(int j=i;j<=n;++j)
scanf("%lld",&q[i][j]);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++){
sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+q[i][j];
}
int mx=0;
for(int i=1;i<=n;++i){
scanf("%lld",&K[i]);
mx=max(mx,K[i]);
for(int j=1;j<=K[i];++j) scanf("%lld%lld",&b[i][j].v,&b[i][j].c);
sort(b[i]+1,b[i]+K[i]+1,amp);
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(i<=j) f[i][j]=-inf;
else f[i][j]=0;
}
}
__int128 it=1;
for(int i=1;i<=n;i++){
int tp=0;
for(int j=1;j<=K[i];j++){
while(tp>1 && it* (Y(i,st[i][tp])-Y(i,st[i][tp-1])) * (X(i,j)-X(i,st[i][tp-1])) >= it* (Y(i,j)-Y(i,st[i][tp-1])) * (X(i,st[i][tp])-X(i,st[i][tp-1])) ) tp--;
st[i][++tp]=j;
}
top[i]=tp;
int Q=ask(i,i,i);
int pos=1;
while(pos+1<=tp && it* (Y(i,st[i][pos+1])-Y(i,st[i][pos])) <= it* Q * (X(i,st[i][pos+1])-X(i,st[i][pos]))) pos++;
f[i][i]=b[i][st[i][pos]].v*Q-b[i][st[i][pos]].c;
}
for(int l=n;l>=1;l--){
for(int p=l;p<=n;p++){
int pos=1;
for(int r=p;r<=n;r++){
int Q=ask(l,r,p);
while(pos+1<=top[p] && it* (Y(p,st[p][pos+1])-Y(p,st[p][pos])) <= it* Q * (X(p,st[p][pos+1])-X(p,st[p][pos])) ) pos++;
f[l][r]=max(f[l][r],f[l][p-1]+f[p+1][r]+b[p][st[p][pos]].v*Q-b[p][st[p][pos]].c);
}
}
}
printf("%lld",f[1][n]);
}
省选联测9 种树#
首先求出每个点的期望深度
维护前缀可以
设
对于编号在
注意
code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+10;
const int mod=1e9+7;
int a[N],b[N],c[N];
int dep[N];
int mgml(int x,int p){
int ans=1;
while(p){
if(p&1) ans=(ans*x)%mod;
x=(x*x)%mod;
p>>=1;
}
return ans;
}
int dlca[N];
signed main(){
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
int n,q;
scanf("%lld%lld",&n,&q);
for(int i=1;i<n;i++) scanf("%lld",&a[i]),b[i]=b[i-1]+a[i];
for(int i=1;i<=n;i++) scanf("%lld",&c[i]);
int sum=0;
for(int i=1;i<=n;i++){
dep[i]=(sum*mgml(b[i-1],mod-2)%mod+c[i])%mod;
if(i==1) dep[i]=0;
sum=(sum+a[i]*(dep[i]+c[i])%mod)%mod;
}
for(int u=2;u<=n;u++){
dlca[u]=dlca[u-1]*b[u-2]%mod*mgml(b[u],mod-2)%mod;
dlca[u]=dlca[u]*((2*a[u-1]%mod+b[u-2])%mod)%mod*mgml(b[u-2],mod-2)%mod;
int x=u-1;
int px=a[x]*a[x]%mod*mgml(b[u-1]*b[u]%mod,mod-2)%mod;
dlca[u]=(dlca[u]+px*dep[x]%mod)%mod;
}
for(int u=1;u<=n;u++){
int op=a[u]*mgml(b[u],mod-2)%mod;
dlca[u]=(dlca[u]+op*dep[u]%mod)%mod;
}
for(int i=1;i<=q;i++){
int u,v;
scanf("%lld%lld",&u,&v);
if(u==v){
printf("0\n");
continue;
}
if(u==1){
printf("%lld\n",dep[v]);
continue;
}
if(u>v) swap(u,v);
int ans=(dep[u]+dep[v]-2*dlca[u]%mod+2*mod)%mod;
printf("%lld\n",ans);
}
}
省选联测11 Giao 徽的烤鸭 #
树形
所以转移为
code
// ubsan: undefined
// accoders
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=5005;
int w[N],v[N];
int head[N],nex[N*2],ver[N*2],tot=0;
void add(int x,int y){
ver[++tot]=y,nex[tot]=head[x],head[x]=tot;
}
int f[N][N];
int n;
int siz[N];
void dfs(int x,int fa){
f[x][0]=0;
for(int i=1;i<=n;i++){
f[x][i]=v[i-1]-w[x];
}
for(int i=head[x];i;i=nex[i]){
int y=ver[i];
if(y==fa) continue;
dfs(y,x);
}
for(int i=head[x];i;i=nex[i]){
int y=ver[i];
if(y==fa) continue;
f[x][0]+=max(f[y][1],f[y][0]);
for(int j=1;j<=n;j++) f[x][j]+=max({f[y][j],f[y][j-1],f[y][j+1]});
}
// for(int i=n-1;i>=1;i--) f[x][i]=max(f[x][i+1],f[x][i]);
}
signed main(){
freopen("duck.in","r",stdin);
freopen("duck.out","w",stdout);
scanf("%lld",&n);
for(int i=1;i<=n;i++) scanf("%lld",&w[i]);
for(int i=0;i<n;i++) scanf("%lld",&v[i]);
for(int i=1;i<n;i++){
int x,y;
scanf("%lld%lld",&x,&y);
if(y==0) cerr<<"fk ";
add(x,y),add(y,x);
}
dfs(1,0);
int ans=0;
for(int i=0;i<=n;i++) ans=max(ans,f[1][i]);
printf("%lld",ans);
}
省选联测11 GA Dance of Fire and Ice #
首先根据原根可以讲乘法转化为加法,因为原根有这样的性质:
假如
所以将
code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6;
const int M=2*1e5+10;
int a[N],b[N];
int top1=0,top2=0;
int phi[N],prime[N];
bool nprime[N];
int p,n,g;
int lg[N];
bitset<M> bt;
void get_prime(){
phi[1]=1;
for(int i=2;i<=p;i++){
if(!nprime[i]){
prime[++prime[0]]=i;
phi[i]=i-1;
}
for(int j=1;j<=prime[0] && i*prime[j]<=p;j++){
int k=i*prime[j];
nprime[k]=1;
if(i%prime[j]==0){
phi[k]=prime[j]*phi[i];
break;
}
else phi[k]=(prime[j]-1)*phi[i];
}
}
}
int gcd(int a,int b){
if(b==0) return a;
else return gcd(b,a%b);
}
int qpow(int x,int p,int mod){
int ans=1;
while(p){
if(p&1) ans=(ans*x)%mod;
x=(x*x)%mod;
p>>=1;
}
return ans;
}
int vi[N],tp=0;
bool check(int x,int m){
if(gcd(x,m)!=1) return 0;
for(int i=1;i<=tp;i++){
// cout<<qpow(x,phi[m]/vi[i],m)<<" "<<x<<" "<<phi[m]/vi[i]<<endl;
if(qpow(x,phi[m]/vi[i],m)%m==1) return 0;
}
return 1;
}
int sum[N];
signed main(){
freopen("dance.in","r",stdin);
freopen("dance.out","w",stdout);
scanf("%lld%lld",&p,&n);
nprime[1]=1;
get_prime();
// cout<<phi[p]<<endl;
int tmp=p;
for(int i=2;i*i<=tmp;i++){
if(phi[p]%i==0){
if(!nprime[i]) vi[++tp]=i;
if(phi[p]/i!=i && !nprime[phi[p]/i]) vi[++tp]=phi[p]/i;
}
}
int t=sqrt(sqrt(p))+1;
for(int i=2;;i++){
if(check(i,p)){
g=i;
break;
}
}
for(int i=1;i<=p-1;i++){
int x=qpow(g,i,p);
lg[x]=i;
}
bt[0]=1;
int vis0=0;
for(int i=1;i<=n;i++){
int op,x;
scanf("%lld%lld",&op,&x);
x%=p;
if(x==0){
vis0=1;
continue;
}
if(!op) bt[lg[x]]=1;
else sum[lg[x]]++;
}
for(int x=1;x<p;x++){
if(sum[x]){
for(int j=1;j<=sum[x];j++){
std::bitset<M> nnext = bt;
nnext=bt|(bt>>x)|(bt<<(p-x-1));
if (nnext == bt) {
break;
} else {
bt = nnext;
}
}
}
}
int ans=0;
for(int i=0;i<p-1;i++) if(bt[i]) ans++;
cout<<ans+(vis0==1)<<endl;
}
省选联测12 硬币#
我们考虑这个序列是
首先第
现在考虑枚举每个数时,剩下的数都是质数。
假如枚举到一个合数
code
%:pragma GCC optimize(3)
%:pragma GCC optimize("Ofast")
%:pragma GCC optimize("inline")
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+100;
int ans[N];
int w[N];
signed main(){
freopen("coins.in","r",stdin);
freopen("coins.out","w",stdout);
for(int i=1;i<=1e6;i++) ans[i]=w[i]=i*i+1;
for(int i=1;i<=1e6;i++){
if(w[i]==1) continue;
int k=w[i];
for(int j=i;j<=1e6;j+=k){
while(w[j]%k==0) w[j]/=k;
ans[j]=min(ans[j],k);
}
// cout<<i<<endl;
}
int q;
scanf("%lld",&q);
for(int i=1;i<=q;i++){
int x;
scanf("%lld",&x);
if(ans[x]==x*x+1){
printf("-1\n");
}
else{
printf("%lld %lld\n",ans[x],(x*x+1)/ans[x]);
}
}
}
省选联测12 猜数#
首先有转移
直接转移发现是
但是发现对于区间
那么我们可以先枚举
打表发现,决策点在距离右侧
还可以打表,讲序列差分后压成
compress
#include <bits/stdc++.h>
using namespace std;
const char s[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+-";
int main() {
freopen("out.out", "r", stdin);
freopen("compress.out", "w", stdout);
int x;
while (scanf("%d,", &x) != EOF) {
putchar(s[x % 64]);
putchar(s[x / 64 % 64]);
putchar(s[x / 64 / 64 % 64]);
}
return 0;
}
code
#include<bits/stdc++.h>
using namespace std;
const int N=50005;
const int S=5005;
const int M=5100;
int f[50001];
int g[5500][5500];
int st[N],l,r;
signed main(){
freopen("guess.in","r",stdin);
freopen("guess.out","w",stdout);
int n;
scanf("%d",&n);
long long sum=0;
for(int j=2;j<=n;j++){
int p=j;
l=1,r=0;
int lim=max(1,j-S-1);
int mn=INT_MAX;
memset(g[j%M],0,sizeof(g[j%M]));
for(int i=j-1;i>=lim;i--){
while(p>i && g[j%M][(p+1)%M]<g[(p-1)%M][i%M]) p--;
while(st[l]>p && l<=r) l++;
while(l<=r && st[r]+g[j%M][(st[r]+1)%M] > i+g[j%M][(i+1)%M]) r--;
st[++r]=i;
g[j%M][i%M]=min(st[l]+g[j%M][(st[l]+1)%M],p+1+g[p%M][i%M]);
mn=min(mn,max(g[j%M][(i+1)%M],f[i-1])+i);
}
f[j]=mn;
sum+=f[j];
}
printf("%lld",sum);
}
省选联测12 猜数#
设
然后发现
不妨将每种状态都倒换成
这样每个状态都对应一个数,我们设
最后减去
裴蜀定理:设
为不都为 的整数,则 有解的充要条件是 。
那么推广到多个数,设
考虑怎么做:
方法一:暴力枚举
方法二:同余最短路,就是相当于 「至少要拼几次才能拼出模
然后根号分治,对于
code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=20;
const int M=2*1e6;
int a[N];
int n,m;
int f[N];
int g[M];
void init(){
f[0]=1;
for(int i=1;i<=n;i++) f[i]=f[i-1]*i*2;
}
int ask(){
int ans=0;
for(int i=1;i<=n;i++) ans+=a[i]*f[i-1];
return ans;
}
int query(int x){
int ans=0;
for(int i=1;i<=n;i++){
ans+=(x%(2*i));
x/=(2*i);
}
return ans;
}
void solve(){
scanf("%lld%lld",&n,&m);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
init();
int p=ask();
int d=f[n]-1;
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++) scanf("%lld",&a[j]);
int sa=ask();
d=__gcd(d,sa);
}
if(d<=sqrt(f[n])){
queue<int> q;
memset(g,-1,sizeof(g));
for(int i=0;i<n;i++){
q.push(f[i]%d);
g[f[i]%d]=1;
}
while(!q.empty()){
int x=q.front();
q.pop();
for(int i=0;i<n;i++){
int v=(x+f[i])%d;
if(g[v]==-1){
g[v]=g[x]+1;
q.push(v);
}
}
}
printf("%lld\n",min(g[p%d],query(p)));
}
else{
int ans=query(p);
for(int x=(p%d ? p%d : d);x<f[n];x+=d){
ans=min(ans,query(x));
}
printf("%lld\n",ans);
}
}
signed main(){
freopen("card.in","r",stdin);
freopen("card.out","w",stdout);
solve();
}
省选联测13 树上横跳#
楼房重建的思想,首先倍增预处理出来区间最小值和区间答案,那么考虑将上下两个区间合并,首先上区间直接算入答案就可以,设上区间最小值为
code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=3*1e5+10;
int n,m,t;
int head[N],nex[N*2],ver[N*2],edge[N*2],tot=0;
void add(int x,int y,int w){
ver[++tot]=y,nex[tot]=head[x],head[x]=tot,edge[tot]=w;
}
struct T2{
int dfn[N],num=0,fa[N],siz[N];
void dfs(int x,int fat){
fa[x]=fat;
dfn[x]=++num;
siz[x]=1;
for(int i=head[x];i;i=nex[i]){
int y=ver[i];
if(y==fat) continue;
dfs(y,x);
siz[x]+=siz[y];
}
}
int mp[3005][3005];
int anss[3005][3004];
int st[N],top,dist[N],rk[N];
int f[N][50],g[N][50];
void dfs1(int x,int fat){
st[++top]=x;
rk[x]=top;
for(int i=head[x];i;i=nex[i]){
int y=ver[i];
if(y==fat) continue;
dist[top+1]=dist[top]+edge[i];
dfs1(y,x);
}
}
int cnt[N],tp=0;
void init(){
dfs(1,0);
dfs1(1,0);
int op=top;
for(int i=1;i<=n;i++){
while(tp>0 && st[cnt[tp]]>st[i]){
f[cnt[tp]][0]=i;
tp--;
}
cnt[++tp]=i;
}
while(tp){
f[cnt[tp]][0]=n;
tp--;
}
for(int i=1;i<=n;i++) g[i][0]=(dist[f[i][0]]-dist[i])*st[i];
int t=log2(n)+1;
for(int j=1;j<=t;j++){
for(int i=1;i<=n;i++){
f[i][j]=f[f[i][j-1]][j-1];
g[i][j]=g[i][j-1]+g[f[i][j-1]][j-1];
}
}
}
void solve(){
for(int p=1;p<=m;p++){
int x,y;
scanf("%lld%lld",&x,&y);
if(dfn[y]>=dfn[x] && dfn[y]<=dfn[x]+siz[x]-1){
int fx=rk[x],fy=rk[y];
int ans=0;
for(int i=t;i>=0;i--){
if(f[fx][i]<=fy){
ans+=g[fx][i];
fx=f[fx][i];
}
}
ans+=st[fx]*(dist[fy]-dist[fx]);
printf("%lld\n",ans);
}
else printf("-1\n");
}
}
}T;
int dep[N],dist[N],fa[N][50],mn[N][50],f[N][50];
int dfn[N],num=0,siz[N];
int merge(int lim,int x,int k){
if(mn[x][k]>=lim) return (dist[x]-dist[fa[x][k]])*lim;
if(fa[x][k]<lim) return f[x][k];
if(mn[fa[x][k-1]][k-1]>=lim) return lim*(dist[fa[x][k-1]]-dist[fa[x][k]])+merge(lim,x,k-1);
else return f[x][k]-f[fa[x][k-1]][k-1]+merge(lim,fa[x][k-1],k-1);
}
void dfs(int x,int fat){
dfn[x]=++num;
siz[x]=1;
dep[x]=dep[fat]+1;
fa[x][0]=mn[x][0]=fat;
for(int j=1;j<=t;j++){
int i=x;
fa[i][j]=fa[fa[i][j-1]][j-1];
mn[i][j]=min(mn[i][j-1],mn[fa[i][j-1]][j-1]);
f[i][j]=f[fa[i][j-1]][j-1]+merge(mn[fa[i][j-1]][j-1],i,j-1);
}
for(int i=head[x];i;i=nex[i]){
int y=ver[i];
if(y==fat) continue;
dist[y]=dist[x]+edge[i];
f[y][0]=edge[i]*x;
dfs(y,x);
siz[x]+=siz[y];
}
}
struct asd{
int x,k;
}st[N];
int solve(int x,int y){
int top=0;
int tmp=y;
for(int j=t;j>=0;j--){
if(dep[fa[tmp][j]]>=dep[x]){
st[++top]={tmp,j};
tmp=fa[tmp][j];
}
}
int ans=f[st[top].x][st[top].k];
int lim=mn[st[top].x][st[top].k];
for(int i=top-1;i>=1;i--){
ans+=merge(lim,st[i].x,st[i].k);
lim=min(lim,mn[st[i].x][st[i].k]);
}
return ans;
}
int du[N];
signed main(){
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
scanf("%lld%lld",&n,&m);
t=log2(n)+1;
for(int i=1;i<n;i++){
int x,y,w;
scanf("%lld%lld%lld",&x,&y,&w);
add(x,y,w),add(y,x,w);
du[x]++,du[y]++;
}
int op=0;
for(int i=1;i<=n;i++){
if(du[i]>2) op=1;
}
if(!op){
T.init();
T.solve();
return 0;
}
dfs(1,0);
for(int i=1;i<=m;i++){
int x,y;
scanf("%lld%lld",&x,&y);
if(dfn[y]>=dfn[x] && dfn[y]<=dfn[x]+siz[x]-1){
printf("%lld\n",solve(x,y));
}
else printf("-1\n");
}
}
省选联测13 定价#
首先考虑暴力,那么就是假如上一位有若干一,那么这一位需要找到最高位的上一位为一这一位不能为一的位(如果没有从第零位开始),然后从那一位向上找到第一位可以位一的但上一位不为一的位,然后选它并将其下面的一全部清掉。
考虑怎么优,我们维护一个栈,里面存的是我们选的那些位置的一,每一个数维护一个
将上面的过程转化为我们通过
code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1005;
const int M=1000005;
const int mod=1e9+7;
bitset<N> bt[M];
set<int> s[N];
struct asd{
int op,r,c;
}a[M];
int qz[M],cnt=0;
int st[M],top=0;
unordered_map<int,int> mp;
struct qwe{
int p,k;
friend bool operator <(qwe a,qwe b){
if(a.p==b.p) return a.k<b.k;
return a.p>b.p;
}
}b[M];
priority_queue<qwe> ads,dels;
inline void insert(qwe x){
ads.push(x);
}
inline void dele(qwe x){
dels.push(x);
while(!dels.empty() && !ads.empty() & ads.top().k==dels.top().k && ads.top().p==dels.top().p){
ads.pop();
dels.pop();
}
}
inline qwe begin(){
while(!dels.empty() && !ads.empty() && ads.top().k==dels.top().k && ads.top().p==dels.top().p){
ads.pop();
dels.pop();
}
return ads.top();
}
int siz(){
if(ads.empty()) return 0;
return 1;
}
inline void clear(){
while(!dels.empty()) dels.pop();
while(!ads.empty()) ads.pop();
}
inline int qpow(int x,int p){
int ans=1;
while(p){
if(p&1) ans=(ans*x)%mod;
x=(x*x)%mod;
p>>=1;
}
return ans;
}
bool vis[M];
signed main(){
// freopen("2-1.in","r",stdin);
// freopen("price.out","w",stdout);
freopen("price.in","r",stdin);
freopen("price.out","w",stdout);
int n,m,q;
scanf("%lld%lld%lld",&n,&m,&q);
for(int i=1;i<=q;i++){
scanf("%lld",&a[i].op);
if(a[i].op==1){
scanf("%lld%lld",&a[i].r,&a[i].c);
a[i].c=m-a[i].c;
qz[++cnt]=a[i].c;
}
}
sort(qz+1,qz+cnt+1);
// cout<<cnt<<endl;
cnt=unique(qz+1,qz+cnt+1)-(qz+1);
// return 0;
for(int i=1;i<=cnt;i++){
// bt[i].set(M,1);
mp[qz[i]]=i;
b[i]={0,0};
}
for(int i=1;i<=q;i++){
if(a[i].op==1) a[i].c=mp[a[i].c];
}
for(int p=1;p<=q;p++){
if(a[p].op==1){
if(bt[a[p].c][a[p].r]){
s[a[p].r].erase(a[p].c);
bt[a[p].c][a[p].r]=0;
}
else{
s[a[p].r].insert(a[p].c);
bt[a[p].c][a[p].r]=1;
}
}
else{
top=0;
clear();
int getans=0;
int ans=0,anss=0;
if(s[1].size()){
for(int i=1;i<=n;i++){
int mx=0;
while(siz()){
qwe s=begin();
dele(s);
// cout<<"w "<<s.p<<" "<<dels.top().p<<" "<<ads.top().p<<" "<<i<<" "<<" "<<dels.top().k<<" "<<ads.top().k;
if(s.p<=i){
// cout<<" h ";
if(s.p==i) mx=max(mx,s.k);
b[s.k]={0,0};
}
else{
insert(s);
break;
}
}
auto op=s[i].lower_bound(mx);
for(;op!=s[i].end();op++){
if(!vis[*op]) break;
}
if(op==s[i].end()){
getans=1;
break;
}
while(st[top]<*op && top>=1){
vis[st[top]]=0;
if(b[st[top]].k){
dele(b[st[top]]);
b[st[top]]={0,0};
}
anss=(anss-qpow(2,qz[st[top]])+mod)%mod;
top--;
}
st[++top]=*op;
vis[st[top]]=1;
anss=(anss+qpow(2,qz[st[top]]))%mod;
int wh=(~bt[*op])._Find_next(i);
if(wh<=n){
if(b[*op].p){
dele(b[*op]);
}
b[*op]={wh,*op};
insert(b[*op]);
}
ans=(ans+anss)%mod;
}
if(getans==1) printf("-1\n");
else printf("%lld\n",ans);
while(top){
b[st[top]]={0,0};
vis[st[top]]=0;
top--;
}
}
else{
printf("-1\n");
}
}
}
}
作者:bloss
出处:https://www.cnblogs.com/jinjiaqioi/p/17953270
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效