Bronze
A Cow Gymnastics
題目:https://www.luogu.com.cn/problem/P5831
題解:用數組存一下出現位置,O(n^2)枚舉一下就好。
代碼:
#include<bits/stdc++.h>
using namespace std;
#define re register int
#define F(x,y,z) for(re x=y;x<=z;x++)
#define FOR(x,y,z) for(re x=y;x>=z;x--)
#define I inline void
#define IN inline int
typedef long long ll;
I read(int &res){
re g=1;register char ch=getchar();res=0;
while(!isdigit(ch)){
if(ch=='-')g=-1;
ch=getchar();
}
while(isdigit(ch)){
res=(res<<3)+(res<<1)+(ch^48);
ch=getchar();
}
res*=g;
}
int n,m,a[100][100],b[100][100],sn,ans;
int main(){
freopen("gymnastics.in","r",stdin);
freopen("gymnastics.out","w",stdout);
read(m);read(n);
F(i,1,m){
F(j,1,n){
read(a[i][j]);
b[i][a[i][j]]=j;
}
}
F(i,1,n-1){
F(j,i+1,n){
sn=1;
if(b[1][i]>b[1][j]){
F(k,2,m)
if(b[k][i]<b[k][j]){
sn=0;
break;
}
}
else{
F(k,2,m){
if(b[k][i]>b[k][j]){
sn=0;
break;
}
}
}
ans+=sn;
}
}
cout<<ans;
return 0;
}
B Where Am I?
題目:https://www.luogu.com.cn/problem/P5832
題解:枚舉一下K,哈希一下用map一存就行。復雜度:O(n^2)
代碼:
#include<bits/stdc++.h>
using namespace std;
#define re register int
#define F(x,y,z) for(re x=y;x<=z;x++)
#define FOR(x,y,z) for(re x=y;x>=z;x--)
#define I inline void
#define IN inline int
typedef long long ll;
I read(int &res){
re g=1;register char ch=getchar();res=0;
while(!isdigit(ch)){
if(ch=='-')g=-1;
ch=getchar();
}
while(isdigit(ch)){
res=(res<<3)+(res<<1)+(ch^48);
ch=getchar();
}
res*=g;
}
const int Mod=1e9+7,bas=131;
set<int>mp;
char c[110];
int n,m,a[110],j,s;
int main(){
freopen("whereami.in","r",stdin);
freopen("whereami.out","w",stdout);
read(n);
scanf("%s",c+1);
F(i,1,n){
a[i]=c[i]-'A'+1;
}
F(len,1,n){
mp.clear();m=1;
F(i,1,n-len+1){
j=i+len-1;s=0;
F(k,i,j){
s=(ll)s*bas%Mod;
s+=a[k];
}
if(mp.count(s)){
m=0;
break;
}
mp.insert(s);
}
if(m){
cout<<len;
return 0;
}
}
return 0;
}
C Livestock Lineup
題目:https://www.luogu.com.cn/problem/P5833
題解:按字典序爆搜即可。當然,我寫的比較麻煩,
建了圖,還要用每個點的度數分類討論,其實復雜度
還是一樣,O(8!*8)
代碼:
#include<bits/stdc++.h>
using namespace std;
#define re register int
#define F(x,y,z) for(re x=y;x<=z;x++)
#define FOR(x,y,z) for(re x=y;x>=z;x--)
#define I inline void
#define IN inline int
typedef long long ll;
I read(int &res){
re g=1;register char ch=getchar();res=0;
while(!isdigit(ch)){
if(ch=='-')g=-1;
ch=getchar();
}
while(isdigit(ch)){
res=(res<<3)+(res<<1)+(ch^48);
ch=getchar();
}
res*=g;
}
string s[9]={"0","Beatrice","Belinda","Bella","Bessie","Betsy","Blue","Buttercup","Sue"};
char c[100];
int n,m,a[10][10],x,y,du[10],ans[10],v[10];
I D_1(int x){
/* cout<<x<<" ";
F(i,1,x){
cout<<ans[i]<<" ";
}
cout<<endl;*/
if(x==9){
if(du[ans[x-1]]==2)return;
F(i,1,8){
cout<<s[ans[i]]<<endl;
}
exit(0);
}
re sn=0;
if(du[ans[x-1]]>=1){
F(i,1,8){
if(a[ans[x-1]][i]&&!v[i]){
sn++;
}
}
if(sn==2)return;
sn=1;
F(i,1,8){
if(a[ans[x-1]][i]&&!v[i]){
ans[x]=i,v[i]=1,D_1(x+1),v[i]=0;sn=0;
break;
}
}
if(!sn)return;
}
F(i,1,8){
if(v[i])continue;
ans[x]=i,v[i]=1,D_1(x+1);
ans[x]=0;v[i]=0;
}
}
int main(){
freopen("lineup.in","r",stdin);
freopen("lineup.out","w",stdout);
read(n);
memset(a,0,sizeof(a));
memset(du,0,sizeof(du));
F(i,1,n){
cin>>c+1;
m=strlen(c+1);
if(m==3)x=8;
if(m==4)x=6;
if(m==6)x=4;
if(m==7)x=2;
if(m==8)x=1;
if(m==9)x=7;
if(m==5){
if(c[3]=='l')x=3;
else x=5;
}
cin>>c+1>>c+1>>c+1>>c+1>>c+1;
m=strlen(c+1);
if(m==3)y=8;
if(m==4)y=6;
if(m==6)y=4;
if(m==7)y=2;
if(m==8)y=1;
if(m==9)y=7;
if(m==5){
if(c[3]=='l')y=3;
else y=5;
}
du[x]++;du[y]++;
a[x][y]=1;a[y][x]=1;
//cout<<x<<" "<<y<<endl;
}
F(i,1,n){
if(du[i]<2){
ans[1]=i;v[i]=1;
D_1(2);v[i]=0;
}
}
//cout<<"!";
return 0;
}
Silver
A MooBuzz
題目:https://www.luogu.com.cn/problem/P5834
題解:可以明顯看出,每15次,有8個數被報出來。
隨便寫寫就過了
復雜度O(1)
代碼:
#include<bits/stdc++.h>
using namespace std;
#define re register int
#define F(x,y,z) for(re x=y;x<=z;x++)
#define FOR(x,y,z) for(re x=y;x>=z;x--)
#define I inline void
#define IN inline int
typedef long long ll;
I read(int &res){
re g=1;register char ch=getchar();res=0;
while(!isdigit(ch)){
if(ch=='-')g=-1;
ch=getchar();
}
while(isdigit(ch)){
res=(res<<3)+(res<<1)+(ch^48);
ch=getchar();
}
res*=g;
}
int n,m,ans;
int main(){
freopen("moobuzz.in","r",stdin);
freopen("moobuzz.out","w",stdout);
read(n);
if(n%8==0){
ans=(n/8)*15;
ans--;
cout<<ans;
return 0;
}
ans=(n/8)*15;
n%=8;m=0;
F(i,1,n){
while(m%3==0||m%5==0)m++;
if(i==n){
ans+=m;
break;
}
m++;
}
cout<<ans;
return 0;
}
B Meetings
題目:https://www.luogu.com.cn/problem/P5835
題解:相信這道題的兩個套路大家應該都見過:
1.不管這些牛怎么走,他們的相對位置都是不變的(沒見過的一定要細細斟酌)
2.兩頭牛相遇后,可以看做是“擦肩而過”,也就是並沒有改變方向。所以,
我們可以直接算出每頭牛走到終點的時間。
知道了這兩個性質,做法就順水推舟了:
1.用類似歸並排序的方法算出時刻T,由性質1,我們對時間和重量做出貢獻的並不是一頭牛(一定要小心);
2.因為時間已經確定,所以可以固定向一個方向走的牛不動,相當於另一個方向走的牛走了2*T個單位距離。
對兩種方向的牛分別排序,雙指針掃一下即可。具體實現看代碼。
復雜度:O(nlogn)
代碼:
#include<bits/stdc++.h>
using namespace std;
#define re register ll
#define F(x,y,z) for(re x=y;x<=z;x++)
#define FOR(x,y,z) for(re x=y;x>=z;x--)
#define I inline void
#define IN inline ll
typedef long long ll;
I read(ll &res){
re g=1;register char ch=getchar();res=0;
while(!isdigit(ch)){
if(ch=='-')g=-1;
ch=getchar();
}
while(isdigit(ch)){
res=(res<<3)+(res<<1)+(ch^48);
ch=getchar();
}
res*=g;
}
struct P{
ll x,w;
}l[50500],r[50500],p[101000];
ll n,m,num,tot,sum,W,X,d,Y,cnt,T,ans,L,R;
inline bool bb1(P a,P b){
return a.x==b.x?a.w>b.w:a.x>b.x;
}
inline bool bb2(P a,P b){
return a.x==b.x?a.w<b.w:a.x<b.x;
}
IN max(ll x,ll y){
return x>y?x:y;
}
int main(){
freopen("meetings.in","r",stdin);
freopen("meetings.out","w",stdout);
read(n);read(m);
tot=sum=num=0;
F(i,1,n){
read(W);read(X);read(d);num+=W;
p[i].x=X;p[i].w=W;
if(d==1)tot++,l[tot].x=X,l[tot].w=W;
else sum++,r[sum].x=X,r[sum].w=W;
}
sort(l+1,l+1+tot,bb1);
sort(r+1,r+1+sum,bb2);
sort(p+1,p+1+n,bb2);
X=1;Y=1;cnt=0;T=0;L=1;R=n;
while(cnt*2<num){
if(m-l[X].x>r[Y].x)cnt+=p[L++].w,T=r[Y].x,Y++;
else cnt+=p[R--].w,T=m-l[X].x,X++;
}
//cout<<X<<" "<<Y<<" "<<T<<endl;
sort(l+1,l+1+tot,bb2);
X=1;Y=1;
F(i,1,sum){
while(X<=tot&&l[X].x<max(0ll,r[i].x-T-T))X++;
while(Y<=tot&&l[Y].x<=r[i].x)Y++;
//cout<<X<<" "<<Y<<endl;
ans+=(Y-X);
}
cout<<ans;
return 0;
}
C Milk Visits
題目:https://www.luogu.com.cn/problem/P5836
題解:樹剖+線段樹板子題。詳細講解請見下一道Milk Visits。
復雜度:O(nlog^2n)
代碼:
#include<bits/stdc++.h>
using namespace std;
#define re register int
#define F(x,y,z) for(re x=y;x<=z;x++)
#define FOR(x,y,z) for(re x=y;x>=z;x--)
#define I inline void
#define IN inline int
typedef long long ll;
I read(int &res){
re g=1;register char ch=getchar();res=0;
while(!isdigit(ch)){
if(ch=='-')g=-1;
ch=getchar();
}
while(isdigit(ch)){
res=(res<<3)+(res<<1)+(ch^48);
ch=getchar();
}
res*=g;
}
char c[101000];
struct E{
int to,nt;
}e[202000];
#define T e[k].to
char sit;
int n,m,head[101000],fa[101000],X,Y,tot=-1,tr[101000],t[101000],siz[101000],top[101000],son[101000],id[101000],dep[101000];
I D_1(int x,int fat,int depth){
siz[x]=1;dep[x]=depth;son[x]=-1;fa[x]=fat;
re maxi=-1;
for(re k=head[x];k!=-1;k=e[k].nt){
if(T==fat)continue;
D_1(T,x,depth+1);
siz[x]+=siz[T];
if(maxi<siz[T])maxi=siz[T],son[x]=T;
}
}
IN lbt(int x){
return x&(-x);
}
I modi(int x){
while(x<=n)tr[x]++,x+=lbt(x);
}
I modify(int x){
while(x<=n)t[x]++,x+=lbt(x);
}
I D_2(int x,int fat,int topi){
top[x]=topi;id[x]=++tot;
if(c[x]=='H')modi(id[x]);else modify(id[x]);
if(son[x]!=-1)D_2(son[x],x,topi);
for(re k=head[x];k!=-1;k=e[k].nt){
if(T==fat||T==son[x])continue;
D_2(T,x,T);
}
}
IN ques_sum(int x){
re res=0;
while(x)res+=tr[x],x-=lbt(x);
return res;
}
IN query(int x){
re res=0;
while(x)res+=t[x],x-=lbt(x);
return res;
}
IN ques(int x,int y,char S){
re res=0;
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]])swap(x,y);
if(S=='H')res=(ques_sum(id[x])-ques_sum(id[top[x]]-1));
else res=(query(id[x])-query(id[top[x]]-1));
if(res)return 1;
x=fa[top[x]];
}
if(dep[x]>dep[y])swap(x,y);
if(S=='H')res=ques_sum(id[y])-ques_sum(id[x]-1);
else res=query(id[y])-query(id[x]-1);
if(res)return 1;
return 0;
}
int main(){
freopen("milkvisits.in","r",stdin);
freopen("milkvisits.out","w",stdout);
read(n);read(m);
cin>>c+1;
memset(head,-1,sizeof(head));
tot=-1;
F(i,1,n-1){
read(X);read(Y);
e[++tot].to=Y;
e[tot].nt=head[X];
head[X]=tot;
e[++tot].to=X;
e[tot].nt=head[Y];
head[Y]=tot;
}
D_1(1,0,1);
tot=0;
D_2(1,0,1);
while(m--){
read(X);read(Y);cin>>sit;
cout<<ques(X,Y,sit);
}
return 0;
}
Gold
A Milk Pumping
題目:https://www.luogu.com.cn/problem/P5837
題解:從1到1000枚舉這個最小流量,跑最短路就好。
復雜度:O(1000*(n+m)logn)
代碼:
#include<bits/stdc++.h>
using namespace std;
#define re register int
#define F(x,y,z) for(re x=y;x<=z;x++)
#define FOR(x,y,z) for(re x=y;x>=z;x--)
#define I inline void
#define IN inline int
typedef long long ll;
I read(int &res){
re g=1;register char ch=getchar();res=0;
while(!isdigit(ch)){
if(ch=='-')g=-1;
ch=getchar();
}
while(isdigit(ch)){
res=(res<<3)+(res<<1)+(ch^48);
ch=getchar();
}
res*=g;
}
struct E{
int to,nt,w,v;
}e[2020];
#define T e[k].to
const int INF=1e9+7;
int n,m,head[1010],X,Y,W,V,dis[1010],vis[1010],p,ans,tot;
queue<int>q;
int main(){
freopen("pump.in","r",stdin);
freopen("pump.out","w",stdout);
read(n);read(m);
memset(head,-1,sizeof(head));tot=-1;
F(i,1,m){
read(X);read(Y);read(W);read(V);
e[++tot].to=Y;
e[tot].nt=head[X];
head[X]=tot;
e[tot].w=W;
e[tot].v=V;
e[++tot].to=X;
e[tot].nt=head[Y];
head[Y]=tot;
e[tot].w=W;
e[tot].v=V;
}
ans=0;
F(val,1,1000){
F(i,1,n)dis[i]=INF,vis[i]=0;
dis[1]=0,vis[1]=1;
q.push(1);
while(!q.empty()){
p=q.front();q.pop();vis[p]=0;
for(re k=head[p];k!=-1;k=e[k].nt){
if(e[k].v<val)continue;
if(dis[p]+e[k].w<dis[T]){
dis[T]=dis[p]+e[k].w;
if(!vis[T]){
vis[T]=1;
q.push(T);
}
}
}
}
if(dis[n]!=INF)ans=max(ans,val*1000000/dis[n]);
}
cout<<ans;
return 0;
}
B Milk Visits
題目:https://www.luogu.com.cn/problem/P5838
題解:在上一道Milk Visits中,我們用兩個線段樹維護了樹上的信息,
這里,我們顯然不能直接開1e5棵線段樹,所以考慮避免這個問題。
Approach 1:可持久化線段樹
這個嘛。。。不會的打一下模板題就會了,這里就不講了。
Approach 2:離線
遇到不強制在線,且無修改的題目,離線算法是一定要take into consideration的。
我們將每個點的點權當做修改,和查詢中每個人的喜好牛奶一起排序,按點權排序。
這樣,我們就只需要在一棵線段樹上進行修改、查詢操作了。每次先打個lazytag清空
之前的信息,然后將一個點權的所有點插入線段樹,然后查詢。
具體實現還是看代碼。
復雜度:O(nlog^2n)
代碼:
#include<bits/stdc++.h>
using namespace std;
#define re register int
#define F(x,y,z) for(re x=y;x<=z;x++)
#define FOR(x,y,z) for(re x=y;x>=z;x--)
#define I inline void
#define IN inline int
typedef long long ll;
I read(int &res){
re g=1;register char ch=getchar();res=0;
while(!isdigit(ch)){
if(ch=='-')g=-1;
ch=getchar();
}
while(isdigit(ch)){
res=(res<<3)+(res<<1)+(ch^48);
ch=getchar();
}
res*=g;
}
char c[101000];
struct E{
int to,nt;
}e[202000];
struct Q{
int x,y,p,id;
friend bool operator < (Q a,Q b){
return a.p==b.p?a.id<b.id:a.p<b.p;
}
}q[202000];
#define T e[k].to
char sit;
int n,m,now,ans[101000],a[100100],tr[404000],laz[404000],head[101000],fa[101000],X,Y,tot=-1,siz[101000],top[101000],son[101000],id[101000],dep[101000];
I D_1(int x,int fat,int depth){
siz[x]=1;dep[x]=depth;son[x]=-1;fa[x]=fat;
re maxi=-1;
for(re k=head[x];k!=-1;k=e[k].nt){
if(T==fat)continue;
D_1(T,x,depth+1);
siz[x]+=siz[T];
if(maxi<siz[T])maxi=siz[T],son[x]=T;
}
}
I D_2(int x,int fat,int topi){
top[x]=topi;id[x]=++tot;
q[tot].x=x;q[tot].y=id[x];q[tot].p=a[x];q[tot].id=-1;
//將每個點看做修改,id為負是為了使其排序時排在查詢前面
if(son[x]!=-1)D_2(son[x],x,topi);
for(re k=head[x];k!=-1;k=e[k].nt){
if(T==fat||T==son[x])continue;
D_2(T,x,T);
}
}
#define all 1,1,n
#define lt k<<1,l,mid
#define rt k<<1|1,mid+1,r
I modi(int k,int l,int r,int x){
if(l==r){
tr[k]=1;
return;
}
if(laz[k])tr[k<<1]=tr[k<<1|1]=laz[k]=0,laz[k<<1]=laz[k<<1|1]=1;
re mid=(l+r)>>1;
if(x<=mid)modi(lt,x);
else modi(rt,x);
tr[k]=tr[k<<1]|tr[k<<1|1];
}
IN ques_sum(int k,int l,int r,int x,int y){
if(x>r||y<l)return 0;
if(x<=l&&r<=y)return tr[k];
if(laz[k])tr[k<<1]=tr[k<<1|1]=laz[k]=0,laz[k<<1]=laz[k<<1|1]=1;
re mid=(l+r)>>1;
return ques_sum(lt,x,y)|ques_sum(rt,x,y);
}
IN ques(int x,int y){
re res=0;
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]])swap(x,y);
res|=ques_sum(all,id[top[x]],id[x]);
if(res)return 1;
x=fa[top[x]];
}
if(dep[x]>dep[y])swap(x,y);
res|=ques_sum(all,id[x],id[y]);
if(res)return 1;
return 0;
}
int main(){
freopen("milkvisits.in","r",stdin);
freopen("milkvisits.out","w",stdout);
read(n);read(m);
F(i,1,n){
read(a[i]);
}
memset(head,-1,sizeof(head));
tot=-1;
F(i,1,n-1){
read(X);read(Y);
e[++tot].to=Y;
e[tot].nt=head[X];
head[X]=tot;
e[++tot].to=X;
e[tot].nt=head[Y];
head[Y]=tot;
}
D_1(1,0,1);
tot=0;
D_2(1,0,1);
F(i,1,m){
tot++;
read(q[tot].x);read(q[tot].y);read(q[tot].p);q[tot].id=i;
}
sort(q+1,q+1+tot);
now=q[1].p;
F(i,1,tot){
if(q[i].p>now)laz[1]=1,tr[1]=0,now=q[i].p;//點權變化時,清空線段樹,打懶標記是一個不錯的處理方式
if(q[i].id<0)modi(all,q[i].y);
else ans[q[i].id]=ques(q[i].x,q[i].y);
}
F(i,1,m){
cout<<ans[i];
}
return 0;
}
C Moortal Cowmbat
題目:https://www.luogu.com.cn/problem/P5839
題解:首先觀察到m很小,先O(m^3)floyd判一下兩兩之間的最小代價。
設f[i][j]表示處理了前i個字符,最后一堆字符為j的最小代價。
可以口胡一下轉移方程
暴力轉移是O(n^2m^2)的,我們無法接受。
考慮優化這個dp。
O(n^2m)
考慮到當前的轉移並不需要知道上一次的最后一段的字母是什么,我們
可以先對i的所有的f[i][j]取個min,再進行轉移。
O(nm)
發現對於當前的i,可以轉移到它的p是從1開始的一段連續區間。
我們設f[i]表示處理完前i個字母的最小代價,sum[i][j]表示從1到i,
將所有字母都變為j的代價。
這樣,轉移方程就可以寫為:
f[i]=min{f[p]+sum[i][j]-sum[p][j},p \(\in\) [1,i-k]
枚舉特定的j時,可以將sum[i][j]從這個max里提出來。
發現只需要取f[p]-sum[p][j]的min。
我們設mx[j]表示對於p \(\in\) [1,i-k],f[p]-sum[p][j]的最小值。
這樣,轉移就變成了O(1)。具體實現還有一些小細節,看代碼吧~
復雜度:O(m^3+nm)
代碼:
#include<bits/stdc++.h>
using namespace std;
#define re register int
#define F(x,y,z) for(re x=y;x<=z;x++)
#define FOR(x,y,z) for(re x=y;x>=z;x--)
#define I inline void
#define IN inline int
typedef long long ll;
I read(int &res){
re g=1;register char ch=getchar();res=0;
while(!isdigit(ch)){
if(ch=='-')g=-1;
ch=getchar();
}
while(isdigit(ch)){
res=(res<<3)+(res<<1)+(ch^48);
ch=getchar();
}
res*=g;
}
const int INF=1e9+7;
char c[101000];
int n,m,k,a[101000],dis[30][30],mx[30],sum[101000][30],f[101000];
int main(){
freopen("cowmbat.in","r",stdin);
freopen("cowmbat.out","w",stdout);
read(n);read(m);read(k);
cin>>c+1;
F(i,1,n){
a[i]=c[i]-'a'+1;
}
F(i,1,m){
F(j,1,m){
read(dis[i][j]);
}
}
F(k,1,m){
F(i,1,m){
F(j,1,m){
dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);//Floyd
}
}
}
memset(mx,0,sizeof(mx));
memset(sum,0,sizeof(sum));
F(i,1,k-1){
F(j,1,m){
sum[i][j]=sum[i-1][j]+dis[a[i]][j];//預處理sum
}
}
F(i,k,n){
f[i]=INF;
if(i>=(k<<1)){
F(j,1,m){
mx[j]=min(mx[j],f[i-k]-sum[i-k][j]);//i<2k時不存在合法的f
}
}
F(j,1,m){
sum[i][j]=sum[i-1][j]+dis[a[i]][j];
f[i]=min(f[i],mx[j]+sum[i][j]);
}
}
cout<<f[n];
return 0;
}
Platinum
A Greedy Pie Eaters
題目:http://www.usaco.org/index.php?page=viewproblem2&cpid=972
題目大意:有n個派,m頭牛,每頭牛有一個區間[l,r]和一個權值w。
沒有兩頭牛有相同的[l,r]。
你需要選擇一些牛,按一定順序吃派。每頭牛會吃完他的那個區間的所有派。
問在滿足每頭牛都至少有一個派吃的情況下,能獲得的最大權值是多少。
n<=300,m<=45000
題解:n很小,可以想到區間DP。
設f[i][j]表示吃掉的派是[i,j]的子集的最大權值。
顯然,f[i][j]=max{f[i][k-1]+f[k][j]},k\(\in\)[i,j]
但這樣的轉移不能加入新的牛。
可以考慮待合並的兩個狀態f[i][k-1]和f[k+1][j],此時k這個派還沒有牛動。
我們設mx[k][i][j]表示滿足i<=l[k]<=k<=r[k]<=j的所有牛的最大權值。
那么加入新牛的轉移方程就出爐了:
f[i][j]=max{f[i][k-1]+mx[k][i][j]+f[k+1][j]},k\(\in\)(i,j)
復雜度O(n^3)
代碼:
#include<bits/stdc++.h>
using namespace std;
#define re register int
#define F(x,y,z) for(re x=y;x<=z;x++)
#define FOR(x,y,z) for(re x=y;x>=z;x--)
typedef long long ll;
#define I inline void
#define IN inline int
template<class D>I read(D &res){
res=0;register D g=1;register char ch=getchar();
while(!isdigit(ch)){
if(ch=='-')g=-1;
ch=getchar();
}
while(isdigit(ch)){
res=(res<<3)+(res<<1)+(ch^48);
ch=getchar();
}
res*=g;
}
int n,m,mx[330][330][330],f[330][330],W,X,Y;
int main(){
freopen("pieaters.in","r",stdin);
freopen("pieaters.out","w",stdout);
read(n);read(m);
memset(mx,0,sizeof(mx));
F(i,1,m){
read(W);read(X);read(Y);
F(j,X,Y)mx[j][X][Y]=max(W,mx[j][X][Y]);
}
F(i,1,n){
FOR(j,i,1){
F(k,i,n){
if(j>1)mx[i][j-1][k]=max(mx[i][j-1][k],mx[i][j][k]);
if(k<n)mx[i][j][k+1]=max(mx[i][j][k+1],mx[i][j][k]);
}
}
}
FOR(i,n,1){
F(j,i,n){
F(k,i,j-1)f[i][j]=max(f[i][j],f[i][k]+f[k+1][j]);
F(k,i,j)f[i][j]=max(f[i][j],mx[k][i][j]+(k>i?f[i][k-1]:0)+(k<j?f[k+1][j]:0));
}
}
printf("%d",f[1][n]);
return 0;
}
B Bessie's Snow Cow
題目:http://www.usaco.org/index.php?page=viewproblem2&cpid=973
題目大意:給你一棵樹,要求支持兩種操作:
1.給定x,c,給x的子樹染上顏色c(一個節點可以有多個顏色)
2.給定x,求x的子樹的所有節點的所有顏色個數和(每個節點單獨算)
題解:這個題有兩個難點:
1.修改時,如何更新其對子樹內節點的貢獻,以及如何上傳對祖先的影響;
2.查詢時,如何一次性統計子樹內所有節點的貢獻
我們發現,如果將一個操作的復雜度變為O(logn)甚至O(1),另一個操作的
復雜度將不可避免地到達O(n)。
考慮統計答案。答案可以分為兩個部分:一是x或x的祖先已經染過的顏色,
這些的貢獻可以一並計算;二是子樹內染色的情況。
對於第一個,我們在每次修改時區間加就行了,當然還要撤銷之前子樹內
的一些修改操作的貢獻。
我們以這棵樹的dfs序為下標開一個樹狀數組,用差分維護每個節點已經染
上的顏色個數,再開1e5個set記錄下每個顏色的染色情況,避免重復染色。
每次修改操作步驟如下:
1.判斷x的祖先是否已經被染上c色。若已經染色,直接退出;
2.查詢x子樹內是否有節點染上c色。若已經染色,撤銷其影響並在set中
刪除此節點;
3.將此次修改加入set,進行區間修改。
那對於第二個,又該怎么求呢?
因為這些貢獻是一段dfs序連續區間的單點貢獻,我們可以另開一個樹狀數組
記錄這個貢獻。每次修改只需要單點+siz[x]即可。
修改操作的步驟同上。每次只需要查詢[id[x]+1,id[x]+siz[x]-1]這段區間的
貢獻即可(也就是x子樹除去x節點的貢獻)
設兩個查詢得到的值是ans1和ans2,那么答案即為:siz[x]*ans1+ans2
復雜度:因為每次修改只會被加入和撤銷一次,每次都是O(logn),
查詢也是O(logn)的,所以總復雜度即為O(nlogn)
代碼:
#include<bits/stdc++.h>
using namespace std;
#define re register ll
#define F(x,y,z) for(re x=y;x<=z;x++)
#define FOR(x,y,z) for(re x=y;x>=z;x--)
typedef long long ll;
#define I inline void
#define IN inline ll
template<class D>I read(D &res){
res=0;register D g=1;register char ch=getchar();
while(!isdigit(ch)){
if(ch=='-')g=-1;
ch=getchar();
}
while(isdigit(ch)){
res=(res<<3)+(res<<1)+(ch^48);
ch=getchar();
}
res*=g;
}
const ll INF=1e9+7;
struct E{
ll to,nt;
}e[202000];
#define T e[k].to
ll n,m,tot,sit,X,Y,head[101000],siz[101000],id[101000],p[101000],out[101000];
struct BIT{
ll tr[101000],res;
IN lbt(ll x){
return x&(-x);
}
I modi(ll x,ll w){while(x<=n){tr[x]+=w,x+=lbt(x);}}
IN ques(ll x){res=0;while(x){res+=tr[x],x-=lbt(x);}return res;}
}A,B;
set<ll>s[101000];
I modify(ll x,ll sn){
A.modi(id[x],sn);
A.modi(out[x]+1,-1*sn);
B.modi(id[x],sn*siz[x]);
//cout<<id[x]<<" "<<out[x]<<" "<<sn<<endl;
}
I D_1(ll x,ll fa){
id[x]=++tot;siz[x]=1;p[tot]=x;
for(re k=head[x];k!=-1;k=e[k].nt){
if(T==fa)continue;
D_1(T,x);siz[x]+=siz[T];
}
out[x]=tot;
}
int main(){
freopen("snowcow.in","r",stdin);
freopen("snowcow.out","w",stdout);
read(n);read(m);
memset(head,-1,sizeof(head));
tot=-1;
F(i,1,n-1){
read(X);read(Y);
e[++tot].to=Y;
e[tot].nt=head[X];
head[X]=tot;
e[++tot].to=X;
e[tot].nt=head[Y];
head[Y]=tot;
}
F(i,0,100000)s[i].insert(INF);
tot=0;
D_1(1,0);
//F(i,1,n)cout<<id[i]<<" "<<siz[i]<<" "<<out[i]<<endl;
while(m--){
read(sit);read(X);
if(sit==1){
read(Y);
auto P=s[Y].upper_bound(id[X]);
if(P!=s[Y].begin()&&out[p[*prev(P)]]>=out[X])continue;
while(1){
P=s[Y].upper_bound(id[X]);
if(*P>out[X])break;
modify(p[*P],-1);
s[Y].erase(P);
}
s[Y].insert(id[X]);
modify(X,1);
}
else printf("%lld\n",siz[X]*A.ques(id[X])+B.ques(out[X])-B.ques(id[X]));//printf("%d %d %d %d %d %lld\n",id[X],out[X],A.ques(id[X]),B.ques(out[X]),B.ques(id[X]),(ll)siz[X]*A.ques(id[X])+(ll)B.ques(out[X])-(ll)B.ques(id[X]));
}
return 0;
}
C Tree Depth
待填坑~