ioi2020集训队作业_IOI2020 集训队作业 Part 1

CF504EMisha and LCP on Tree

直接树剖,变成一堆重链,然后用 SA 求 LCP。

时间复杂度 \(O((n+q)\log n)\)。

看起来细节就很多,以后补。

CF505EMr. Kitayuta vs. Bamboos

最大值最小,二分答案。

倒过来看,一开始有 \(n\) 条高度都为 \(x\) 的竹子。共 \(m\) 天,每天开始,第 \(i\) 条竹子会往下缩 \(a_i\)(smsd),然后你可以 \(k\) 次操作,把一条竹子拔高 \(p\)。然后要整个过程中竹子都没有缩到地底下(高度非负),最后第 \(i\) 条竹子的高度 \(\ge h_i\)(输入给出的那个)。

先看高度非负的限制。对每条竹子都记录它哪天会缩到地底下。用个堆,每次取出最早缩到地底下的拔出来。如果无论如何最早的都会缩下去,就是不行。

然后当所有竹子缩到地底下的天数 \(>m\) 时,就不用管非负的限制了,判断每条竹子需要再操作几次才能最后 \(\ge h_i\)。

然后我个蠢货还想再用一次堆来模拟第二个过程。

这样时间复杂度是 \(O((n+mk\log n)\log nv)\)。

注意到其实堆是没有用的,因为我们只在意 \(\le m\) 天就缩下去的竹子,可以开个 vector 之类的记录下每天缩下去的竹子有哪些。

时间复杂度 \(O((n+mk)\log nv)\)。

下面第一篇代码是堆,第二篇代码是 vector。

#include

using namespace std;

typedef long long ll;

typedef pair PII;

const int maxn=100010;

#define MP make_pair

#define PB push_back

#define lson o<<1,l,mid

#define rson o<<1|1,mid+1,r

#define FOR(i,a,b) for(int i=(a);i<=(b);i++)

#define ROF(i,a,b) for(int i=(a);i>=(b);i--)

#define MEM(x,v) memset(x,v,sizeof(x))

inline ll read(){

char ch=getchar();ll x=0,f=0;

while(ch'9') f|=ch=='-',ch=getchar();

while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();

return f?-x:x;

}

int n,m,k,p,h[maxn],a[maxn],cnt[maxn];

priority_queue,greater > pq;

bool yet_another_check(ll x,int rem){

ll sum=0;

FOR(i,1,n) sum+=max(0ll,(h[i]+1ll*m*a[i]-x+p-1)/p-cnt[i]);

return sum<=rem;

}

bool check(ll x){

while(!pq.empty()) pq.pop();

FOR(i,1,n) cnt[i]=0,pq.push(MP(x/a[i]+1,i));

FOR(i,1,m){

if(pq.top().first<=i) return false;

FOR(j,1,k){

ll v=pq.top().first;

int id=pq.top().second;

pq.pop();

if(v>m) return yet_another_check(x,k-j+1+k*(m-i));

cnt[id]++;

pq.push(MP((x+1ll*cnt[id]*p)/a[id]+1,id));

}

}

return yet_another_check(x,0);

}

int main(){

n=read();m=read();k=read();p=read();

FOR(i,1,n) h[i]=read(),a[i]=read();

ll l=0,r=2e14;

while(l

ll mid=(l+r)>>1;

if(check(mid)) r=mid;

else l=mid+1;

}

printf("%lld\n",l);

}

#include

using namespace std;

typedef long long ll;

const int maxn=100010;

#define MP make_pair

#define PB push_back

#define lson o<<1,l,mid

#define rson o<<1|1,mid+1,r

#define FOR(i,a,b) for(int i=(a);i<=(b);i++)

#define ROF(i,a,b) for(int i=(a);i>=(b);i--)

#define MEM(x,v) memset(x,v,sizeof(x))

inline ll read(){

char ch=getchar();ll x=0,f=0;

while(ch'9') f|=ch=='-',ch=getchar();

while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();

return f?-x:x;

}

int n,m,k,p,h[maxn],a[maxn],cnt[maxn],at;

vector v[maxn];

bool yet_another_check(ll x,int rem){

ll sum=0;

FOR(i,1,n) sum+=max(0ll,(h[i]+1ll*m*a[i]-x+p-1)/p-cnt[i]);

return sum<=rem;

}

bool check(ll x){

FOR(i,1,m) v[i].clear();

FOR(i,1,n){

cnt[i]=0;

ll d=x/a[i]+1;

if(d<=m) v[d].PB(i);

}

at=0;

FOR(i,1,m){

while(at<=m && v[at].empty()) at++;

if(at>m) return yet_another_check(x,k*(m-i+1));

if(at<=i) return false;

FOR(j,1,k){

int id=v[at].back();

v[at].pop_back();

while(at<=m && v[at].empty()) at++;

if(at>m) return yet_another_check(x,k-j+1+k*(m-i));

cnt[id]++;

ll d=(x+1ll*cnt[id]*p)/a[id]+1;

if(d<=m) v[d].PB(id);

}

}

return yet_another_check(x,0);

}

int main(){

n=read();m=read();k=read();p=read();

FOR(i,1,n) h[i]=read(),a[i]=read();

ll l=0,r=2e14;

while(l

ll mid=(l+r)>>1;

if(check(mid)) r=mid;

else l=mid+1;

}

printf("%lld\n",l);

}

CF506EMr. Kitayuta's Gift

CF512DFox And Travelling

CF516DDrazil and Morning Exercise

先用两次 DP 求出每个点的 \(f_i\)。

如果我们以 \(f_i\) 最小的点为根,那么这棵树满足小根堆性质。

考虑反证。若存在一个点 \(v\),父亲为 \(u\),满足 \(f_v

若 \(u\) 的最长路不经过 \(v\),那么 \(v\) 是可以沿着 \(u\) 走的,也就是 \(f_v>f_u\),矛盾。

若 \(u\) 的最长路经过 \(v\),那么根是可以沿着 \(u\) 再到 \(v\) 这么走的,\(f_{rt}>f_u\),和一开始根的 \(f\) 最小矛盾。

那么我们对于每次询问双指针。每次加一个点就合并一堆集合,删一个点根据上面的结论不会影响到连通性。

时间复杂度 \(O(qn\log n)\)。

#include

using namespace std;

typedef long long ll;

typedef pair PII;

const int maxn=200020,mod=998244353;

#define MP make_pair

#define PB push_back

#define lson o<<1,l,mid

#define rson o<<1|1,mid+1,r

#define FOR(i,a,b) for(int i=(a);i<=(b);i++)

#define ROF(i,a,b) for(int i=(a);i>=(b);i--)

#define MEM(x,v) memset(x,v,sizeof(x))

inline ll read(){

char ch=getchar();ll x=0,f=0;

while(ch'9') f|=ch=='-',ch=getchar();

while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();

return f?-x:x;

}

int n,q,el,head[maxn],to[maxn],nxt[maxn],w[maxn],rt=1,p[maxn],fa[maxn],sz[maxn];

ll d[maxn][2],d2[maxn],D[maxn];

bool vis[maxn];

inline bool cmp(int x,int y){

return D[x]

}

inline void add(int u,int v,int w_){

to[++el]=v;nxt[el]=head[u];head[u]=el;w[el]=w_;

}

int getfa(int x){

return x==fa[x]?x:fa[x]=getfa(fa[x]);

}

void unite(int x,int y){

x=getfa(x);y=getfa(y);

if(x==y) return;

fa[x]=y;

sz[y]+=sz[x];

}

void dfs(int u,int f){

for(int i=head[u];i;i=nxt[i]){

int v=to[i];

if(v==f) continue;

dfs(v,u);

if(d[v][0]+w[i]>d[u][0]) d[u][1]=d[u][0],d[u][0]=d[v][0]+w[i];

else d[u][1]=max(d[u][1],d[v][0]+w[i]);

}

}

void dfs2(int u,int f){

for(int i=head[u];i;i=nxt[i]){

int v=to[i];

if(v==f) continue;

d2[v]=max(d2[v],d2[u]+w[i]);

if(d[v][0]+w[i]==d[u][0]) d2[v]=max(d2[v],d[u][1]+w[i]);

else d2[v]=max(d2[v],d[u][0]+w[i]);

dfs2(v,u);

}

}

int main(){

n=read();

FOR(i,1,n-1){

int u=read(),v=read(),w=read();

add(u,v,w);add(v,u,w);

}

dfs(1,0);dfs2(1,0);

FOR(i,1,n) D[i]=max(d[i][0],d2[i]);

FOR(i,1,n) p[i]=i;

sort(p+1,p+n+1,cmp);

q=read();

while(q--){

ll x=read();

FOR(i,1,n) fa[i]=i,sz[i]=1,vis[i]=0;

int cur=n,ans=1;

ROF(i,n,1){

vis[p[i]]=true;

while(D[p[cur]]-D[p[i]]>x){

sz[getfa(p[cur])]--;

vis[p[cur]]=false;

cur--;

}

for(int j=head[p[i]];j;j=nxt[j]) if(vis[to[j]]){

unite(p[i],to[j]);

ans=max(ans,sz[getfa(to[j])]);

}

}

printf("%d\n",ans);

}

}

CF516EDrazil and His Happy Friends

CF521DShop

少数我能自己想出来的集训队作业(

首先,最优策略一定是先赋值,再加,最后乘。

赋值,对于同一个 \(x\) 只需要关心最大的那个,这个可以转成加法。

加法,对于同一个 \(x\) 一定是选最大的那些(包括赋值转换成的加法)。排序后可以变成乘法。

乘法,直接选最大的几个。注意到加法转成的乘法中,对于同一个 \(x\) 的倍率一定递减,所以选最大的几个,对于同一个 \(x\) 选的也一定是个前缀,还是正确的。

排序的比较是个小细节。如果我们把倍率看成 \(\frac{a}{b}\),那么有 \(0\le a-b\le 10^6,1\le b\le 10^{11}\)。那么按 \(\frac{a}{b}-1=\frac{a-b}{b}\) 排序,交叉相乘就可以避免精度误差和爆 long long。

输出方案也要注意赋值、加、乘的顺序。

时间复杂度 \(O(n\log n)\)。(假如 \(n,m,k\) 同阶)

#include

using namespace std;

typedef long long ll;

typedef pair PII;

const int maxn=100010;

#define MP make_pair

#define PB push_back

#define lson o<<1,l,mid

#define rson o<<1|1,mid+1,r

#define FOR(i,a,b) for(int i=(a);i<=(b);i++)

#define ROF(i,a,b) for(int i=(a);i>=(b);i--)

#define MEM(x,v) memset(x,v,sizeof(x))

inline ll read(){

char ch=getchar();ll x=0,f=0;

while(ch'9') f|=ch=='-',ch=getchar();

while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();

return f?-x:x;

}

struct it{

int a;

ll b;

int tp,id;

bool operator

if(a*i.b!=i.a*b) return a*i.b>i.a*b;

return id

}

};

int k,n,m;

ll a[maxn];

it to[maxn];

vector v[maxn],mul;

int main(){

k=read();n=read();m=read();

FOR(i,1,k) a[i]=read();

FOR(i,1,k) to[i].b=1;

FOR(i,1,n){

int t=read(),x=read(),b=read();

if(t==1) to[x]=min(to[x],(it){b,1,1,i});

if(t==2) v[x].PB((it){b,1,2,i});

if(t==3) mul.PB((it){b-1,1,3,i});

}

FOR(i,1,k) if(to[i].a>a[i]) v[i].PB((it){to[i].a-a[i],1,1,to[i].id});

FOR(i,1,k){

sort(v[i].begin(),v[i].end());

FOR(j,0,(int)v[i].size()-1){

mul.PB((it){v[i][j].a,a[i],v[i][j].tp,v[i][j].id});

a[i]+=v[i][j].a;

}

}

sort(mul.begin(),mul.end());

int l=min(m,(int)mul.size());

printf("%d\n",l);

FOR(i,0,l-1) if(mul[i].tp==1) printf("%d ",mul[i].id);

FOR(i,0,l-1) if(mul[i].tp==2) printf("%d ",mul[i].id);

FOR(i,0,l-1) if(mul[i].tp==3) printf("%d ",mul[i].id);

}

CF521ECycling City

首先可以发现,当给定图是仙人掌时无解,因为一条边至多在一个环上。

判断仙人掌可以先搞出 DFS 树,对于非树边进行链上加。如果最后有边的权值 \(\ge 2\),那么就不是仙人掌,否则就是。

同时可以发现当给定图是仙人掌时一定有解。

难点在输出方案。 随便找一条权值 \(\ge 2\) 的边。这条边一定在某个方案中存在。 在非树边中随便找两条覆盖它的。 然后就分情况,发现可以归为一种模拟。

时间复杂度 \(O(n+m)\)。

#include

using namespace std;

typedef long long ll;

const int maxn=444444;

#define FOR(i,a,b) for(int i=(a);i<=(b);i++)

#define ROF(i,a,b) for(int i=(a);i>=(b);i--)

#define MEM(x,v) memset(x,v,sizeof(x))

template

inline void read(T &x){

x=0;

char ch=getchar();bool f=false;

while(ch'9') f|=ch=='-',ch=getchar();

while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();

if(f) x=-x;

}

int n,m,el,head[maxn],to[maxn],nxt[maxn],dfn[maxn],sz[maxn],fa[maxn],dep[maxn],dfs_cnt;

int U[maxn],V[maxn],ncnt,cnt[maxn],at,au,av,bu,bv,ans[3][maxn],al[3],tmp[maxn],tl;

bool ntr[maxn];

inline void add(int u,int v){

to[++el]=v;nxt[el]=head[u];head[u]=el;

}

void dfs1(int u,int f){

dep[u]=dep[fa[u]=f]+1;

dfn[u]=++dfs_cnt;

sz[u]=1;

for(int i=head[u];i;i=nxt[i]){

int v=to[i];

if(v==f) continue;

if(dfn[v]){

ntr[i]=true;

if(dfn[v]

continue;

}

dfs1(v,u);

sz[u]+=sz[v];

}

}

void dfs2(int u,int f){

for(int i=head[u];i;i=nxt[i]){

int v=to[i];

if(v==f || ntr[i]) continue;

dfs2(v,u);

cnt[u]+=cnt[v];

}

}

int main(){

read(n);read(m);

FOR(i,1,m){

int u,v;

read(u);read(v);

add(u,v);add(v,u);

}

FOR(i,1,n) if(!dfn[i]) dfs1(i,0),dfs2(i,0);

FOR(i,1,n) if(cnt[i]>=2){at=i;break;}

if(!at) return puts("NO"),0;

puts("YES");

FOR(i,1,ncnt) if(dfn[U[i]]>=dfn[at] && dfn[U[i]]<=dfn[at]+sz[at]-1 && dfn[fa[at]]>=dfn[V[i]] && dfn[fa[at]]<=dfn[V[i]]+sz[V[i]]-1){

if(au){bu=U[i];bv=V[i];break;}

else au=U[i],av=V[i];

}

int tau=au,tbu=bu;

while(tau!=tbu){

if(dep[tau]

tau=fa[tau];

}

int lwr=tau,upr=dep[av]

for(int i=lwr;i!=upr;i=fa[i]) ans[0][++al[0]]=i;

ans[0][++al[0]]=upr;

for(int i=upr;i!=av;i=fa[i]) ans[1][++al[1]]=i;

ans[1][++al[1]]=av;

for(int i=au;i!=lwr;i=fa[i]) ans[1][++al[1]]=i;

ans[1][++al[1]]=lwr;

for(int i=1,j=al[1];i

for(int i=upr;i!=bv;i=fa[i]) ans[2][++al[2]]=i;

ans[2][++al[2]]=bv;

for(int i=bu;i!=lwr;i=fa[i]) ans[2][++al[2]]=i;

ans[2][++al[2]]=lwr;

for(int i=1,j=al[2];i

FOR(i,0,2){

printf("%d ",al[i]);

FOR(j,1,al[i]) printf("%d ",ans[i][j]);

puts("");

}

return 0;

}

CF526FPudding Monsters

连续段等同于 \(r-l=\max-\min\)。

所以,从小到大枚举右端点 \(r\),线段树对每个 \(i\) 都维护 \(l=i\) 时 \((r-l)-(\max-\min)\) 的值。

这个数肯定是恒 \(\ge 0\) 的,所以当最小值是 \(0\) 时,答案就加上取到最小值的数的个数。

实际上就是同时维护两个单调栈,压栈和弹栈都是对一个区间进行区间加。

时间复杂度 \(O(n\log n)\)。

#include

using namespace std;

typedef long long ll;

const int maxn=1111111;

#define lson o<<1,l,mid

#define rson o<<1|1,mid+1,r

#define FOR(i,a,b) for(int i=(a);i<=(b);i++)

#define ROF(i,a,b) for(int i=(a);i>=(b);i--)

#define MEM(x,v) memset(x,v,sizeof(x))

inline int read(){

int x=0,f=0;char ch=getchar();

while(ch'9') f|=ch=='-',ch=getchar();

while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();

return f?-x:x;

}

struct node{

int mn,mncnt;

node operator+(const node &nd)const{

if(mn

else if(mn>nd.mn) return nd;

else return (node){mn,mncnt+nd.mncnt};

}

}seg[maxn];

int n,a[maxn],stkmx[maxn],tpmx,stkmn[maxn],tpmn,tag[maxn];

ll ans;

inline void setadd(int o,int v){

tag[o]+=v;

seg[o].mn+=v;

}

inline void pushdown(int o){

if(tag[o]){

setadd(o<<1,tag[o]);

setadd(o<<1|1,tag[o]);

tag[o]=0;

}

}

void build(int o,int l,int r){

if(l==r) return void(seg[o]=(node){l,1});

int mid=(l+r)>>1;

build(lson);build(rson);

seg[o]=seg[o<<1]+seg[o<<1|1];

}

void update(int o,int l,int r,int ql,int qr,int v){

if(l>=ql && r<=qr) return setadd(o,v);

int mid=(l+r)>>1;

pushdown(o);

if(mid>=ql) update(lson,ql,qr,v);

if(mid

seg[o]=seg[o<<1]+seg[o<<1|1];

}

int main(){

n=read();

FOR(i,1,n){

int x=read(),y=read();

a[x]=y;

}

build(1,1,n);

stkmx[tpmx=1]=stkmn[tpmn=1]=0;

FOR(i,1,n){

update(1,1,n,1,n,-1);

while(tpmx>1 && a[i]>a[stkmx[tpmx]]) update(1,1,n,stkmx[tpmx-1]+1,stkmx[tpmx],-a[stkmx[tpmx]]),tpmx--;

update(1,1,n,stkmx[tpmx]+1,i,a[i]);

stkmx[++tpmx]=i;

while(tpmn>1 && a[i]

update(1,1,n,stkmn[tpmn]+1,i,-a[i]);

stkmn[++tpmn]=i;

ans+=seg[1].mncnt;

}

printf("%lld\n",ans);

}

CF526GSpiders Evil Plan

CF527EData Center Drama

CF536DTavas in Kansas

CF538GBerserk Robot

CF538HSummer Dichotomy

CF547DMike and Fish

每个横坐标看成一个点,纵坐标看成一个点。输入的点就把对应的横坐标纵坐标连边。

问题变成把边定向,使得每个点入度出度之差不超过 1。

度数为偶数的点,一定是入度出度相等。度数为奇数的点,一定是入度出度相差正好是 1。

建一个虚点,和每个奇点连边。这样奇点变成的偶点,如果能让入度出度相等,那么原来的奇点就满足要求。

奇点有偶数个,所以虚点的度数也是偶数。

对每个联通块跑欧拉回路即可。

时间复杂度,忽略离散化(或之类的东西)是 \(O(n)\)。

#include

using namespace std;

typedef long long ll;

typedef pair PII;

const int maxn=1222222;

#define MP make_pair

#define PB push_back

#define lson o<<1,l,mid

#define rson o<<1|1,mid+1,r

#define FOR(i,a,b) for(int i=(a);i<=(b);i++)

#define ROF(i,a,b) for(int i=(a);i>=(b);i--)

#define MEM(x,v) memset(x,v,sizeof(x))

inline ll read(){

char ch=getchar();ll x=0,f=0;

while(ch'9') f|=ch=='-',ch=getchar();

while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();

return f?-x:x;

}

int n,x[maxn],y[maxn],tmpx[maxn],xl,tmpy[maxn],yl,deg[maxn],el=1,head[maxn],to[maxn],nxt[maxn];

char s[maxn];

inline void add(int u,int v){

to[++el]=v;nxt[el]=head[u];head[u]=el;

}

void dfs(int u){

for(int &i=head[u];i;i=nxt[i]){

int v=to[i];

if(!s[i>>1]){

s[i>>1]=v>xl?'r':'b';

dfs(v);

}

}

}

int main(){

n=read();

FOR(i,1,n){

tmpx[++xl]=x[i]=read();

tmpy[++yl]=y[i]=read();

}

sort(tmpx+1,tmpx+xl+1);xl=unique(tmpx+1,tmpx+xl+1)-tmpx-1;

sort(tmpy+1,tmpy+yl+1);yl=unique(tmpy+1,tmpy+yl+1)-tmpy-1;

FOR(i,1,n){

x[i]=lower_bound(tmpx+1,tmpx+xl+1,x[i])-tmpx;

y[i]=lower_bound(tmpy+1,tmpy+yl+1,y[i])-tmpy;

add(x[i],y[i]+xl);add(y[i]+xl,x[i]);

deg[x[i]]++;deg[y[i]+xl]++;

}

FOR(i,1,xl+yl) if(deg[i]&1) add(i,xl+yl+1),add(xl+yl+1,i);

FOR(i,1,xl+yl+1) dfs(i);

FOR(i,1,n) printf("%c",s[i]);

}

CF547EMike and Friends

拆成两个前缀的询问,离线。

然后发现就是 AC 自动机的 fail 树上单点修改和子树求和。

时间复杂度 \(O((q+\sum|s_i|)\log\sum|s_i|)\)。

#include

using namespace std;

typedef long long ll;

typedef pair PII;

const int maxn=1000100,mod=998244353;

#define MP make_pair

#define PB push_back

#define lson o<<1,l,mid

#define rson o<<1|1,mid+1,r

#define FOR(i,a,b) for(int i=(a);i<=(b);i++)

#define ROF(i,a,b) for(int i=(a);i>=(b);i--)

#define MEM(x,v) memset(x,v,sizeof(x))

inline ll read(){

char ch=getchar();ll x=0,f=0;

while(ch'9') f|=ch=='-',ch=getchar();

while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();

return f?-x:x;

}

struct ques{

int r,k,id,w;

bool operator

}qu[maxn];

int n,Q,tot,ch[maxn][26],at[maxn],fail[maxn],fa[maxn],q[maxn],h,r,el,head[maxn],to[maxn],nxt[maxn],lft[maxn],rig[maxn],dfn;

ll ans[maxn],b[maxn];

char s[maxn];

inline void add(int u,int v){

to[++el]=v;nxt[el]=head[u];head[u]=el;

}

inline void update(int p,ll v){

for(int i=p;i<=tot+1;i+=i&-i) b[i]+=v;

}

inline ll query(int p){

ll s=0;

for(int i=p;i;i-=i&-i) s+=b[i];

return s;

}

inline ll query(int l,int r){

return query(r)-query(l-1);

}

void insert(char *s,int id){

int l=strlen(s+1),now=0;

FOR(i,1,l){

int p=s[i]-'a';

if(!ch[now][p]) ch[now][p]=++tot,fa[tot]=now;

now=ch[now][p];

}

at[id]=now;

}

void build(){

h=1;r=0;

FOR(i,0,25) if(ch[0][i]) q[++r]=ch[0][i];

while(h<=r){

int u=q[h++];

FOR(i,0,25) if(ch[u][i]){

fail[ch[u][i]]=ch[fail[u]][i];

q[++r]=ch[u][i];

}

else ch[u][i]=ch[fail[u]][i];

}

}

void dfs(int u){

lft[u]=++dfn;

for(int i=head[u];i;i=nxt[i]) dfs(to[i]);

rig[u]=dfn;

}

int main(){

n=read();Q=read();

FOR(i,1,n){

scanf("%s",s+1);

insert(s,i);

}

build();

FOR(i,1,tot) add(fail[i],i);

dfs(0);

FOR(i,1,Q){

int l=read(),r=read(),k=read();

qu[2*i-1]=(ques){l-1,k,i,-1};

qu[2*i]=(ques){r,k,i,1};

}

sort(qu+1,qu+2*Q+1);

int cur=1;

FOR(i,1,2*Q){

while(cur<=qu[i].r){

for(int u=at[cur];u;u=fa[u]) update(lft[u],1);

cur++;

}

ans[qu[i].id]+=qu[i].w*query(lft[at[qu[i].k]],rig[at[qu[i].k]]);

}

FOR(i,1,Q) printf("%lld\n",ans[i]);

}

CF549ESasha Circle

CF553EKyoya and Train

CF555ECase of Computer Network

少数自己能想出来的集训队作业(

然后我又没注意图可以不连通蛤蛤蛤(

联通块独立。下面假设图联通。

对于一个边双,显然可以定向成里面的点两两可互达。缩边双后变成棵树。

然后一个限制就是固定一条路径上的方向。

随便选个根,大概记录一下一条边是深度小的连向深度大的,还是相反,判断有没有矛盾就行了。

时间复杂度 \(O(n+m+q\log n)\)。

本来以为是个小清新题,结果写了 4 个 dfs(虽然也不难写

#include

using namespace std;

typedef long long ll;

typedef pair PII;

const int maxn=400040;

#define MP make_pair

#define PB push_back

#define lson o<<1,l,mid

#define rson o<<1|1,mid+1,r

#define FOR(i,a,b) for(int i=(a);i<=(b);i++)

#define ROF(i,a,b) for(int i=(a);i>=(b);i--)

#define MEM(x,v) memset(x,v,sizeof(x))

inline ll read(){

char ch=getchar();ll x=0,f=0;

while(ch'9') f|=ch=='-',ch=getchar();

while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();

return f?-x:x;

}

int n,m,q,U[maxn],V[maxn],el=1,head[maxn],to[maxn],nxt[maxn];

int id[maxn],dfn[maxn],low[maxn],cnt,tot,stk[maxn],tp;

int fa[maxn],dep[maxn],sz[maxn],son[maxn],top[maxn],s1[maxn],s2[maxn];

map vis;

inline void add(int u,int v){

to[++el]=v;nxt[el]=head[u];head[u]=el;

}

void dfs(int u,int f){

stk[++tp]=u;

dfn[u]=low[u]=++cnt;

for(int i=head[u];i;i=nxt[i]){

int v=to[i];

if(i==(f^1)) continue;

if(dfn[v]) low[u]=min(low[u],low[v]);

else dfs(v,i),low[u]=min(low[u],low[v]);

}

if(dfn[u]==low[u]){

tot++;

do{

id[stk[tp]]=tot;

}while(stk[tp--]!=u);

}

}

void dfs2(int u,int f){

dfn[u]=1;

dep[u]=dep[fa[u]=f]+1;

sz[u]=1;

for(int i=head[u];i;i=nxt[i]){

int v=to[i];

if(v==f) continue;

dfs2(v,u);

sz[u]+=sz[v];

if(sz[v]>sz[son[u]]) son[u]=v;

}

}

void dfs3(int u,int topf){

top[u]=topf;

if(son[u]) dfs3(son[u],topf);

for(int i=head[u];i;i=nxt[i]){

int v=to[i];

if(v==fa[u] || v==son[u]) continue;

dfs3(v,v);

}

}

int lca(int u,int v){

while(top[u]!=top[v]){

if(dep[top[u]]

u=fa[top[u]];

}

return dep[u]

}

void dfs4(int u){

dfn[u]=1;

for(int i=head[u];i;i=nxt[i]){

int v=to[i];

if(v==fa[u]) continue;

dfs4(v);

s1[u]+=s1[v];

s2[u]+=s2[v];

}

}

int main(){

n=read();m=read();q=read();

FOR(i,1,m){

U[i]=read();V[i]=read();

add(U[i],V[i]);add(V[i],U[i]);

}

FOR(i,1,n) if(!dfn[i]) dfs(i,0);

MEM(head,0);MEM(to,0);MEM(nxt,0);

el=0;

FOR(i,1,m){

int u=id[U[i]],v=id[V[i]];

if(u!=v && !vis[MP(u,v)]){

vis[MP(u,v)]=vis[MP(v,u)]=true;

add(u,v);add(v,u);

}

}

MEM(dfn,0);

FOR(i,1,cnt) if(!dfn[i]) dfs2(i,0),dfs3(i,i);

while(q--){

int u=id[read()],v=id[read()];

int l=lca(u,v);

s1[u]++;s1[l]--;

s2[v]++;s2[l]--;

}

MEM(dfn,0);

FOR(i,1,cnt) if(!dfn[i]) dfs4(i);

FOR(i,1,cnt) if(s1[i] && s2[i]) return puts("No"),0;

puts("Yes");

}

CF559EGerald and Path

CF566CLogistical Questions

CF566ERestoring Map

CF568CNew Language

CF568ELongest Increasing Subsequence

CF571DCampus

CF571EGeometric Progressions

CF573EBear and Bowling

CF575AFibonotci

CF575ESpectator Riots

CF575IRobots protection

CF576DFlights for Regular Customers

CF576EPainting Edges

CF578EWalking!

CF578FMirror Box

CF582DNumber of Binominal Coefficients

应该也是自己想出来的一题吧(

语言和题解很像是因为我语文太差,借鉴了一下,信我啊(

设 \(f(x)\) 为 \(x\) 分解质因数后 \(p\) 的出现次数。

要求 \(f(\binom{n}{k})=f(n!)-f(k!)-f((n-k)!)\ge\alpha\)。

\(f(n!)\) 其实就是:把 \(n\) 写成 \(p\) 进制,每个前缀(不包括自己)的和。

考虑 \(k\) 加上 \(n-k\) 的过程,注意到每进位一次,这一位会减少 \(p\),前一位会加上 \(1\)。经过冷静思考,\(f\) 就会增加 \(1\)。

所以 \(k\) 加上 \(n-k\) 进了至少 \(\alpha\) 次位就可以了。

其实这一部分当时我是半推半猜,但是看了一波题解说是对的,自己证了一下,就当我是自己想出来的吧(

接下来就很蠢了。直接数位 DP,\(f_{i,j,0/1,0/1}\) 表示两个数都填了前 \(i\) 位,已经进位了 \(j\) 次,需不需要来自后面的进位(如果有肯定是进了 \(1\)),两个数的和有没有顶到上界的方案数。

转移不要枚举两个位分别是啥,因为 \(p\) 太大了。发现情况也就那么几种:顶到上界,进位,不进位之类的,而且每种填的方案数都很好算。

注意到 \(\alpha\le 10^9\) 是吓人的,因为进位次数 \(\le\log_pA\)。

时间复杂度看成 \(O(\log^2A)\) 好了。

代码等会补。(看起来很恶心的样子……)

CF582EBoolean Function

CF585EPresent for Vitalik the Philatelist

\(f_i\) 为 \(\gcd=i\) 的集合个数。

\(g_i\) 为 \(i|\gcd\) 的集合个数。

\(c_i\) 为 \(i\) 的个数。

\(cnt_i\) 为 \(i\) 的倍数的个数。

\[g_i=2^{cnt_i}-1

\]

为了方便,硬点 \(f_1=0\),原来的 \(f_1\) 是:

\[\sum_{i}g_i\mu(i)

\]

对 \(g\) 的影响,也即 \(g_1\) 减去原来的 \(f_1\)。

答案为:

\[\sum_{i}\sum_{j}c_if_j[\gcd(i,j)=1]\\\sum_{i}\sum_{j}c_if_j\sum_{d|\gcd(i,j)}\mu(d)\\\sum_{d}\mu(d)\sum_{d|i}\sum_{d|j}c_if_j\\\sum_{d}\mu(d)cnt_dg_d

\]

直接做,用上“狄利克雷后缀和”的 trick,时间复杂度 \(O(n+v\log\log v)\)。

我也不清楚枚举倍数能不能过(

#include

using namespace std;

typedef long long ll;

typedef pair PII;

const int maxn=10001000,mod=1000000007;

#define MP make_pair

#define PB push_back

#define lson o<<1,l,mid

#define rson o<<1|1,mid+1,r

#define FOR(i,a,b) for(int i=(a);i<=(b);i++)

#define ROF(i,a,b) for(int i=(a);i>=(b);i--)

#define MEM(x,v) memset(x,v,sizeof(x))

inline ll read(){

char ch=getchar();ll x=0,f=0;

while(ch'9') f|=ch=='-',ch=getchar();

while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();

return f?-x:x;

}

int n,v,c[maxn],pw[maxn],pr[maxn/10],pl,mu[maxn],g[maxn],f1,ans;

bool vis[maxn];

void init(int upr){

mu[1]=1;

FOR(i,2,upr){

if(!vis[i]) mu[pr[++pl]=i]=-1;

FOR(j,1,pl){

int k=i*pr[j];

if(k>upr) break;

vis[k]=true;

if(i%pr[j]) mu[i*pr[j]]=-mu[i];

else break;

}

}

FOR(i,1,upr) if(mu[i]==-1) mu[i]=mod-1;

pw[0]=1;

FOR(i,1,upr) pw[i]=(pw[i-1]+pw[i-1])%mod;

}

void is_this_fmt(int *A){

FOR(i,1,pl) ROF(j,v/pr[i],1) A[j]=(A[j]+A[j*pr[i]])%mod;

}

int main(){

n=read();

while(n--){

int x=read();

c[x]++;

v=max(v,x);

}

init(v);

is_this_fmt(c);

FOR(i,1,v) g[i]=(pw[c[i]]-1+mod)%mod;

FOR(i,1,v) f1=(f1+1ll*mu[i]*g[i])%mod;

g[1]=(g[1]-f1+mod)%mod;

FOR(i,1,v) ans=(ans+1ll*mu[i]*c[i]%mod*g[i])%mod;

printf("%d\n",ans);

}

CF585FDigits of Number Pi

CF587DDuff in Mafia

CF587FDuff is Mad

CF590EBirthday

CF594ECutting the Line

CF603EPastoral Oddities

CF605EIntergalaxy Trips

CF607ECross Sum

CF611GNew Year and Cake

CF611HNew Year and Forgotten Tree

CF613EPuzzle Lover

CF626GRaffles

CF627FIsland Puzzle

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值