2019年1月5日
今日小結:今天還是主要做的樹鏈剖分和線段樹的題目,鞏固了一下,最近沒有什麽特別想學的知識點,明天準備復習一下二分圖的有關知識。
一. 完成的題目:
洛谷P1607,洛谷P3128,SP375,CF438D,洛谷P3398,洛谷P2146
二.
1.當日完成題目數:6道。
2. 未完成6個題目的原因:
3. 復習的知識點:樹鏈剖分,線段樹,LCA。
4.不會題目:洛谷P3313
三:
1. 洛谷P1607 [USACO09FEB]廟會班車Fair Shuttle
題目描述
逛逛集市,兌兌獎品,看看節目對農夫約翰來說不算什麽,可是他的奶牛們非常缺乏鍛煉——如果要逛完一整天的集市,他們一定會筋疲力盡的。所以為了讓奶牛們也能愉快地逛集市,約翰準備讓奶牛們在集市上以車代步。但是,約翰木有錢,他租來的班車只能在集市上沿直線跑一次,而且只能停靠\(N(1 ≤N≤20000)\)
由於班車容量有限,可能載不下所有想乘車的奶牛們,此時也允許小裏的一部分奶牛分開乘坐班車。約翰經過調查得知班車的容量是\(C(1≤C≤100)\),請你幫助約翰計劃一個盡可能滿足更多奶牛願望的方案。
輸入輸出格式
輸入格式:
【輸入】
第一行:包括三個整數:\(K\),\(N\)和\(C\),彼此用空格隔開。
第二行到\(K+1\)行:在第\(i+1\)行,將會告訴你第\(i\)
此用空格隔開。
輸出格式:
【輸出】
第一行:可以坐班車的奶牛的最大頭數。
輸入輸出樣例
輸入樣例#1:
8 15 3
1 5 2
13 14 1
5 8 3
8 14 2
14 15 1
9 12 1
12 15 2
4 6 1
輸出樣例#1:
10
說明
【樣例說明】
班車可以把\(2\)頭奶牛從\(1\)送到\(5\),\(3\)頭奶牛從\(5\)送到\(8\),\(2\)頭奶牛從\(8\)送到\(14\),\(1\)頭奶牛從\(9\)送到\(12\),\(1\)頭奶牛從\(13\)送到\(14\),\(1\)頭奶牛從\(14\)
思路:
題意是給你幾堆奶牛,每個奶牛都有想去的地方,給你一個汽車,汽車有一個容量和固定的行走路線,詢問最多能搭載的奶牛的數量。
我們來考慮一種貪心的思想,按右端點從小到大排一遍序,為什麽呢?後面說,然後對排好序的每堆奶牛依次進行遍歷,如果當前有空位,空位大於這堆奶牛的數量,那就全上去,不然的話,就能上去幾個就上去幾個。這樣下來的話,結果一定是最優的,其正確性不難證明,因為剛開始我們對每堆奶牛要到的地方從小到大排了序(即終點),那麽每個位置最多只有一次奶牛上車,而且這些奶牛來自同一群,所以我們對每堆奶牛分別進行考慮即可,這就是為什麽要按右端點排序的原因。貪心過程中,要維護最大值。因為要算最少的空位子,下面給出兩種代碼:
自己整理的題解
1、純貪心&貪心:
#include<cstdio>
#include<algorithm>
#include<cctype>
#define maxn 50007
using namespace std;
int ans,n,m,k,w[maxn];
inline int qread() {
char c=getchar();int num=0,f=1;
for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
for(;isdigit(c);c=getchar()) num=num*10+c-'0';
return num*f;
}
struct node {
int u,v,c;
}e[maxn];
const int inf=0x7fffffff;
inline bool cmp(node a, node b) {
if(a.v!=b.v) return a.v<b.v;
return a.u<b.u;
}
int main() {
k=qread(),n=qread(),m=qread();
for(int i=1;i<=k;++i) e[i].u=qread(),e[i].v=qread(),e[i].c=qread();
sort(e+1,e+1+k,cmp);
for(int i=1;i<=k;++i) {
if(w[e[i].u]>=m) continue;
int minn=inf;
for(int j=e[i].u;j<=e[i].v;++j) {
minn=min(m-w[j],minn);
if(minn<=0) break;
}
if(minn>0) {
if(minn>=e[i].c) {
for(int j=e[i].u;j<e[i].v;++j)
w[j]+=e[i].c;
ans+=e[i].c;
}
else {
for(int j=e[i].u;j<e[i].v;++j)
w[j]+=minn;
ans+=minn;
}
}
}
printf("%d\n",ans);
return 0;
}
2、線段樹&貪心&排序:
#include<cstdio>
#include<algorithm>
#include<cctype>
#define maxn 20007
#define ls rt<<1
#define rs rt<<1|1
using namespace std;
const int inf=0x7fffffff;
int n,k,m,maxx[maxn<<2],lazy[maxn<<2],zrj,w[50007];
inline int qread() {
char c=getchar();int num=0,f=1;
for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
for(;isdigit(c);c=getchar()) num=num*10+c-'0';
return num*f;
}
struct node {
int u,v,c;
}e[50007];
inline bool cmp(node a, node b) {
if(a.v!=b.v) return a.v<b.v;
return a.u<b.u;
}
inline void pushup(int rt) {
maxx[rt]=max(maxx[ls],maxx[rs]);
}
inline void pushdown(int rt) {
if(lazy[rt]) {
maxx[ls]+=lazy[rt],lazy[ls]+=lazy[rt];
maxx[rs]+=lazy[rt],lazy[rs]+=lazy[rt];
lazy[rt]=0;
}
}
void modify(int rt, int l, int r, int L, int R, int val) {
if(L>r||R<l) return;
if(L<=l&&r<=R) {
lazy[rt]+=val,maxx[rt]+=val;
return;
}
int mid=(l+r)>>1;
pushdown(rt);
modify(ls,l,mid,L,R,val),modify(rs,mid+1,r,L,R,val);
pushup(rt);
}
int cmax(int rt, int l, int r, int L, int R) {
if(L>r||R<l) return -inf;
if(L<=l&&r<=R) return maxx[rt];
int mid=(l+r)>>1,ans=-inf;
pushdown(rt);
if(L<=mid) ans=max(ans,cmax(ls,l,mid,L,R));
if(R>mid) ans=max(ans,cmax(rs,mid+1,r,L,R));
return ans;
}
int main() {
k=qread(),n=qread(),m=qread();
for(int i=1;i<=k;++i) e[i].u=qread(),e[i].v=qread(),e[i].c=qread();
sort(e+1,e+1+k,cmp);
for(int i=1;i<=k;++i) {
int p=cmax(1,1,n,e[i].u,e[i].v-1);
int minn=min(m-p,e[i].c);
zrj+=minn,w[i]+=minn;
modify(1,1,n,e[i].u,e[i].v-1,w[i]);
}
printf("%d\n",zrj);
return 0;
}
2. 洛谷 P3128 [USACO15DEC]最大流Max Flow
題目描述
\(FJ\)給他的牛棚的\(N(2≤N≤50,000)\)個隔間之間安裝了\(N-1\)根管道,隔間編號從\(1\)到\(N\)。所有隔間都被管道連通了。
\(FJ\)有\(K(1≤K≤100,000)\)條運輸牛奶的路線,第\(i\)條路線從隔間\(s_i\)運輸到隔間\(t_i\)。一條運輸路線會給它的兩個端點處的隔間以及中間途徑的所有隔間帶來一個單位的運輸壓力,你需要計算壓力最大的隔間的壓力是多少。
輸入輸出格式
輸入格式:
The first line of the input contains \(N\) and \(K\).
The next \(N-1\) lines each contain two integers \(x\) and \(y\) \((x \ne y\)) describing a pipe
between stalls \(x\) and \(y\).
The next \(K\) lines each contain two integers \(s\) and \(t\) describing the endpoint
stalls of a path through which milk is being pumped.
輸出格式:
An integer specifying the maximum amount of milk pumped through any stall in the
barn.
輸入輸出樣例
輸入樣例#1:
5 10
3 4
1 5
4 2
5 4
5 4
5 4
3 5
4 3
4 3
1 3
3 5
5 4
1 5
3 4
輸出樣例#1:
9
思路:樹鏈剖分+線段樹,題目中給出的每組點就相當於是路徑修改,也就是樹鏈剖分裏面的基本操作,要求輸出的最大壓力值的隔間就是一到n壓力值的最大值,因此我們只需要再用線段樹維護區間最大值就好了。
代碼:
#include<cstdio>
#include<algorithm>
#include<cctype>
#define maxn 50007
#define ls rt<<1
#define rs rt<<1|1
using namespace std;
int id[maxn],d[maxn],n,k,cnt,son[maxn],top[maxn],maxx[maxn<<2];
int head[maxn],num,fa[maxn],siz[maxn],lazy[maxn<<2];
inline int qread() {
char c=getchar();int num=0,f=1;
for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
for(;isdigit(c);c=getchar()) num=num*10+c-'0';
return num*f;
}
const int inf=0x7fffffff;
struct node {
int v,nxt;
}e[maxn<<1];
inline void ct(int u, int v) {
e[++num].v=v;
e[num].nxt=head[u];
head[u]=num;
}
void dfs1(int u) {
siz[u]=1;
for(int i=head[u];i;i=e[i].nxt) {
int v=e[i].v;
if(v!=fa[u]) {
d[v]=d[u]+1;
fa[v]=u;
dfs1(v);
siz[u]+=siz[v];
if(siz[v]>siz[son[u]]) son[u]=v;
}
}
}
void dfs2(int u, int t) {
id[u]=++cnt;
top[u]=t;
if(son[u]) dfs2(son[u],t);
for(int i=head[u];i;i=e[i].nxt) {
int v=e[i].v;
if(v!=fa[u]&&v!=son[u]) dfs2(v,v);
}
}
inline void pushup(int rt) {
maxx[rt]=max(maxx[ls],maxx[rs]);
}
inline void pushdown(int rt) {
if(lazy[rt]) {
lazy[ls]+=lazy[rt],maxx[ls]+=lazy[rt];
lazy[rs]+=lazy[rt],maxx[rs]+=lazy[rt];
lazy[rt]=0;
}
}
void modify(int rt, int l, int r, int L, int R, int val) {
if(L>r||R<l) return;
if(L<=l&&r<=R) {
lazy[rt]+=val,maxx[rt]+=val;
return;
}
int mid=(l+r)>>1;
pushdown(rt);
modify(ls,l,mid,L,R,val),modify(rs,mid+1,r,L,R,val);
pushup(rt);
}
int cmax(int rt, int l, int r, int L, int R) {
if(L>r||R<l) return -inf;
if(L<=l&&r<=R) return maxx[rt];
int mid=(l+r)>>1,ans=-inf;
pushdown(rt);
if(L<=mid) ans=max(ans,cmax(ls,l,mid,L,R));
if(R>mid) ans=max(ans,cmax(rs,mid+1,r,L,R));
return ans;
}
void cal(int x, int y, int val) {
int fx=top[x],fy=top[y];
while(fx!=fy) {
if(d[fx]<d[fy]) swap(x,y),swap(fx,fy);
modify(1,1,cnt,id[fx],id[x],val);
x=fa[fx],fx=top[x];
}
if(id[x]>id[y]) swap(x,y);
modify(1,1,cnt,id[x],id[y],val);
}
int main() {
n=qread(),k=qread();
for(int i=1,u,v;i<n;++i) {
u=qread(),v=qread();
ct(u,v),ct(v,u);
}
dfs1(1);dfs2(1,1);
for(int i=1,u,v;i<=k;++i) {
u=qread(),v=qread();
cal(u,v,1);
}
printf("%d\n",cmax(1,1,cnt,1,cnt));
return 0;
}
3. SP375 QTREE - Query on a tree
題意大意
給定\(n\)個點的樹,邊按輸入順序編號為\(1,2,...n-1\),要求作以下操作: CHANGE \(i\) \(t_i\) 將第\(i\)條邊權值改為\(t_i\),QUERY \(a\) \(b\) 詢問從\(a\)點到\(b\)點路徑上的最大邊權
有多組測試數據,每組數據以DONE結尾
題目描述
You are given a tree (an acyclic undirected connected graph) with N nodes, and edges numbered 1, 2, 3...N-1.
We will ask you to perfrom some instructions of the following form:
- CHANGE i ti : change the cost of the i-th edge to ti
or - QUERY a b : ask for the maximum edge cost on the path from node a to node b
輸入輸出格式
輸入格式:
The first line of input contains an integer t, the number of test cases (t <= 20). t test cases follow.
For each test case:
- In the first line there is an integer N (N <= 10000),
- In the next N-1 lines, the i-th line describes the i-th edge: a line with three integers a b c denotes an edge between a, b of cost c (c <= 1000000),
- The next lines contain instructions "CHANGE i ti" or "QUERY a b",
- The end of each test case is signified by the string "DONE".
There is one blank line between successive tests.
輸出格式:
For each "QUERY" operation, write one integer representing its result.
輸入輸出樣例
輸入樣例#1:
1
3
1 2 1
2 3 2
QUERY 1 2
CHANGE 1 3
QUERY 1 2
DONE
輸出樣例#1:
1
3
思路:
其實這個題也不算特別的難,如果你做過有關換邊權為點權的題目的話,比如……月下“毛景樹”,旅遊等。自我感覺難度都比這道題要大,並且都涉及到化邊權為點權然後樹鏈剖分。
不多說了,我們來看這個題,這道題與普通的樹鏈剖分題目不同的是,這道題目給出的是邊權,但是普通的樹鏈剖分題目都是給出的點權,那,該怎麽辦呢?不難發現,每個點有且只有一條邊連向它的父親結點,誒??有且只有一條?那!!我們就可以用這個點的點權去代替它與它父親之間邊的邊權呀!!那這不就又成了樹鏈剖分板子題了嘛?!\(QwQ\)。還有一個坑就是最後查詢的時候,因為LCA的點權不在查詢的路徑範圍內,因此要排除掉。
洛谷上此題的c++評測有些問題,下面給出c的代碼(memset必須要全寫,不然沒法AC,我也不知道為什麽):
自己整理的題解
#include <ctype.h>
#include <stdio.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#define maxn 10007
#define ls rt<<1
#define rs rt<<1|1
int id[maxn],d[maxn],n,cnt,son[maxn],top[maxn],maxx[maxn<<2],t;
int head[maxn],num,fa[maxn],siz[maxn],w[maxn],a[maxn];
char s[8];
struct node {
int v,w,nxt;
}e[maxn<<1];
int max(int a, int b) { return a > b ? a : b; }
#define swap(A, B) { int __T = A; A = B; B = __T; }
void ct(int u, int v, int w) {
e[++num].v=v;
e[num].w=w;
e[num].nxt=head[u];
head[u]=num;
}
void pushup(int rt) {
maxx[rt]=max(maxx[ls],maxx[rs]);
}
void build(int rt, int l, int r) {
if(l==r) {
maxx[rt]=a[l];
return;
}
int mid=(l+r)>>1;
build(ls,l,mid);
build(rs,mid+1,r);
pushup(rt);
}
void modify(int rt, int l, int r, int L, int val) {
if(l==r) {
maxx[rt]=val;
return;
}
int mid=(l+r)>>1;
if(L<=mid) modify(ls,l,mid,L,val);
else modify(rs,mid+1,r,L,val);
pushup(rt);
}
int cmax(int rt, int l, int r, int L, int R) {
if(L<=l&&r<=R) return maxx[rt];
int ans=0;
int mid=(l+r)>>1;
if(L<=mid) ans=max(ans,cmax(ls,l,mid,L,R));
if(R>mid) ans=max(ans,cmax(rs,mid+1,r,L,R));
return ans;
}
void dfs1(int u) {
siz[u]=1;
for(int i=head[u];i;i=e[i].nxt) {
int v=e[i].v;
if(v!=fa[u]) {
d[v]=d[u]+1;
fa[v]=u;
w[v]=e[i].w;
dfs1(v);
siz[u]+=siz[v];
if(siz[v]>siz[son[u]]) son[u]=v;
}
}
}
void dfs2(int u, int t) {
id[u]=++cnt;
top[u]=t;
a[cnt]=w[u];
if(son[u]) dfs2(son[u],t);
for(int i=head[u];i;i=e[i].nxt) {
int v=e[i].v;
if(v!=fa[u]&&v!=son[u]) dfs2(v,v);
}
}
int query(int x, int y) {
int ans=0,fx=top[x],fy=top[y];
while(fx!=fy) {
if(d[fx]<d[fy]) {
swap(x,y);
swap(fx,fy);
}
ans=max(ans,cmax(1,1,n,id[fx],id[x]));
x=fa[fx],fx=top[x];
}
if(id[x]>id[y]) swap(x,y);
ans=max(ans,cmax(1,1,n,id[x]+1,id[y]));
return ans;
}
int main() {
scanf("%d",&t);
while(t--) {
memset(head,0,sizeof(head));
memset(siz,0,sizeof(siz));
memset(id,0,sizeof(id));
memset(top,0,sizeof(top));
memset(son,0,sizeof(son));
memset(w,0,sizeof(w));
memset(maxx,0,sizeof(maxx));
memset(a,0,sizeof(a));
memset(fa,0,sizeof(fa));
memset(d,0,sizeof(d));num=0;cnt=0;
scanf("%d",&n);
for(int i=1,u,v,w;i<n;++i) {
scanf("%d%d%d",&u,&v,&w);
ct(u,v,w),ct(v,u,w);
}
dfs1(1);dfs2(1,1);build(1,1,n);
while(1) {
scanf("%s",s);
if(s[0]=='D') break;
if(s[0]=='C') {
int x,y;
scanf("%d%d",&x,&y);
x=d[e[x*2-1].v]<d[e[x<<1].v]?e[x<<1].v:e[x*2-1].v;
modify(1,1,cnt,id[x],y);
}
if(s[0]=='Q') {
int x,y;
scanf("%d%d",&x,&y);
printf("%d\n",query(x,y));
}
}
}
return 0;
}
接下來是在SPOJ上能夠AC的c++代碼:
#include<cstdio>
#include<algorithm>
#include<cstring>
#define maxn 10007
#define ls rt<<1
#define rs rt<<1|1
using namespace std;
int id[maxn],d[maxn],n,cnt,son[maxn],top[maxn],maxx[maxn<<2],t;
int head[maxn],num,fa[maxn],siz[maxn],w[maxn],a[maxn];
char s[8];
struct node {
int v,w,nxt;
}e[maxn<<1];
inline void ct(int u, int v, int w) {
e[++num].v=v;
e[num].w=w;
e[num].nxt=head[u];
head[u]=num;
}
inline void pushup(int rt) {
maxx[rt]=max(maxx[ls],maxx[rs]);
}
void build(int rt, int l, int r) {
if(l==r) {
maxx[rt]=a[l];
return;
}
int mid=(l+r)>>1;
build(ls,l,mid);
build(rs,mid+1,r);
pushup(rt);
}
void modify(int rt, int l, int r, int L, int val) {
if(l==r) {
maxx[rt]=val;
return;
}
int mid=(l+r)>>1;
if(L<=mid) modify(ls,l,mid,L,val);
else modify(rs,mid+1,r,L,val);
pushup(rt);
}
int cmax(int rt, int l, int r, int L, int R) {
if(L<=l&&r<=R) return maxx[rt];
int ans=0;
int mid=(l+r)>>1;
if(L<=mid) ans=max(ans,cmax(ls,l,mid,L,R));
if(R>mid) ans=max(ans,cmax(rs,mid+1,r,L,R));
return ans;
}
void dfs1(int u) {
siz[u]=1;
for(int i=head[u];i;i=e[i].nxt) {
int v=e[i].v;
if(v!=fa[u]) {
d[v]=d[u]+1;
fa[v]=u;
w[v]=e[i].w;
dfs1(v);
siz[u]+=siz[v];
if(siz[v]>siz[son[u]]) son[u]=v;
}
}
}
void dfs2(int u, int t) {
id[u]=++cnt;
top[u]=t;
a[cnt]=w[u];
if(son[u]) dfs2(son[u],t);
for(int i=head[u];i;i=e[i].nxt) {
int v=e[i].v;
if(v!=fa[u]&&v!=son[u]) dfs2(v,v);
}
}
int query(int x, int y) {
int ans=0,fx=top[x],fy=top[y];
while(fx!=fy) {
if(d[fx]<d[fy]) {
swap(x,y);
swap(fx,fy);
}
ans=max(ans,cmax(1,1,n,id[fx],id[x]));
x=fa[fx],fx=top[x];
}
if(id[x]>id[y]) swap(x,y);
ans=max(ans,cmax(1,1,n,id[x]+1,id[y]));
return ans;
}
int main() {
scanf("%d",&t);
while(t--) {
memset(head,0,sizeof(head));
memset(siz,0,sizeof(siz));
memset(id,0,sizeof(id));
memset(top,0,sizeof(top));
memset(son,0,sizeof(son));
memset(w,0,sizeof(w));
memset(maxx,0,sizeof(maxx));
memset(a,0,sizeof(a));
memset(fa,0,sizeof(fa));
memset(d,0,sizeof(d));num=0;cnt=0;
scanf("%d",&n);
for(int i=1,u,v,w;i<n;++i) {
scanf("%d%d%d",&u,&v,&w);
ct(u,v,w),ct(v,u,w);
}
dfs1(1);dfs2(1,1);build(1,1,n);
while(1) {
scanf("%s",s);
if(s[0]=='D') break;
if(s[0]=='C') {
int x,y;
scanf("%d%d",&x,&y);
x=d[e[x*2-1].v]<d[e[x<<1].v]?e[x<<1].v:e[x*2-1].v;
modify(1,1,cnt,id[x],y);
}
if(s[0]=='Q') {
int x,y;
scanf("%d%d",&x,&y);
printf("%d\n",query(x,y));
}
}
}
return 0;
}
4. CF438D The Child and Sequence
題意大意
給定數列,區間查詢和,區間取模,單點修改。
\(n\),\(m\)小於\(10^5\)
題目描述
At the children‘s day, the child came to Picks‘s house, and messed his house up. Picks was angry at him. A lot of important things were lost, in particular the favorite sequence of Picks.
Fortunately, Picks remembers how to repair the sequence. Initially he should create an integer array \(a[1],a[2],...,a[n]\). Then he should perform a sequence of mm operations. An operation can be one of the following:
- Print operation \(l,r\) . Picks should write down the value of .
- Modulo operation \(l,r,x\) . Picks should perform assignment \(a[i]=a[i] mod x\) for each \(i\) \((l<=i<=r)\).
- Set operation \(k,x\) . Picks should set the value of \(a[k]\) to \(x\) (in other words perform an assignment \(a[k]=x\)).
Can you help Picks to perform the whole sequence of operations?
輸入輸出格式
輸入格式:
The first line of input contains two integer: \(n,m\) \((1<=n,m<=10^{5})\) . The second line contains nnintegers, separated by space: \(a[1],a[2],...,a[n] (1<=a[i]<=10^{9})\) — initial value of array elements.
Each of the next mm lines begins with a number typetype .
- If \(type=1\) , there will be two integers more in the line: \(l,r (1<=l<=r<=n)\) , which correspond the operation \(1\).
- If \(type=2\) , there will be three integers more in the line: \(l,r,x (1<=l<=r<=n; 1<=x<=10^{9})\) , which correspond the operation \(2\).
- If \(type=3\), there will be two integers more in the line: \(k,x (1<=k<=n; 1<=x<=10^{9})\) , which correspond the operation \(3\).
輸出格式:
For each operation \(1\), please print a line containing the answer. Notice that the answer may exceed the \(32\)-bit integer.
輸入輸出樣例
輸入樣例#1:
5 5
1 2 3 4 5
2 3 5 4
3 3 5
1 2 5
2 1 3 3
1 1 3
輸出樣例#1:
8
5
輸入樣例#2:
10 10
6 9 6 7 6 1 10 10 9 5
1 3 9
2 7 10 9
2 5 10 8
1 4 7
3 3 7
2 7 9 9
1 2 4
1 6 6
1 5 9
3 1 10
輸出樣例#2:
49
15
23
1
9
說明
Consider the first testcase:
- At first, a={1,2,3,4,5}.
- After operation 1 , a={1,2,3,0,1}.
- After operation 2 , a={1,2,5,0,1}.
- At operation 3 , 2+5+0+1=8.
- After operation 4 , a={1,2,2,0,1}.
- At operation 5 , 1+2+2=5.
思路:
題目大意:給定數列,區間查詢和,區間取模,單點修改。
\(n,m\)小於\(10^5\)
查詢區間和和單點修改就不用說了吧,線段樹基本操作,那??對於這道題目的區間修改該怎麽處理呢??不難發現,一個數模兩次同樣的數時沒有任何意義的,而且一個數模一個比它大的數也是沒有意義的?!!!那麽,我們便可以采用一種暴力的思想,去修改每一個位置,每次判斷一下區間最大值是不是比模數大即可。因為是暴力修改,所以也無需\(pushdown\),那麽代碼應該就很好寫了吧,也不算太長。
自己整理的題解
下面是我的代碼,僅供參考,畢竟太醜了:
#include<cstdio>
#include<algorithm>
#include<cctype>
#define maxn 100007
#define ls rt<<1
#define rs rt<<1|1
#define ll long long
using namespace std;
int n,m,maxx[maxn<<2];
ll sum[maxn<<2];
inline int qread() {
char c=getchar();int num=0,f=1;
for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
for(;isdigit(c);c=getchar()) num=num*10+c-'0';
return num*f;
}
inline void pushup(int rt) {
sum[rt]=sum[ls]+sum[rs];
maxx[rt]=max(maxx[ls],maxx[rs]);
}
void build(int rt, int l, int r) {
if(l==r) {
maxx[rt]=sum[rt]=qread();
return;
}
int mid=(l+r)>>1;
build(ls,l,mid);
build(rs,mid+1,r);
pushup(rt);
}
void add(int rt, int l, int r, int L, int val) {
if(l==r) {
maxx[rt]=sum[rt]=val;
return;
}
int mid=(l+r)>>1;
if(L<=mid) add(ls,l,mid,L,val);
else add(rs,mid+1,r,L,val);
pushup(rt);
}
ll csum(int rt, int l, int r, int L, int R) {
if(L<=l&&r<=R) return sum[rt];
int mid=(l+r)>>1;
ll ans=0;
if(L<=mid) ans+=csum(ls,l,mid,L,R);
if(R>mid) ans+=csum(rs,mid+1,r,L,R);
return ans;
}
void modify(int rt, int l, int r, int L, int R, int p) {
if(maxx[rt]<p) return;
if(l==r) {
sum[rt]%=p;maxx[rt]%=p;
return;
}
int mid=(l+r)>>1;
if(L<=mid) modify(ls,l,mid,L,R,p);
if(R>mid) modify(rs,mid+1,r,L,R,p);
pushup(rt);
}
int main() {
n=qread(),m=qread();
build(1,1,n);
for(int i=1,k,x,y,z;i<=m;++i) {
k=qread(),x=qread(),y=qread();
if(k==1) printf("%lld\n",csum(1,1,n,x,y));
if(k==2) {
z=qread();
modify(1,1,n,x,y,z);
}
if(k==3) add(1,1,n,x,y);
}
return 0;
}
5. 洛谷P3398 倉鼠找sugar
題目描述
小倉鼠的和他的基\((mei)\)友\((zi)sugar\)住在地下洞穴中,每個節點的編號為\(1\)~\(n\)。地下洞穴是一個樹形結構。這一天小倉鼠打算從從他的臥室\((a)\)到餐廳\((b)\),而他的基友同時要從他的臥室\((c)\)到圖書館\((d)\)。他們都會走最短路徑。現在小倉鼠希望知道,有沒有可能在某個地方,可以碰到他的基友?
小倉鼠那麽弱,還要天天被\(zzq\)大爺虐,請你快來救救他吧!
輸入輸出格式
輸入格式:
第一行兩個正整數\(n\)和\(q\),表示這棵樹節點的個數和詢問的個數。
接下來\(n-1\)行,每行兩個正整數\(u\)和\(v\),表示節點\(u\)到節點\(v\)之間有一條邊。
接下來\(q\)行,每行四個正整數\(a\)、\(b\)、\(c\)和\(d\),表示節點編號,也就是一次詢問,其意義如上。
輸出格式:
對於每個詢問,如果有公共點,輸出大寫字母“Y”;否則輸出“N”。
輸入輸出樣例
輸入樣例#1:
5 5
2 5
4 2
1 3
1 4
5 1 5 1
2 2 1 4
4 1 3 4
3 1 1 5
3 5 1 4
輸出樣例#1:
Y
N
Y
Y
Y
說明
本題時限1s,內存限制128M,因新評測機速度較為接近NOIP評測機速度,請註意常數問題帶來的影響。
\(20\%\)的數據 \(n<=200,q<=200\)
\(40\%\)的數據 \(n<=2000,q<=2000\)
\(70\%\)的數據 \(n<=50000,q<=50000\)
\(100\%\)的數據 \(n<=100000,q<=100000\)
思路:
其實,多畫幾個圖模擬一下,可以發現如下一個神奇的規律:
如果兩條路徑相交,那麽一定有一條路徑的LCA在另一條路徑上
而判斷一個節點\(x\),是否在路徑\(a\)-\(b\)上需要滿足如下幾個條件
- d[x]>=d[LCA(a,b)]
- LCA(a,x)=x或LCA(b,x)=x;
其中d數組代表深度。
所以分兩種情況討論一下即可
時間復雜度\(O(n log n)\),下面是樹剖求\(LCA\)的代碼,倍增照樣可以做,就是慢了點。
代碼:
#include<cstdio>
#include<algorithm>
#include<cctype>
#define maxn 100007
using namespace std;
int num,head[maxn],n,d[maxn],son[maxn],top[maxn],siz[maxn];
int cnt,id[maxn],fa[maxn],m;
inline int qread() {
char c=getchar();int num=0,f=1;
for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
for(;isdigit(c);c=getchar()) num=num*10+c-'0';
return num*f;
}
struct node {
int v,nxt;
}e[maxn<<1];
inline void ct(int u, int v) {
e[++num].v=v;
e[num].nxt=head[u];
head[u]=num;
}
void dfs1(int u) {
siz[u]=1;
for(int i=head[u];i;i=e[i].nxt) {
int v=e[i].v;
if(v!=fa[u]) {
d[v]=d[u]+1;
fa[v]=u;
dfs1(v);
siz[u]+=siz[v];
if(siz[v]>siz[son[u]]) son[u]=v;
}
}
}
void dfs2(int u, int t) {
id[u]=++cnt;
top[u]=t;
if(son[u]) dfs2(son[u],t);
for(int i=head[u];i;i=e[i].nxt) {
int v=e[i].v;
if(v!=fa[u]&&v!=son[u]) dfs2(v,v);
}
}
inline int lca(int x, int y) {
int fx=top[x],fy=top[y];
while(fx!=fy) {
if(d[fx]<d[fy]) swap(x,y),swap(fx,fy);
x=fa[fx],fx=top[x];
}
return d[x]>d[y]?y:x;
}
int main() {
n=qread(),m=qread();
for(int i=1,u,v;i<n;++i) {
u=qread(),v=qread();
ct(u,v),ct(v,u);
}
dfs1(1);dfs2(1,1);
for(int i=1,a,b,c,p;i<=m;++i) {
a=qread(),b=qread(),c=qread(),p=qread();
int zrj=lca(a,b),cyh=lca(c,p);
if(d[zrj]<d[cyh]) swap(zrj,cyh),swap(a,c),swap(b,p);
if(lca(zrj,c)==zrj||lca(zrj,p)==zrj) printf("Y\n");
else printf("N\n");
}
return 0;
}
6. 洛谷P2146 [NOI2015]軟件包管理器
題目描述
\(Linux\)用戶和\(OSX\)用戶一定對軟件包管理器不會陌生。通過軟件包管理器,你可以通過一行命令安裝某一個軟件包,然後軟件包管理器會幫助你從軟件源下載軟件包,同時自動解決所有的依賴(即下載安裝這個軟件包的安裝所依賴的其它軟件包),完成所有的配置。\(Debian/Ubuntu\)使用的\(apt-get\),\(Fedora/CentOS\)使用的\(yum\),以及\(OSX\)下可用的\(homebrew\)都是優秀的軟件包管理器。
你決定設計你自己的軟件包管理器。不可避免地,你要解決軟件包之間的依賴問題。如果軟件包\(A\)依賴軟件包\(B\),那麽安裝軟件包\(A\)以前,必須先安裝軟件包\(B\)。同時,如果想要卸載軟件包\(B\),則必須卸載軟件包\(A\)。現在你已經獲得了所有的軟件包之間的依賴關系。而且,由於你之前的工作,除0號軟件包以外,在你的管理器當中的軟件包都會依賴一個且僅一個軟件包,而0號軟件包不依賴任何一個軟件包。依賴關系不存在環(若有\(m(m≥2)\)個軟件包\(A1,A2,A3,?,Am\),其中\(A1\)依賴\(A2\),\(A2\)依賴\(A3\),\(A3\)依賴\(A4\),……,\(A[m-1]\)依賴\(Am\),而\(Am\)依賴\(A1\),則稱這\(m\)個軟件包的依賴關系構成環),當然也不會有一個軟件包依賴自己。
現在你要為你的軟件包管理器寫一個依賴解決程序。根據反饋,用戶希望在安裝和卸載某個軟件包時,快速地知道這個操作實際上會改變多少個軟件包的安裝狀態(即安裝操作會安裝多少個未安裝的軟件包,或卸載操作會卸載多少個已安裝的軟件包),你的任務就是實現這個部分。註意,安裝一個已安裝的軟件包,或卸載一個未安裝的軟件包,都不會改變任何軟件包的安裝狀態,即在此情況下,改變安裝狀態的軟件包數為\(0\)。
輸入輸出格式
輸入格式:
從文件\(manager.in\)中讀入數據。
輸入文件的第\(1\)行包含\(1\)個整數\(n\),表示軟件包的總數。軟件包從\(0\)開始編號。
隨後一行包含\(n?1\)個整數,相鄰整數之間用單個空格隔開,分別表示\(1,2,3,?,n?2,n?1\)號軟件包依賴的軟件包的編號。
接下來一行包含\(1\)個整數\(q\),表示詢問的總數。之後\(q\)行,每行\(1\)個詢問。詢問分為兩種:
\(install\) \(x\):表示安裝軟件包\(x\)
\(uninstall\) \(x\):表示卸載軟件包\(x\)
你需要維護每個軟件包的安裝狀態,一開始所有的軟件包都處於未安裝狀態。
對於每個操作,你需要輸出這步操作會改變多少個軟件包的安裝狀態,隨後應用這個操作(即改變你維護的安裝狀態)。
輸出格式:
輸出到文件\(manager.out\)中。
輸出文件包括\(q\)行。
輸出文件的第\(i\)行輸出\(1\)個整數,為第i步操作中改變安裝狀態的軟件包數。
輸入輸出樣例
輸入樣例#1:
7
0 0 0 1 1 5
5
install 5
install 6
uninstall 1
install 4
uninstall 0
輸出樣例#1:
3
1
3
2
3
輸入樣例#2:
10
0 1 2 1 3 0 0 3 2
10
install 0
install 3
uninstall 2
install 7
install 5
install 9
uninstall 9
install 4
install 1
install 9
輸出樣例#2:
1
3
2
1
3
1
1
1
0
1
說明
【樣例說明 1】
一開始所有的軟件包都處於未安裝狀態。
安裝\(5\)號軟件包,需要安裝\(0\),\(1\),\(5\)三個軟件包。
之後安裝\(6\)號軟件包,只需要安裝\(6\)號軟件包。此時安裝了\(0\),\(1\),\(5\),\(6\)四個軟件包。
卸載\(1\)號軟件包需要卸載\(1\),\(5\),\(6\)三個軟件包。此時只有\(0\)號軟件包還處於安裝狀態。
之後安裝\(4\)號軟件包,需要安裝\(1\),\(4\)兩個軟件包。此時\(0\),\(1\),\(4\)處在安裝狀態。最後,卸載\(0\)號軟件包會卸載所有的軟件包。`
【數據範圍】
【時限1s,內存512M】
思路:根據題意可以知道,題目中每個點只有兩個狀態,安裝和未安裝,我們可以用\(0\)來表示安裝,\(1\)來表示未安裝,那麽對於\(install\)類型的詢問,就是從被詢問的點到根節點最短路徑的\(1\)的個數。對於\(uninstall\)類型的詢問,就是被詢問的點的子樹點的個數減去這個點子樹中點權為\(1\)的點的個數,那代碼就很好寫了。直接用樹剖+線段樹來維護即可。
代碼:
#include<cstdio>
#include<algorithm>
#include<cctype>
#define maxn 100007
#define ls rt<<1
#define rs rt<<1|1
using namespace std;
int n,m,id[maxn],cnt,num,son[maxn],fa[maxn],siz[maxn];
int d[maxn],lazy[maxn<<2],sum[maxn<<2],top[maxn],head[maxn];
char s[8];
inline int qread() {
char c=getchar();int num=0,f=1;
for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
for(;isdigit(c);c=getchar()) num=num*10+c-'0';
return num*f;
}
struct node {
int v,nxt;
}e[maxn<<1];
inline void ct(int u, int v) {
e[++num].v=v;
e[num].nxt=head[u];
head[u]=num;
}
void dfs1(int u) {
siz[u]=1;
for(int i=head[u];i;i=e[i].nxt) {
int v=e[i].v;
if(v!=fa[u]) {
d[v]=d[u]+1;
fa[v]=u;
dfs1(v);
siz[u]+=siz[v];
if(siz[v]>siz[son[u]]) son[u]=v;
}
}
}
void dfs2(int u, int t) {
id[u]=++cnt;
top[u]=t;
if(son[u]) dfs2(son[u],t);
for(int i=head[u];i;i=e[i].nxt) {
int v=e[i].v;
if(v!=fa[u]&&v!=son[u]) dfs2(v,v);
}
}
inline void pushup(int rt) {
sum[rt]=sum[ls]+sum[rs];
}
inline void pushdown(int rt, int len) {
if(lazy[rt]>=0) {
lazy[ls]=lazy[rs]=lazy[rt];
sum[ls]=(len-(len>>1))*lazy[rt];
sum[rs]=(len>>1)*lazy[rt];
lazy[rt]=-1;
}
}
void build(int rt, int l, int r) {
lazy[rt]=-1;
if(l==r) {
sum[rt]=1;
return;
}
int mid=(l+r)>>1;
build(ls,l,mid);
build(rs,mid+1,r);
pushup(rt);
}
void modify(int rt, int l, int r, int L, int R, int val) {
if(L>r||R<l) return;
if(L<=l&&r<=R) {
sum[rt]=val*(r-l+1);
lazy[rt]=val;
return;
}
pushdown(rt,r-l+1);
int mid=(l+r)>>1;
if(L<=mid) modify(ls,l,mid,L,R,val);
if(R>mid) modify(rs,mid+1,r,L,R,val);
pushup(rt);
}
int csum(int rt, int l, int r, int L, int R) {
if(L>r||R<l) return 0;
if(L<=l&&r<=R) return sum[rt];
int mid=(l+r)>>1,ans=0;
pushdown(rt,r-l+1);
if(L<=mid) ans+=csum(ls,l,mid,L,R);
if(R>mid) ans+=csum(rs,mid+1,r,L,R);
return ans;
}
int query(int x, int y) {
int fx=top[x],fy=top[y],ans=0;
while(fx!=fy) {
if(d[fx]<d[fy]) swap(x,y),swap(fx,fy);
ans+=csum(1,1,cnt,id[fx],id[x]);
x=fa[fx],fx=top[x];
}
if(id[x]>id[y]) swap(x,y);
ans+=csum(1,1,cnt,id[x],id[y]);
return ans;
}
void cal(int x, int y, int val) {
int fx=top[x],fy=top[y];
while(fx!=fy) {
if(d[fx]<d[fy]) swap(x,y),swap(fx,fy);
modify(1,1,cnt,id[fx],id[x],val);
x=fa[fx],fx=top[x];
}
if(id[x]>id[y]) swap(x,y);
modify(1,1,cnt,id[x],id[y],val);
}
int main() {
n=qread();
for(int i=2,v;i<=n;++i) {
v=qread()+1;
ct(v,i);ct(i,v);
}
dfs1(1);dfs2(1,1);
build(1,1,n);
m=qread();
for(int i=1,x;i<=m;++i) {
scanf("%s",s);x=qread()+1;
if(s[0]=='i') {
printf("%d\n",query(1,x));
cal(1,x,0);
}
else {
printf("%d\n",siz[x]-csum(1,1,n,id[x],id[x]+siz[x]-1));
modify(1,1,n,id[x],id[x]+siz[x]-1,1);
}
}
return 0;
}
2019年1月5日