五一集训题合集
A Simple Task
因为字符集大小只有 \(26\),考虑直接在线段树每个节点维护当前区间的字符个数。排序时直接暴力区间赋值,复杂度会有 \(26\) 的常数。
code
#include<iostream>
using namespace std;
const int N=1e5+10;
int n,q;
#define ls u<<1
#define rs u<<1|1
string s;
int tree[N*4][26],tag[N*4];
void push_up(int u){
for(int i=0;i<26;i++)
tree[u][i]=tree[ls][i]+tree[rs][i];
return;
}
void build(int u,int l,int r){
tag[u]=-1;
if(l==r){
tree[u][s[l]-'a']++;
return;
}
int mid=(l+r)/2;
build(ls,l,mid);
build(rs,mid+1,r);
push_up(u);
return;
}
void push_down(int u,int l,int r){
if(!~tag[u])
return;
int mid=(l+r)/2;
for(int i=0;i<26;i++)
tree[ls][i]=tree[rs][i]=0;
tree[ls][tag[u]]=mid-l+1,tree[rs][tag[u]]=r-mid;
tag[ls]=tag[rs]=tag[u];
tag[u]=-1;
return;
}
void modify(int u,int l,int r,int x,int y,int k){
if(x<=l&&y>=r){
for(int i=0;i<26;i++)
tree[u][i]=0;
tree[u][k]=r-l+1;
tag[u]=k;
return;
}
int mid=(l+r)/2;
push_down(u,l,r);
if(x<=mid)
modify(ls,l,mid,x,y,k);
if(y>mid)
modify(rs,mid+1,r,x,y,k);
push_up(u);
return;
}
int query(int u,int l,int r,int x,int y,int k){
if(x<=l&&y>=r)
return tree[u][k];
int mid=(l+r)/2,res=0;
push_down(u,l,r);
if(x<=mid)
res+=query(ls,l,mid,x,y,k);
if(y>mid)
res+=query(rs,mid+1,r,x,y,k);
return res;
}
char query_ans(int u,int l,int r,int x){
if(l==r)
for(int i=0;i<26;i++)
if(tree[u][i])
return 'a'+i;
int mid=(l+r)/2;
push_down(u,l,r);
if(x<=mid)
return query_ans(ls,l,mid,x);
else
return query_ans(rs,mid+1,r,x);
}
int a[26];
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n>>q>>s;
s=" "+s;
build(1,1,n);
int l,r,k;
while(q--){
cin>>l>>r>>k;
if(k){
for(int i=0;i<26;i++)
a[i]=query(1,1,n,l,r,i);
for(int i=0;i<26;i++){
if(a[i])
modify(1,1,n,l,l+a[i]-1,i);
l+=a[i];
}
}
else{
for(int i=0;i<26;i++)
a[i]=query(1,1,n,l,r,i);
for(int i=25;i>=0;i--){
if(a[i])
modify(1,1,n,l,l+a[i]-1,i);
l+=a[i];
}
}
}
for(int i=1;i<=n;i++)
printf("%c",query_ans(1,1,n,i));
return 0;
}
[HEOI2012] 采花
离线,树状数组维护之前出现两次及以上的颜色数,每次出现 \(2\) 次后在之前的位置加 \(1\),并且把之前的之前加的撤销。
code
#include<cstdio>
#include<algorithm>
template<typename T>
void read(T &x){
x=0;
char ch=getchar();
while(ch<'0'||ch>'9')
ch=getchar();
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return;
}
template<typename T,typename...Args>
void read(T &x,Args &...args){
read(x);
read(args...);
return;
}
#define lowbit(x) (x&-x)
const int N=2e6+10;
int n,c,m,x[N],tree[N];
void modify(int x,int k){
for(int i=x;i<=n;i+=lowbit(i))
tree[i]+=k;
return;
}
int query(int x){
int res=0;
for(int i=x;i>0;i-=lowbit(i))
res+=tree[i];
return res;
}
struct Query{
int l,r,id;
bool operator<(const Query &x)const{
return r<x.r;
}
}q[N];
int pre1[N],pre2[N],ans[N];
int main(){
read(n,c,m);
for(int i=1;i<=n;i++)
read(x[i]);
for(int i=1;i<=m;i++)
read(q[i].l,q[i].r),q[i].id=i;
std::sort(q+1,q+1+m);
int now=1;
for(int i=1;i<=m;i++){
while(now<=q[i].r){
int temp=x[now];
if(pre1[temp]){
modify(pre1[temp],1);
if(pre2[temp])
modify(pre2[temp],-1);
pre2[temp]=pre1[temp];
}
pre1[temp]=now;
now++;
}
ans[q[i].id]=query(q[i].r)-query(q[i].l-1);
}
for(int i=1;i<=m;i++)
printf("%d\n",ans[i]);
return 0;
}
[Ynoi2012] NOIP2015 充满了希望
注意到,任何情况下,查询的答案要么是 \(0\),要么是一个确定的值(即有没有被修改过),这个确定的值必然来自 2 操作。所以维护时间戳,利用树状数组查询区间和。
code
#include<iostream>
#include<vector>
using namespace std;
#define ls u<<1
#define rs u<<1|1
#define lowbit(x) x&-x
typedef long long ll;
const int N=1e6+10;
int n,m,q,opt[N];
class segtree{
private:
int tree[N*4];
void push_down(int u){
if(!tree[u])
return;
tree[ls]=tree[rs]=tree[u];
tree[u]=0;
return;
}
public:
void modify(int u,int l,int r,int x,int y,int k){
if(x<=l&&y>=r){
tree[u]=k;
return;
}
int mid=(l+r)/2;
push_down(u);
if(x<=mid)
modify(ls,l,mid,x,y,k);
if(y>mid)
modify(rs,mid+1,r,x,y,k);
return;
}
int query(int u,int l,int r,int x){
if(tree[u]||l==r)
return tree[u];
int mid=(l+r)/2;
push_down(u);
if(x<=mid)
return query(ls,l,mid,x);
else
return query(rs,mid+1,r,x);
}
}a;
class BIT{
private:
ll tree[N];
public:
void modify(int x,int k){
if(!x)
return;
for(int i=x;i<=m;i+=lowbit(i))
tree[i]+=k;
return;
}
ll query(int x){
if(!x)
return 0;
ll res=0;
for(int i=x;i;i-=lowbit(i))
res+=tree[i];
return res;
}
}b;
int val[N],t[N];
ll ans[N];
vector<pair<int,int>> ask[N];
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n>>m>>q;
for(int i=1,opt,l,r,k;i<=m;i++){
cin>>opt;
if(opt==1){
cin>>l>>r;
int x=a.query(1,1,n,l),y=a.query(1,1,n,r);
a.modify(1,1,n,l,l,y);
a.modify(1,1,n,r,r,x);
}
else if(opt==2){
cin>>l>>r>>val[i];
a.modify(1,1,n,l,r,i);
}
else{
cin>>k;
t[i]=a.query(1,1,n,k);
}
}
for(int i=1,l,r;i<=q;i++){
cin>>l>>r;
ask[r].push_back(make_pair(l,i));
}
for(int i=1;i<=m;i++){
if(t[i])
b.modify(t[i],val[t[i]]);
for(auto k:ask[i])
ans[k.second]=b.query(i)-b.query(k.first-1);
}
for(int i=1;i<=q;i++)
printf("%lld\n",ans[i]);
return 0;
}
[THUPC 2017] 天天爱射击
法一:
主席树 + 二分答案。对每颗子弹设一个版本,二分版本。\(O(n\log^2n)\)(常数过大无法通过)。
法二:
静态区间第 \(k\) 大。
code
#include<cstdio>
#include<vector>
using namespace std;
template<typename T>
void read(T &x){
x=0;
char ch=getchar();
while(ch<'0'||ch>'9')
ch=getchar();
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return;
}
template<typename T,typename...Args>
void read(T &x,Args &...args){
read(x);
read(args...);
return;
}
const int N=2e5+10;
int root[N],tot;
struct segtree{
int sum,ls,rs;
}tree[N*60];
void push_up(int u){
tree[u].sum=tree[tree[u].ls].sum+tree[tree[u].rs].sum;
return;
}
void modify(int &u,int old,int l,int r,int x){
u=++tot;
tree[u]=tree[old];
if(l==r){
tree[u].sum++;
return;
}
int mid=(l+r)/2;
if(x<=mid)
modify(tree[u].ls,tree[old].ls,l,mid,x);
else
modify(tree[u].rs,tree[old].rs,mid+1,r,x);
push_up(u);
return;
}
int query(int u,int old,int l,int r,int k){
if(l==r)
return l;
int mid=(l+r)/2,s=tree[tree[u].ls].sum-tree[tree[old].ls].sum;
if(k<=s)
return query(tree[u].ls,tree[old].ls,l,mid,k);
else
return query(tree[u].rs,tree[old].rs,mid+1,r,k-s);
}
int n,m,ans[N];
struct node{
int x1,x2,s;
}q[N];
vector<int> b[N];
int main(){
read(n,m);
for(int i=1;i<=n;i++)
read(q[i].x1,q[i].x2,q[i].s);
for(int i=1,x;i<=m;i++){
read(x);
b[x].push_back(i);
}
for(int i=1;i<=2e5;i++){
root[i]=root[i-1];
for(int t:b[i])
modify(root[i],root[i],1,m+1,t);
}
for(int i=1;i<=n;i++){
int x1=q[i].x1,x2=q[i].x2,s=q[i].s;
int k=query(root[x2],root[x1-1],1,m+1,s);
ans[k]++;
}
for(int i=1;i<=m;i++)
printf("%d\n",ans[i]);
return 0;
}
Legacy
线段树优化建图。
code
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
template<typename T>
void read(T &x){
x=0;
char ch=getchar();
while(ch<'0'||ch>'9')
ch=getchar();
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return;
}
template<typename T,typename...Args>
void read(T &x,Args &...args){
read(x);
read(args...);
return;
}
typedef long long ll;
const int N=1e5+10;
#define ls (u<<1)
#define rs (u<<1|1)
#define K 4e5
#define inf 4557430888798830399
int n,q,s,a[N],tot_edge,head[N*8];
struct edge{
int to,nxt,w;
}e[N*50];
void add_edge(int u,int v,int w){
e[++tot_edge].to=v;
e[tot_edge].nxt=head[u];
e[tot_edge].w=w;
head[u]=tot_edge;
return;
}
void build(int u,int l,int r){
if(l==r){
a[l]=u;
return;
}
add_edge(u,ls,0),add_edge(u,rs,0);
add_edge(ls+K,u+K,0),add_edge(rs+K,u+K,0);
int mid=(l+r)/2;
build(ls,l,mid);
build(rs,mid+1,r);
return;
}
void modify(int u,int l,int r,int x,int y,int v,int w,bool rev){
if(x<=l&&y>=r){
if(rev)
add_edge(v+K,u,w);
else
add_edge(u+K,v,w);
return;
}
int mid=(l+r)/2;
if(x<=mid)
modify(ls,l,mid,x,y,v,w,rev);
if(y>mid)
modify(rs,mid+1,r,x,y,v,w,rev);
return;
}
priority_queue<pair<ll,int>,vector<pair<ll,int>>,greater<pair<ll,int>>> que;
bool vis[N*8];
ll dis[N*8];
signed main(){
memset(dis,0x3f,sizeof(dis));
read(n,q,s);
build(1,1,n);
for(int i=1;i<=n;i++)
add_edge(a[i],a[i]+K,0),add_edge(a[i]+K,a[i],0);
for(int i=1,opt,x,y,v,w;i<=q;i++){
read(opt);
if(opt==1){
read(x,y,w);
add_edge(a[x]+K,a[y],w);
}
else if(opt==2){
read(v,x,y,w);
modify(1,1,n,x,y,a[v],w,1);
}
else{
read(v,x,y,w);
modify(1,1,n,x,y,a[v],w,0);
}
}
dis[(int)(a[s]+K)]=0;
que.push(make_pair(0,a[s]+K));
while(!que.empty()){
int u=que.top().second;
que.pop();
if(vis[u])
continue;
vis[u]=1;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to,w=e[i].w;
if(dis[v]>dis[u]+w){
dis[v]=dis[u]+w;
que.push(make_pair(dis[v],v));
}
}
}
for(int i=1;i<=n;i++){
if(dis[a[i]]==inf)
printf("-1 ");
else
printf("%lld ",dis[a[i]]);
}
return 0;
}
[NOI2018] 归程
使用 Kruskal 重构树,倍增查找,时间复杂度 \(O(n\log n)\)。
多测一定要记得清 head 数组……
code
#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
using namespace std;
template<typename T>
void read(T &x){
bool f=0;
x=0;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')
f=1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
if(f)
x=~x+1;
return;
}
template<typename T,typename...Args>
void read(T &x,Args &...args){
read(x);
read(args...);
return;
}
template<typename T>
T Min(const T &a,const T &b){
return a<b?a:b;
}
const int N=2e5+10,M=4e5+10;
int t;
struct node{
int u,v,a;
bool operator<(const node &x)const{
return a>x.a;
}
}p[M*2];
struct edge{
int to,nxt,w;
}e[M*2];
int tot_edge,head[N];
void add_edge(int u,int v,int w){
e[++tot_edge].to=v;
e[tot_edge].w=w;
e[tot_edge].nxt=head[u];
head[u]=tot_edge;
return;
}
priority_queue<pair<int,int>,vector<pair<int,int>>,greater<pair<int,int>>> que;
int dis[N*2];
bool vis[N];
void dijkstra(){
dis[1]=0;
que.push(make_pair(0,1));
while(!que.empty()){
int u=que.top().second;
que.pop();
if(vis[u])
continue;
vis[u]=1;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to,w=e[i].w;
if(dis[v]>dis[u]+w){
dis[v]=dis[u]+w;
que.push(make_pair(dis[v],v));
}
}
}
return;
}
int n,m,q,k,s,fa[M*2],w[M*2],f[M*2][21];
int find(int x){
if(fa[x]==x)
return x;
return fa[x]=find(fa[x]);
}
void kruskal(){
int cnt=n;
sort(p+1,p+1+m);
for(int i=1;i<=n*2;i++)
fa[i]=i;
for(int i=1;i<=m;i++){
int x=find(p[i].u),y=find(p[i].v);
if(x==y)
continue;
fa[x]=fa[y]=++cnt;
w[cnt]=p[i].a;
dis[cnt]=Min(dis[x],dis[y]);
f[x][0]=f[y][0]=cnt;
}
for(int i=1;(1<<i)<=cnt;i++)
for(int j=1;j<=cnt;j++)
f[j][i]=f[f[j][i-1]][i-1];
return;
}
int lastans,v0,p0;
int query(int x,int y){
for(int i=20;i>=0;i--)
if(f[x][i]&&w[f[x][i]]>y)
x=f[x][i];
return dis[x];
}
int main(){
freopen("return.in","r",stdin);
freopen("return.out","w",stdout);
read(t);
while(t--){
tot_edge=lastans=0;
memset(w,0,sizeof(w));
memset(f,0,sizeof(f));
memset(dis,0x3f,sizeof(dis));
memset(vis,0,sizeof(vis));
memset(head,0,sizeof(head));
read(n,m);
for(int i=1,l;i<=m;i++){
read(p[i].u,p[i].v,l,p[i].a);
add_edge(p[i].u,p[i].v,l);
add_edge(p[i].v,p[i].u,l);
}
dijkstra();
kruskal();
read(q,k,s);
while(q--){
read(v0,p0);
v0=(v0+k*lastans-1)%n+1;
p0=(p0+k*lastans)%(s+1);
lastans=query(v0,p0);
printf("%d\n",lastans);
}
}
return 0;
}
[SCOI2016] 萌萌哒
倍增+并查集。考虑将区间二进制拆分,之后再合并。\(f_{i,j}\) 表示以 \(i\) 为左端点,长度为 \(2^j\) 的区间的所在连通块的左端点。
code
#include<cstdio>
#include<algorithm>
template<typename T>
void read(T &x){
bool f=0;
x=0;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')
f=1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
if(f)
x=~x+1;
return;
}
template<typename T,typename...Args>
void read(T &x,Args &...args){
read(x);
read(args...);
return;
}
#define mod 1000000007
const int N=1e5+10;
int n,m,f[N][25],l1,r1,l2,r2,ans,maxn;
int find(int x,int k){
if(x==f[x][k])
return x;
return f[x][k]=find(f[x][k],k);
}
void merge(int x,int y,int k){
int a=find(x,k),b=find(y,k);
if(a!=b)
f[a][k]=b;
return;
}
bool flag=1;
int main(){
read(n,m);
maxn=std::__lg(n);
for(int i=1;i<=n;i++)
for(int j=0;j<=maxn;j++)
f[i][j]=i;
while(m--){
read(l1,r1,l2,r2);
for(int j=maxn;j>=0;j--)
if(l1+(1<<j)-1<=r1){
merge(l1,l2,j);
l1+=1<<j;
l2+=1<<j;
}
}
for(int j=maxn;j;j--)
for(int i=1;i+(1<<j)-1<=n;i++){
int x=find(i,j);
merge(i,x,j-1);
merge(i+(1<<(j-1)),x+(1<<(j-1)),j-1);
}
for(int i=1;i<=n;i++)
if(f[i][0]==i){
if(flag){
ans=9;
flag=0;
}
else
ans=(ans*10ll)%mod;
}
printf("%d\n",ans);
return 0;
}
HH 去散步
设边权均为 \(1\),计算邻接矩阵 \(A\) 的 \(k\) 次幂,则 \(A^k_{i,j}\) 表示走 \(k\) 步能连接 \(i\) 和 \(j\) 两点间的路径条数。但这道题要求不走回头路,我们考虑点边互换,记录入边和出边来避免走回头路。
code
#include<cstdio>
#include<cstring>
template<typename T>
void read(T &x){
bool f=0;
x=0;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')
f=1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
if(f)
x=~x+1;
return;
}
template<typename T,typename...Args>
void read(T &x,Args &...args){
read(x);
read(args...);
return;
}
template<typename T>
void write(T x){
if(x<0){
x=~x+1;
putchar('-');
}
if(x<10)
putchar(x+48);
else{
write(x/10);
putchar(x%10+48);
}
return;
}
#define mod 45989
const int N=310;
int cnt;
struct Matrix{
int x[N][N];
Matrix(){
memset(x,0,sizeof(x));
}
Matrix operator*(const Matrix &a){
Matrix res;
for(int i=1;i<=cnt;i++)
for(int j=1;j<=cnt;j++)
for(int k=1;k<=cnt;k++)
res.x[i][j]=(res.x[i][j]+x[i][k]*a.x[k][j]%mod)%mod;
return res;
}
}p;
int n,m,t,a,b,res;
Matrix qpow(Matrix a,int b){
Matrix res;
for(int i=1;i<=cnt;i++)
res.x[i][i]=1;
while(b){
if(b&1)
res=res*a;
a=a*a;
b>>=1;
}
return res;
}
struct edge{
int u,v;
}e[N];
int main(){
read(n,m,t,a,b);
a++,b++;
e[cnt=1].u=0,e[1].v=a;
for(int i=1,u,v;i<=m;i++){
read(u,v);
u++,v++;
e[++cnt].u=u;
e[cnt].v=v;
e[++cnt].u=v;
e[cnt].v=u;
}
for(int i=1;i<=cnt;i++)
for(int j=1;j<=cnt;j++)
if(e[i].v==e[j].u&&i!=(j^1)&&i!=j)
p.x[i][j]=1;
p=qpow(p,t);
for(int i=1;i<=cnt;i++)
if(e[i].v==b)
res=(res+p.x[1][i])%mod;
write(res);
putchar('\n');
return 0;
}
[SDOI2015] 寻宝游戏
将关键点按 dfn 排序为 \(a_1,a_2,a_3,\cdots,a_n\),则包含所有关键点的最小生成树的边权和的 \(2\) 倍为 \(dis(a_1,a_2)+dis(a_2,a_3)+\cdots+dis(a_n,a_1)\)。
实现方面,使用一个 set 来查询前驱后继。这题不开 long long 一分没有。
code
#include<cstdio>
#include<vector>
#include<set>
#include<algorithm>
using namespace std;
template<typename T>
void read(T &x){
bool f=0;
x=0;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')
f=1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
if(f)
x=~x+1;
return;
}
template<typename T,typename...Args>
void read(T &x,Args &...args){
read(x);
read(args...);
return;
}
typedef long long ll;
const int N=1e5+10;
int n,m;
ll ans,d[N];
int dfn[N],son[N],dfncnt,idfn[N],dep[N],siz[N],top[N],fa[N];
struct edge{
int to,w;
};
vector<edge> e[N];
set<int> st;
void dfs1(int u,int f){
siz[u]=1;
fa[u]=f;
dep[u]=dep[f]+1;
for(auto i:e[u]){
int v=i.to,w=i.w;
if(v==f)
continue;
d[v]=d[u]+w;
dfs1(v,u);
siz[u]+=siz[v];
if(siz[v]>siz[son[u]])
son[u]=v;
}
return;
}
void dfs2(int u,int topf){
top[u]=topf;
dfn[u]=++dfncnt;
idfn[dfncnt]=u;
if(!son[u])
return;
dfs2(son[u],topf);
for(auto i:e[u]){
int v=i.to;
if(v==fa[u]||v==son[u])
continue;
dfs2(v,v);
}
return;
}
int lca(int a,int b){
while(top[a]!=top[b]){
if(dep[top[a]]>dep[top[b]])
a=fa[top[a]];
else
b=fa[top[b]];
}
return dep[a]>dep[b]?b:a;
}
ll dis(int a,int b){
a=idfn[a],b=idfn[b];
return d[a]+d[b]-2*d[lca(a,b)];
}
void add(int t){
if(st.empty()){
ans=0;
st.insert(t);
return;
}
if(st.size()==1){
ans=2*dis(*st.begin(),t);
st.insert(t);
return;
}
st.insert(t);
int x,y;
auto it=st.find(t);
if(it==st.begin())
x=*--st.end();
else
x=*--it;
it=st.find(t);
if(it==--st.end())
y=*st.begin();
else
y=*++it;
ans+=dis(x,t)+dis(t,y)-dis(x,y);
return;
}
void del(int t){
if(st.size()<=2){
st.erase(t);
ans=0;
return;
}
if(st.size()==3){
st.erase(t);
ans=2*dis(*st.begin(),*--st.end());
return;
}
int x,y;
auto it=st.find(t);
if(it==st.begin())
x=*--st.end();
else
x=*--it;
it=st.find(t);
if(it==--st.end())
y=*st.begin();
else
y=*++it;
ans-=dis(x,t)+dis(t,y)-dis(x,y);
st.erase(t);
return;
}
int main(){
read(n,m);
for(int i=1;i<n;i++){
int x,y,z;
read(x,y,z);
e[x].push_back({y,z});
e[y].push_back({x,z});
}
dfs1(1,0);
dfs2(1,1);
while(m--){
int t;
read(t);
t=dfn[t];
if(st.find(t)==st.end())
add(t);
else
del(t);
printf("%lld\n",ans);
}
return 0;
}

浙公网安备 33010602011771号