2019寒假練題計劃——LibreOJ刷題計劃 &《信息學奧賽一本通》提升版題目

目錄node

2019.1.27

#10082. 「一本通 3.3 例 1」Word Rings

題意

每組數據讀入一個n和n個字符串。定義前2個與末尾2個字母相同能夠鏈接。問使這個環串的平均長度最大。求這個最大值。不存在輸出\(No solution\)ios

思路

平均值公式:
\[ Average=(E_1+E_2+.....+E_n)/n \]c++

\[ Average*n=(E_1+E_2+...+E_n) \]數組

\[ (E_1-Average)+(E_2-Average)+...+(E_n-Average)=0 \]佈局

那麼能夠二分答案:
\[ (E_1-Ans)+(E_2-Ans)+...+(E_n-Ans)\geq0 \]
而後瞎搞spfa。ui

#include<algorithm>
#include<bitset>
#include<complex>
#include<deque>
#include<exception>
#include<fstream>
#include<functional>
#include<iomanip>
#include<ios>
#include<iosfwd>
#include<iostream>
#include<istream>
#include<iterator>
#include<limits>
#include<list>
#include<locale>
#include<map>
#include<memory>
#include<new>
#include<numeric>
#include<ostream>
#include<queue>
#include<set>
#include<sstream>
#include<stack>
#include<stdexcept>
#include<streambuf>
#include<string>
#include<typeinfo>
#include<utility>
#include<valarray>
#include<vector>
#include<cctype>
#include<cerrno>
#include<cfloat>
#include<ciso646>
#include<climits>
#include<clocale>
#include<cmath>
#include<csetjmp>
#include<csignal>
#include<cstdarg>
#include<cstddef>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#define E 200010
#define eps 1e-3
using namespace std;
inline int read(){
    int res=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') res=res*10+ch-'0',ch=getchar();
    return res*f;
}
inline void write(int x){
    if(x<0) putchar('-'),x=-x;
    if(x<10) putchar(x+'0');
    else{
        write(x/10);
        putchar(x%10+'0');
    }
}
//queue<int> q;
//set<int> s;
//priority_queue<int> q1;
//priority_queue<int,vector<int>,greater<int> > q2;
//list<int> l;
//stack<int> s;
int n;
string str[100010];
int fir[E],nxt[E],son[E],tot,cnt,Max;
double w[E],dis[E],flag;
int vis[E];
int f[6666];
void add(int x,int y,double z){++tot;son[tot]=y;nxt[tot]=fir[x];fir[x]=tot;w[tot]=z;}
void spfa(int s,int v,double mid){
    if(flag==1) return ;
    vis[s]=v;
    for(int i=fir[s];i;i=nxt[i]){
        int to=son[i];
        if(dis[s]+w[i]>dis[to]+mid){
            dis[to]=dis[s]+w[i]-mid;
            if(dis[to]>Max){
                flag=1;
                return ;
            }
            if(!vis[to]) spfa(to,v,mid);
            if(flag) return ;
            else if(vis[to]==v){
                flag=1;
                return ;
            }
        }
    }
    vis[s]=0;
}
bool check(double mid){
    flag=0;
    for(int i=0;i<=cnt;i++) dis[i]=0.0;
    memset(vis,0,sizeof(vis));
    for(int i=1;i<=cnt;i++){
        spfa(i,i,mid);
        if(flag==1) break;
    }
    return flag;
}
int main(){
//  freopen("code.in","r",stdin);freopen("code.out","w",stdout);
    n=read();
    while(n!=0){
        for(int i=1;i<=n;i++) cin>>str[i];
        memset(fir,0,sizeof(fir));
        memset(f,0,sizeof(f));
        tot=0;cnt=0;Max=0;
        for(int i=1;i<=n;i++){
            int len=str[i].length();
            Max=max(Max,len);
            int a=(str[i][0]-'a')*26+str[i][1]-'a';
            int b=(str[i][len-2]-'a')*26+str[i][len-1]-'a';
            if(!f[a]) f[a]=++cnt;
            int A=f[a];
            if(!f[b]) f[b]=++cnt;
            int B=f[b];
            add(A,B,(double)len);
        }
        Max*=n;
        double l=0,r=1000,ans=-1,mid;
        while(l+eps<r){
            mid=(l+r)/2;
            if(check(mid)) l=mid,ans=mid;
            else r=mid;
        }
        if(ans!=-1) printf("%.2lf\n",ans);
        else puts("No solution");
        n=read();
    }
    return 0;
}

#10083. 「一本通 3.3 例 2」雙調路徑

題意

讀入一個圖,計算最小路徑的總數。費用時間都相同的兩條最小路徑只算一條,輸出不一樣種類的最小路徑數。this

思路

\[f[i][j]\]表示在i點且已花費j時的最少時間
\[ f[i][j]=Min(f[k][i-Cost(k,i)]+Time(k,i)|For Each Edge(k,i)) \]es5

#include<algorithm>
#include<bitset>
#include<complex>
#include<deque>
#include<exception>
#include<fstream>
#include<functional>
#include<iomanip>
#include<ios>
#include<iosfwd>
#include<iostream>
#include<istream>
#include<iterator>
#include<limits>
#include<list>
#include<locale>
#include<map>
#include<memory>
#include<new>
#include<numeric>
#include<ostream>
#include<queue>
#include<set>
#include<sstream>
#include<stack>
#include<stdexcept>
#include<streambuf>
#include<string>
#include<typeinfo>
#include<utility>
#include<valarray>
#include<vector>
#include<cctype>
#include<cerrno>
#include<cfloat>
#include<ciso646>
#include<climits>
#include<clocale>
#include<cmath>
#include<csetjmp>
#include<csignal>
#include<cstdarg>
#include<cstddef>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#define E 5010
#define eps 1e-3
using namespace std;
inline int read(){
    int res=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') res=res*10+ch-'0',ch=getchar();
    return res*f;
}
inline void write(int x){
    if(x<0) putchar('-'),x=-x;
    if(x<10) putchar(x+'0');
    else{
        write(x/10);
        putchar(x%10+'0');
    }
}
//queue<int> q;
//set<int> s;
//priority_queue<int> q1;
//priority_queue<int,vector<int>,greater<int> > q2;
//list<int> l;
//stack<int> s;
int n,m,s,e;
int fir[E],nxt[E],son[E],w[E],cost[E],tot;
void add(int x,int y,int z,int t){++tot;w[tot]=z;son[tot]=y;nxt[tot]=fir[x];cost[tot]=t;fir[x]=tot;}
int dp[105][10005][3],Max,ans,Min=2e9;
int vis[105][10005];
struct node{int pos,dis;};
queue<node> q;
node make(int x,int y){node pp;pp.pos=x;pp.dis=y;return pp;}
void spfa(){
    while(!q.empty()) q.pop();
    for(int i=1;i<=n;i++){
        for(int j=0;j<=10005;j++){
            dp[i][j][0]=2e9;
            dp[i][j][1]=0;
        }
    }
    dp[s][0][0]=0;
    dp[s][0][1]=1;
    vis[s][0]=1;
    q.push(make(s,0));
    while(!q.empty()){
        int u=q.front().pos,dis=q.front().dis;q.pop();
        vis[u][dis]=0;
        for(int i=fir[u];i;i=nxt[i]){
            int to=son[i];
            if(dis+w[i]>Max+1) continue ;
            if(dp[u][dis][0]+cost[i]<dp[to][dis+w[i]][0]){
                dp[to][dis+w[i]][0]=dp[u][dis][0]+cost[i];
                dp[to][dis+w[i]][1]=1;
                if(vis[to][dis+w[i]]==0){
                    vis[to][dis+w[i]]=1;
                    q.push(make(to,dis+w[i]));
                }
            }
        }
    }
}
int main(){
//  freopen("code.in","r",stdin);freopen("code.out","w",stdout);
    n=read();m=read();s=read();e=read();
    for(int i=1;i<=m;i++){
        int x=read(),y=read(),z=read(),t=read();
        add(x,y,z,t);
        add(y,x,z,t);
    }
    Max=(n-1)*100+5;
    spfa();
    for(int i=0;i<=Max;i++){
        if(!dp[e][i][1]) continue ;
        if(dp[e][i][0]<Min){
            ans++;
            Min=dp[e][i][0];
        }
    }
    write(ans);putchar('\n');
    return 0;
}

#10084. 「一本通 3.3 練習 1」最小圈

題意

讀入一個有向圖,求圖中最小環的最小平均值時多少。spa

思路

固然食用二分+spfa啦。.net

基本思路同#10082. 「一本通 3.3 例 1」Word Rings

平均值公式:
\[ Average=(E_1+E_2+.....+E_n)/n \]

\[ Average*n=(E_1+E_2+...+E_n) \]

\[ (E_1-Average)+(E_2-Average)+...+(E_n-Average)=0 \]

那麼能夠二分答案:
\[ (E_1-Ans)+(E_2-Ans)+...+(E_n-Ans)\geq0 \]
而後瞎搞spfa。

但此次求最小值。

改一改符號就行了。

#include<algorithm>
#include<bitset>
#include<complex>
#include<deque>
#include<exception>
#include<fstream>
#include<functional>
#include<iomanip>
#include<ios>
#include<iosfwd>
#include<iostream>
#include<istream>
#include<iterator>
#include<limits>
#include<list>
#include<locale>
#include<map>
#include<memory>
#include<new>
#include<numeric>
#include<ostream>
#include<queue>
#include<set>
#include<sstream>
#include<stack>
#include<stdexcept>
#include<streambuf>
#include<string>
#include<typeinfo>
#include<utility>
#include<valarray>
#include<vector>
#include<cctype>
#include<cerrno>
#include<cfloat>
#include<ciso646>
#include<climits>
#include<clocale>
#include<cmath>
#include<csetjmp>
#include<csignal>
#include<cstdarg>
#include<cstddef>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#define E 20010
#define eps 1e-10
using namespace std;
inline int read(){
    int res=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') res=res*10+ch-'0',ch=getchar();
    return res*f;
}
inline void write(int x){
    if(x<0) putchar('-'),x=-x;
    if(x<10) putchar(x+'0');
    else{
        write(x/10);
        putchar(x%10+'0');
    }
}
//queue<int> q;
//set<int> s;
//priority_queue<int> q1;
//priority_queue<int,vector<int>,greater<int> > q2;
//list<int> l;
//stack<int> s;
int n,m;
int fir[E],nxt[E],son[E],tot;
double w[E],Max;
void add(int x,int y,double z){++tot;nxt[tot]=fir[x];son[tot]=y;fir[x]=tot;w[tot]=z;}
double dis[E],flag;
int vis[E];
int spfa(int s,double mid){
    vis[s]=1;
    for(int i=fir[s];i;i=nxt[i]){
        int to=son[i];
        if(dis[s]+w[i]-mid<dis[to]){
            dis[to]=dis[s]+w[i]-mid;
            if(vis[to]||spfa(to,mid)){vis[s]=0;return 1;}
        }
    }
    vis[s]=0;
    return 0;
}
bool check(double mid){
    for(int i=0;i<=n;i++) dis[i]=0.0;
    memset(vis,0,sizeof(vis));
    for(int i=1;i<=n;i++){
        if(spfa(i,mid)==1) return 1;
    }
    return 0;
}
int main(){
    n=read();m=read();
    for(int i=1;i<=m;i++){
        int x=read(),y=read();double z;
        cin>>z;
        add(x,y,(double)z);
    }
    double l=-1e7,r=1e7,mid,ans;
    while(l+eps<r){
        mid=(l+r)/2;
        if(check(mid)) r=mid-eps,ans=mid;
        else l=mid+eps;
    }
    printf("%.8lf\n",ans);
    return 0;
}

#10085. 「一本通 3.3 練習 2」蟲洞 Wormholes

題意

如今 John 想借助這些蟲洞來回到過去(在出發時刻以前回到出發點),請你告訴他能辦到嗎。 John 將向你提供 F 個農場的地圖。

思路

固然食用spfa啦。

固然,由於我懶,因此將上一題代碼改一改就行了呀。

注意建雙向邊。

注意建負邊。

check時只須要傳入參數0就行了。

由於又沒有讓你求平均值QWQ

#include<algorithm>
#include<bitset>
#include<complex>
#include<deque>
#include<exception>
#include<fstream>
#include<functional>
#include<iomanip>
#include<ios>
#include<iosfwd>
#include<iostream>
#include<istream>
#include<iterator>
#include<limits>
#include<list>
#include<locale>
#include<map>
#include<memory>
#include<new>
#include<numeric>
#include<ostream>
#include<queue>
#include<set>
#include<sstream>
#include<stack>
#include<stdexcept>
#include<streambuf>
#include<string>
#include<typeinfo>
#include<utility>
#include<valarray>
#include<vector>
#include<cctype>
#include<cerrno>
#include<cfloat>
#include<ciso646>
#include<climits>
#include<clocale>
#include<cmath>
#include<csetjmp>
#include<csignal>
#include<cstdarg>
#include<cstddef>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#define E 20010
#define eps 1e-10
using namespace std;
inline int read(){
    int res=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') res=res*10+ch-'0',ch=getchar();
    return res*f;
}
inline void write(int x){
    if(x<0) putchar('-'),x=-x;
    if(x<10) putchar(x+'0');
    else{
        write(x/10);
        putchar(x%10+'0');
    }
}
//queue<int> q;
//set<int> s;
//priority_queue<int> q1;
//priority_queue<int,vector<int>,greater<int> > q2;
//list<int> l;
//stack<int> s;
int n,m;
int fir[E],nxt[E],son[E],tot;
double w[E],Max;
void add(int x,int y,double z){++tot;nxt[tot]=fir[x];son[tot]=y;fir[x]=tot;w[tot]=z;}
double dis[E],flag;
int vis[E];
int spfa(int s,double mid){
    vis[s]=1;
    for(int i=fir[s];i;i=nxt[i]){
        int to=son[i];
        if(dis[s]+w[i]-mid<dis[to]){
            dis[to]=dis[s]+w[i]-mid;
            if(vis[to]||spfa(to,mid)){vis[s]=0;return 1;}
        }
    }
    vis[s]=0;
    return 0;
}
bool check(double mid){
    for(int i=0;i<=n;i++) dis[i]=0.0;
    memset(vis,0,sizeof(vis));
    for(int i=1;i<=n;i++){
        if(spfa(i,mid)==1) return 1;
    }
    return 0;
}
int main(){
    int T,W;
    T=read();
    while(T--){
        n=read();m=read();W=read();
        tot=0;
        memset(fir,0,sizeof(fir));
        for(int i=1;i<=m;i++){
            int x=read(),y=read();double z;
            cin>>z;
            add(x,y,(double)z);
            add(y,x,(double)z);
        }
        for(int i=1;i<=W;i++){
            int x=read(),y=read();double z;
            cin>>z;
            add(x,y,-z);
        }
        if(check(0)) puts("YES");
        else puts("NO");
    }
    return 0;
}

#10086. 「一本通 3.3 練習 3」Easy SSSP

題意

給你一個圖,問從源點到每一個節點的最短路徑分別是多少。

若是存在負權迴路,只輸出一行 -1;若是不存在負權迴路,再求出一個點 S 到每一個點的最短路的長度。若是 S 與這個點不連通,則輸出 NoPath

思路

固然食用spfa啦。

先跑一下非源點的。萬一數據卡你其餘有環呢?

而後再跑一次源點。得出Ans

#include<algorithm>
#include<bitset>
#include<complex>
#include<deque>
#include<exception>
#include<fstream>
#include<functional>
#include<iomanip>
#include<ios>
#include<iosfwd>
#include<iostream>
#include<istream>
#include<iterator>
#include<limits>
#include<list>
#include<locale>
#include<map>
#include<memory>
#include<new>
#include<numeric>
#include<ostream>
#include<queue>
#include<set>
#include<sstream>
#include<stack>
#include<stdexcept>
#include<streambuf>
#include<string>
#include<typeinfo>
#include<utility>
#include<valarray>
#include<vector>
#include<cctype>
#include<cerrno>
#include<cfloat>
#include<ciso646>
#include<climits>
#include<clocale>
#include<cmath>
#include<csetjmp>
#include<csignal>
#include<cstdarg>
#include<cstddef>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#define E 2000010
#define eps 1e-10
#define ll long long
using namespace std;
inline ll read(){
    ll res=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') res=res*10+ch-'0',ch=getchar();
    return res*f;
}
inline void write(ll x){
    if(x<0) putchar('-'),x=-x;
    if(x<10) putchar(x+'0');
    else{
        write(x/10);
        putchar(x%10+'0');
    }
}
//queue<ll> q;
//set<ll> s;
//priority_queue<ll> q1;
//priority_queue<ll,vector<ll>,greater<ll> > q2;
//list<ll> l;
//stack<ll> s;
ll n,m;
ll fir[E],nxt[E],son[E],tot;
double w[E],Max;
void add(ll x,ll y,double z){++tot;nxt[tot]=fir[x];son[tot]=y;fir[x]=tot;w[tot]=z;}
double dis[E],flag;
ll vis[E];
queue<int> q;
int tt[E];
ll spfa(ll s){
    vis[s]=1;
    q.push(s);
    while(!q.empty()){
        int u=q.front();q.pop();
        for(ll i=fir[u];i;i=nxt[i]){
            ll to=son[i];
            if(dis[u]+w[i]<dis[to]){
                dis[to]=dis[u]+w[i];
                tt[to]++;
                if(tt[to]>n+1){
                    puts("-1");
                    exit(0);
                }
                if(vis[to]==0){
                    vis[to]=1;
                    q.push(to);
                }
            }
        }
        vis[u]=0;
    }
    return 0;
}
ll st[E];
int main(){
    ll T=1,S;
    while(T--){
        n=read();m=read();S=read();
        tot=0;
        memset(fir,0,sizeof(fir));
        for(ll i=1;i<=m;i++){
            ll x=read(),y=read();double z;
            cin>>z;
            add(x,y,(double)z);
        }
        for(ll i=0;i<=n;i++) dis[i]=2e18;
        dis[S]=0;
        memset(vis,0,sizeof(vis));
        spfa(2);
        for(ll i=0;i<=n;i++) dis[i]=2e18;
        dis[S]=0;
        memset(vis,0,sizeof(vis));
        spfa(S);
        
        for(ll i=1;i<=n;i++){
            if(i==S) puts("0");
            else if(dis[i]>=2e18) puts("NoPath");
            else{
                printf("%.0lf\n",dis[i]);
            }
        }
    }
    return 0;
}

#10087. 「一本通 3.4 例 1」Intervals

題意

\(0\sim 5\times 10^4\)中選出儘可能少的整數,使每一個區間\([a_i,b_i]\)內都有至少\(c_i\)個數被選出。

思路

固然食用spfa啦。

\(s[k]\)表示0~k中至少選多少個整數。根據題意可得:
\[ s[b_i]-s[a_i-1]\geq c_i \]

\[ s[k]-s[k-1]\geq0 \]

\[ s[k]-s[k-1]\leq1 \]

也就是:
\[ s[k-1]-s[k]\geq-1 \]
那麼跑一次最長路就行了。

#include<algorithm>
#include<bitset>
#include<complex>
#include<deque>
#include<exception>
#include<fstream>
#include<functional>
#include<iomanip>
#include<ios>
#include<iosfwd>
#include<iostream>
#include<istream>
#include<iterator>
#include<limits>
#include<list>
#include<locale>
#include<map>
#include<memory>
#include<new>
#include<numeric>
#include<ostream>
#include<queue>
#include<set>
#include<sstream>
#include<stack>
#include<stdexcept>
#include<streambuf>
#include<string>
#include<typeinfo>
#include<utility>
#include<valarray>
#include<vector>
#include<cctype>
#include<cerrno>
#include<cfloat>
#include<ciso646>
#include<climits>
#include<clocale>
#include<cmath>
#include<csetjmp>
#include<csignal>
#include<cstdarg>
#include<cstddef>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#define E 2000010
#define eps 1e-10
#define ll long long
using namespace std;
inline ll read(){
    ll res=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') res=res*10+ch-'0',ch=getchar();
    return res*f;
}
inline void write(ll x){
    if(x<0) putchar('-'),x=-x;
    if(x<10) putchar(x+'0');
    else{
        write(x/10);
        putchar(x%10+'0');
    }
}
//queue<ll> q;
//set<ll> s;
//priority_queue<ll> q1;
//priority_queue<ll,vector<ll>,greater<ll> > q2;
//list<ll> l;
//stack<ll> s;
ll n,m;
ll fir[E],nxt[E],son[E],tot;
double w[E];
void add(ll x,ll y,double z){++tot;nxt[tot]=fir[x];son[tot]=y;fir[x]=tot;w[tot]=z;}
double dis[E],flag;
ll vis[E];
queue<int> q;
int tt[E];
ll spfa(ll s){
    vis[s]=1;
    q.push(s);
    while(!q.empty()){
        int u=q.front();q.pop();
        for(ll i=fir[u];i;i=nxt[i]){
            ll to=son[i];
            if(dis[u]+w[i]>dis[to]){
                dis[to]=dis[u]+w[i];
                tt[to]++;
                if(tt[to]>n+1){
                    puts("-1");
                    exit(0);
                }
                if(vis[to]==0){
                    vis[to]=1;
                    q.push(to);
                }
            }
        }
        vis[u]=0;
    }
    return 0;
}
ll st[E],Min,Max;
int main(){
    n=read();
    Max=-1;Min=2e9;
    for(int i=1;i<=n;i++){
        ll x=read(),y=read(),z=read();
        add(x-1,y,z);
        Max=max(Max,y);
        Min=min(Min,x-1);
    }
    for(int i=Min;i<=Max;i++){
        add(i,i+1,0);
        add(i+1,i,-1);
    }
    for(int i=Min;i<=Max;i++) dis[i]=-2e9;
    memset(vis,0,sizeof(vis));
    dis[Min]=0;
    spfa(Min);
    printf("%.0lf\n",dis[Max]);
    return 0;
}

#10088. 「一本通 3.4 例 2」出納員問題

題意

\(、、R(0)、R(1)、R(2)...R(23)\)表示第x個時刻須要\(R(x)\)個出納員,有n個出納員申請工做,第\(i\)個出納員從\(t_i\)時刻開始工做\(8\)小時,問至少須要多少出納員?

思路

\(x[i]\)表示第i時刻實際上須要僱傭\(x[i]\)人,\(r[i]\)爲第i時刻至少須要\(r[i]\)我的。
\[ x[i-7]+x[i-6]+x[i-5]+x[i-4]+x[i-3]+x[i-2]+x[i-1]+x[i]\geq r[i] \]
\(s[i]=x[1]+x[2]+x[3]+...+x[i]\),可得:
\[ s[i]-s[i-1]\geq0 \]

\[ s[i-1]-s[i]\geq-num[i] \]

\[ s[i]-s[i-8]\geq r[i] \]

\[ s[i]-s[i+16]\geq r[i]-s[23] \]

#include<algorithm>
#include<bitset>
#include<complex>
#include<deque>
#include<exception>
#include<fstream>
#include<functional>
#include<iomanip>
#include<ios>
#include<iosfwd>
#include<iostream>
#include<istream>
#include<iterator>
#include<limits>
#include<list>
#include<locale>
#include<map>
#include<memory>
#include<new>
#include<numeric>
#include<ostream>
#include<queue>
#include<set>
#include<sstream>
#include<stack>
#include<stdexcept>
#include<streambuf>
#include<string>
#include<typeinfo>
#include<utility>
#include<valarray>
#include<vector>
#include<cctype>
#include<cerrno>
#include<cfloat>
#include<ciso646>
#include<climits>
#include<clocale>
#include<cmath>
#include<csetjmp>
#include<csignal>
#include<cstdarg>
#include<cstddef>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#define E 2000010
#define eps 1e-10
#define ll long long
using namespace std;
inline ll read(){
    ll res=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') res=res*10+ch-'0',ch=getchar();
    return res*f;
}
inline void write(ll x){
    if(x<0) putchar('-'),x=-x;
    if(x<10) putchar(x+'0');
    else{
        write(x/10);
        putchar(x%10+'0');
    }
}
//queue<ll> q;
//set<ll> s;
//priority_queue<ll> q1;
//priority_queue<ll,vector<ll>,greater<ll> > q2;
//list<ll> l;
//stack<ll> s;
ll n,m;
ll fir[E],nxt[E],son[E],tot;
int w[E];
void add(ll x,ll y,ll z){++tot;nxt[tot]=fir[x];son[tot]=y;fir[x]=tot;w[tot]=z;}
int dis[E],flag;
ll vis[E];
queue<int> q;
int tt[E];
ll spfa(ll s){
    memset(dis,63,sizeof(dis));
    memset(tt,0,sizeof(tt));
    memset(vis,0,sizeof(vis));
    dis[24]=0;
    vis[24]=1;
    q.push(24);
    while(!q.empty()){
        int u=q.front();q.pop();
        for(ll i=fir[u];i;i=nxt[i]){
            ll to=son[i];
            if(dis[u]+w[i]<dis[to]){
                dis[to]=dis[u]+w[i];
                tt[to]++;
                if(tt[to]>n+1){
                    return 0;
                }
                if(vis[to]==0){
                    vis[to]=1;
                    q.push(to);
                }
            }
        }
        vis[u]=0;
    }
    return dis[0]==-s;
}
ll T,r[E],Min,Max,s[E],num[E];
void work(int x){
    memset(fir,0,sizeof(fir));tot=0;
    for(register int i=1;i<=24;i++) add(i,i-1,0),add(i-1,i,num[i]);
    for(register int i=8;i<=24;i++) add(i,i-8,-r[i]);
    for(register int j=1;j<=7;j++) add(j,j+16,x-r[j]);
    add(24,0,-x);
}
int main(){
    T=read();
    while(T--){
        for(int i=1;i<=24;i++){
            r[i]=read();num[i]=0;
        }
        n=read();
        for(int i=1;i<=n;i++){
            int t=read();t++;
            num[t]++;
        }
        int l=0,r=n,ans=2e9;
        while(l<=r){
            int mid=(l+r)/2;
            work(mid);
            if(spfa(mid)) ans=mid,r=mid-1;
            else l=mid+1;
        }
        if(ans==2e9) puts("No Solution");
        else write(ans),putchar('\n');
    }
    return 0;
}

#10089. 「一本通 3.4 練習 1」糖果

題意

知足條件:

若是 X=1.表示第 A 個小朋友分到的糖果必須和第 B 個小朋友分到的精果同樣多。
若是 X=2,表示第 A 個小朋友分到的糖果必須少於第 B 個小朋友分到的糖果。
若是 X=3,表示第 A 個小朋友分到的糖果必須很多於第 B 個小朋友分到的糖果。
若是 X=4,表示第 A 個小朋友分到的糖果必須多於第 B 個小朋友分到的糖果。
若是 X=5,表示第 A 個小朋友分到的糖果必須很少於第 B 個小朋友分到的糖果。

求至少須要準備的糖果數?

思路

若是 X=1
\[ B=A \]
若是 X=2
\[ B+1\ge A \]
若是 X=3
\[ A\ge B \]
若是 X=4
\[ A-1\ge B \]
若是 X=5
\[ B \ge A \]
那麼就行了嘛:

#include<algorithm>
#include<bitset>
#include<complex>
#include<deque>
#include<exception>
#include<fstream>
#include<functional>
#include<iomanip>
#include<ios>
#include<iosfwd>
#include<iostream>
#include<istream>
#include<iterator>
#include<limits>
#include<list>
#include<locale>
#include<map>
#include<memory>
#include<new>
#include<numeric>
#include<ostream>
#include<queue>
#include<set>
#include<sstream>
#include<stack>
#include<stdexcept>
#include<streambuf>
#include<string>
#include<typeinfo>
#include<utility>
#include<valarray>
#include<vector>
#include<cctype>
#include<cerrno>
#include<cfloat>
#include<ciso646>
#include<climits>
#include<clocale>
#include<cmath>
#include<csetjmp>
#include<csignal>
#include<cstdarg>
#include<cstddef>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#define E 300010
#define eps 1e-10
#define ll long long
#pragma GCC optimize(2)
using namespace std;
inline ll read(){
    ll res=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') res=res*10+ch-'0',ch=getchar();
    return res*f;
}
inline void write(ll x){
    if(x<0) putchar('-'),x=-x;
    if(x<10) putchar(x+'0');
    else{
        write(x/10);
        putchar(x%10+'0');
    }
}
//queue<ll> q;
//set<ll> s;
//priority_queue<ll> q1;
//priority_queue<ll,vector<ll>,greater<ll> > q2;
//list<ll> l;
//stack<ll> s;
ll n,k;
ll fir[E],nxt[E],son[E],w[E],tot,inf,ans;
inline void add(register ll x,register ll y,register ll z){
    ++tot;
    w[tot]=z;
    nxt[tot]=fir[x];
    fir[x]=tot;
    son[tot]=y;
}
ll dis[E],vis[E],tt[E];
deque<ll> q;
inline void spfa(){
    memset(dis,0,sizeof(dis));
    memset(tt,0,sizeof(tt));
    memset(vis,0,sizeof(vis));inf=dis[0];
    dis[0]=0;vis[0]=1;
    while(!q.empty()) q.pop_front();
    q.push_back(0);
    while(!q.empty()){
        register ll u=q.front();q.pop_front();
        vis[u]=0;
        for(register ll i=fir[u];i;i=nxt[i]){
            register ll to=son[i];
            if(dis[to]<dis[u]+w[i]){
                tt[to]++;
                dis[to]=dis[u]+w[i];
                if(tt[to]>n+1){
                    puts("-1");
                    exit(0);
                }
                if(!vis[to]){
                    vis[to]=1;
                    if(!q.empty()&&dis[to]<dis[q.front()]) q.push_front(to);
                    else q.push_back(to);
                }
            }
        }
    }
}
int main(){
    n=read();k=read();
    for(register ll i=1;i<=k;i++){
        ll x=read(),a=read(),b=read();
        if(a==b&&(x==2||x==4)){
            puts("-1");
            return 0;
        }
        if(x==1) add(a,b,0),add(b,a,0);
        if(x==2) add(a,b,1);
        if(x==3) add(b,a,0);
        if(x==4) add(b,a,1);
        if(x==5) add(a,b,0);
    }
    for(register ll i=1;i<=n;i++) add(0,i,1);
    spfa();
    for(register ll i=1;i<=n;i++){
        ans+=dis[i];
    }
    write(ans);putchar('\n');
    return 0;
}

#10090. 「一本通 3.4 練習 2」佈局 Layout

題意

有些奶牛是好基友,它們但願彼此之間的距離小於等於某個數。有些奶牛是情敵,它們但願彼此之間的距離大於等於某個數。

思路

若是兩隻奶牛是好基友,那麼:
\[ A-B\leq D \]
若是兩隻奶牛是情敵,那麼:
\[ A-B\ge D \]
即:
\[ D\leq A-B \]
也就是:
\[ B-A\leq -D \]
直接上代碼:

#include<algorithm>
#include<bitset>
#include<complex>
#include<deque>
#include<exception>
#include<fstream>
#include<functional>
#include<iomanip>
#include<ios>
#include<iosfwd>
#include<iostream>
#include<istream>
#include<iterator>
#include<limits>
#include<list>
#include<locale>
#include<map>
#include<memory>
#include<new>
#include<numeric>
#include<ostream>
#include<queue>
#include<set>
#include<sstream>
#include<stack>
#include<stdexcept>
#include<streambuf>
#include<string>
#include<typeinfo>
#include<utility>
#include<valarray>
#include<vector>
#include<cctype>
#include<cerrno>
#include<cfloat>
#include<ciso646>
#include<climits>
#include<clocale>
#include<cmath>
#include<csetjmp>
#include<csignal>
#include<cstdarg>
#include<cstddef>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#define E 300010
#define eps 1e-10
#define ll long long
#pragma GCC optimize(2)
using namespace std;
inline ll read(){
    ll res=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') res=res*10+ch-'0',ch=getchar();
    return res*f;
}
inline void write(ll x){
    if(x<0) putchar('-'),x=-x;
    if(x<10) putchar(x+'0');
    else{
        write(x/10);
        putchar(x%10+'0');
    }
}
//queue<ll> q;
//set<ll> s;
//priority_queue<ll> q1;
//priority_queue<ll,vector<ll>,greater<ll> > q2;
//list<ll> l;
//stack<ll> s;
ll n,k;
ll fir[E],nxt[E],son[E],w[E],tot,inf,ans;
inline void add(register ll x,register ll y,register ll z){
    ++tot;
    w[tot]=z;
    nxt[tot]=fir[x];
    fir[x]=tot;
    son[tot]=y;
}
ll dis[E],vis[E],tt[E];
deque<ll> q;
inline void spfa(int s){
    memset(dis,63,sizeof(dis));
    memset(tt,0,sizeof(tt));
    memset(vis,0,sizeof(vis));inf=dis[0];
    dis[s]=0;vis[s]=1;
    while(!q.empty()) q.pop_front();
    q.push_back(s);
    while(!q.empty()){
        register ll u=q.front();q.pop_front();
        vis[u]=0;
        for(register ll i=fir[u];i;i=nxt[i]){
            register ll to=son[i];
            if(dis[to]>dis[u]+w[i]){
                tt[to]++;
                dis[to]=dis[u]+w[i];
                if(tt[to]>n+1){
                    puts("-1");
                    exit(0);
                }
                if(!vis[to]){
                    vis[to]=1;
                    if(!q.empty()&&dis[to]<dis[q.front()]) q.push_front(to);
                    else q.push_back(to);
                }
            }
        }
    }
}
int k1,k2;
int main(){
    n=read();k1=read();k2=read();
    for(register ll i=1;i<=k1;i++){
        ll x=read(),y=read(),z=read();
        add(x,y,z);
    }
    for(register ll i=1;i<=k2;i++){
        ll x=read(),y=read(),z=read();
        add(y,x,-z);
    }
    spfa(1);
    ans=dis[n];
    for(int i=1;i<=n;i++) spfa(i);
    if(ans>=inf) puts("-2");
    else write(ans),putchar('\n');
    return 0;
}

#130. 樹狀數組 1 :單點修改,區間查詢

題意

這是一道模板題。

給定Q個操做:

  • 1 i x:給定 \(i,x\),將 \(a[i]\) 加上 \(x\)
  • 2 l r:給定 \(l,r\),求 \(\sum_{i=l}^ra[i]\) 的值(換言之,求 \(a[l]+a[l+1]+\dots+a[r]\) 的值)。

思路

模板題呀。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll n,m;
ll a[5000010];
void add(ll x,ll y){
    for(ll i=x;i<=n;i+=i&(-i)){
        a[i]+=y;
    }
}
ll getsum(ll x){
    ll sum=0;
    for(ll i=x;i;i-=i&(-i)){
        sum+=a[i];
    }
    return sum;
}
int main(){
    scanf("%lld%lld",&n,&m);
    for(ll i=1;i<=n;i++){
        ll x;scanf("%lld",&x);
        add(i,x);
    }
    for(ll i=1;i<=m;i++){
        ll type;
        scanf("%lld",&type);
        if(type==1){
            ll x,k;
            scanf("%lld%lld",&x,&k);
            add(x,k);
        }
        if(type==2){
            ll x,y;
            scanf("%lld%lld",&x,&y);
            printf("%lld\n",getsum(y)-getsum(x-1));
        }
    }
}

#10114. 「一本通 4.1 例 2」數星星 Stars

題意

給定 \(n\) 個點,定義每一個點的等級是在該點左下方(含正左、正下)的點的數目,試統計每一個等級有多少個點。

思路

這不就是裸的樹狀數組嗎?

題目都按順序(y的增序)。。。

#include<bits/stdc++.h>
using namespace std;
inline int read(){
    char ch=getchar();int res=0,f=1;
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') res=res*10+ch-'0',ch=getchar();
    return res*f;
}
inline void write(int x){
    if(x<0) putchar('-'),x=-x;
    if(x<10) putchar(x+'0');
    else{
        write(x/10);
        putchar(x%10+'0');
    }
}
int n,x,y,ans[1500010];
int a[1500010];
void add(int x,int y){
    for(int i=x;i<=1500000;i+=i&(-i)){
        a[i]+=y;
    }
}
int getsum(int x){
    int sum=0;
    for(int i=x;i;i-=i&(-i)){
        sum+=a[i];
    }
    return sum;
}
int main(){
    n=read();
    for(int i=1;i<=n;i++){
        x=read();y=read();
        int t=getsum(x+1);
        ans[t]++;
        add(x+1,1);
    }
    for(int i=0;i<n;i++){
        write(ans[i]);putchar('\n');
    }
    return 0;
}

2019.1.28

#10116. 「一本通 4.1 練習 1」清點人數

題意

  • 若是字母爲 A,接下來是一個數 \(m\),表示年級主任如今在第 \(m\) 節車箱;
  • 若是字母爲 B,接下來是兩個數 \(m,p\),表示在第 \(m\) 節車箱有 \(p\) 名學生上車;
  • 若是字母爲 C,接下來是兩個數 \(m,p\),表示在第 \(m\) 節車箱有 \(p\) 名學生下車。

思路

固然是樹狀數組啦。。。

若是字母爲 A ,那麼\(getsum(m)\)

若是字母爲 B ,那麼\(add(m,p)\)

若是字母爲 C ,那麼\(add(m,-p)\)

好了呀。。。

#include<algorithm>
#include<bitset>
#include<complex>
#include<deque>
#include<exception>
#include<fstream>
#include<functional>
#include<iomanip>
#include<ios>
#include<iosfwd>
#include<iostream>
#include<istream>
#include<iterator>
#include<limits>
#include<list>
#include<locale>
#include<map>
#include<memory>
#include<new>
#include<numeric>
#include<ostream>
#include<queue>
#include<set>
#include<sstream>
#include<stack>
#include<stdexcept>
#include<streambuf>
#include<string>
#include<typeinfo>
#include<utility>
#include<valarray>
#include<vector>
#include<cctype>
#include<cerrno>
#include<cfloat>
#include<ciso646>
#include<climits>
#include<clocale>
#include<cmath>
#include<csetjmp>
#include<csignal>
#include<cstdarg>
#include<cstddef>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
using namespace std;
inline int read(){
    int res=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') res=res*10+ch-'0',ch=getchar();
    return res*f;
}
inline void write(int x){
    if(x<0) putchar('-'),x=-x;
    if(x<10) putchar(x+'0');
    else{
        write(x/10);
        putchar(x%10+'0');
    }
}
//queue<int> q;
//set<int> s;
//priority_queue<int> q1;
//priority_queue<int,vector<int>,greater<int> > q2;
//list<int> l;
//stack<int> s;
int n,k;
int c[500010];
void add(int x,int y){
    for(int i=x;i<=n;i+=i&(-i)){
        c[i]+=y;
    }
}
int getsum(int x){
    int sum=0;
    for(int i=x;i;i-=i&(-i)){
        sum+=c[i];
    }
    return sum;
}
int main(){
//  freopen("code.in","r",stdin);freopen("code.out","w",stdout);
    n=read();k=read();
    for(int i=1;i<=k;i++){
        char A;cin>>A;
        int m,p;
        if(A=='A'){
            m=read();
            write(getsum(m));putchar('\n');
        }else if(A=='B'){
            m=read();p=read();
            add(m,p);
        }else if(A=='C'){
            m=read();p=read();
            add(m,-p);
        }
    }
    return 0;
}

#10117. 「一本通 4.1 練習 2」簡單題

題意

有一個 \(n\) 個元素的數組,每一個元素初始均爲 \(0\)。有 \(m\) 條指令,要麼讓其中一段連續序列數字反轉——\(0\)\(1\)\(1\)\(0\)(操做 \(1\)),要麼詢問某個元素的值(操做 \(2\))。

思路

固然是樹狀數組啦。。。

這裏介紹C++的一大利器——位運算。

&在C++裏叫作與運算。應該差很少吧。。大概就是這樣的:(按一個個位運算)

1&1=1
0&1=0
1&0=0
0&0=0

|在C++裏叫或運算

0|1=1
1|0=1
1|1=1
0|0=0

^在C++裏叫異或(xor)

0^0=0
1^0=1
0^1=1
1^1=0

~在C++裏叫取反

顧名思義。。。

~1=0
~0=1

而後你就會發現這道題能夠用C++的異或+樹狀數組解決。

利用樹狀數組,作一個異或前綴和。而後在輸出時異或一遍就行了。

(這道題的1操做就至關於異或1)

(然而咱們知道x xor 1 xor 1仍是等於x)

(因此對於每一個1操做只須要先把l以前的xor 1,而後r+1以前的xor 1)

(對於每一個2操做只須要把前面通通xor一遍)

你就完美的解決了這道題

#include<bits/stdc++.h>
using namespace std;//醜陋無比的頭文件終於結束
inline int read(){
    int res=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') res=res*10+ch-'0',ch=getchar();
    return res*f;
}//讀入
inline void write(int x){
    if(x<0) putchar('-'),x=-x;
    if(x<10) putchar(x+'0');
    else{
        write(x/10);
        putchar(x%10+'0');
    }
}//輸出
int n,k;
int c[500010];//不解釋
void add(int x,int y){//修改
    for(int i=x;i<=n;i+=i&(-i)){
        c[i]^=y;//異或前綴和
    }
}
int getsum(int x){//詢問
    int sum=0;
    for(int i=x;i;i-=i&(-i)){
        sum^=c[i];//詢問異或
    }
    return sum;//返回啊
}
int main(){
    n=read();k=read();//讀入
    for(int i=1;i<=k;i++){
        char A;cin>>A;
        int m,p;
        if(A=='1'){//操做1
            m=read();p=read();
            add(m,1);//先將l以前的xor 1
            add(p+1,1);//而後把r+1以前的xor 1
            //那麼l以前的數通通 xor 1 xor 1,抵消
        }else if(A=='2'){
            m=read();
            write(getsum(m));putchar('\n');//詢問輸出
        }
    }
    return 0;//結束了。。。
}

#133. 二維樹狀數組 1:單點修改,區間查詢

題意

這是一道模板題。

給出一個 \(n\times m\) 的零矩陣 \(A\),你須要完成以下操做:

  • 1 x y k:表示元素 \(A_{x,y}\) 自增 \(k\)
  • 2 a b c d:表示詢問左上角爲 \((a,b)\),右下角爲 \((c,d)\) 的子矩陣內全部數的和。

思路

固然是樹狀數組啦。。。

模板題。不介紹。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll n,m;
ll a[5010][5010];
ll lowbit(ll x){
    return (x&-x);
}
void add(ll x,ll y,ll k){
    for(ll i=x;i<=n;i+=lowbit(i)){
        for(ll j=y;j<=m;j+=lowbit(j)){
            a[i][j]+=k;
        }
    }
}
ll getsum(ll x,ll y){
    ll sum=0;
    for(ll i=x;i>0;i-=lowbit(i)){
        for(ll j=y;j>0;j-=lowbit(j)){
            sum+=a[i][j];
        }
    }
    return sum;
}
int main(){
    scanf("%lld%lld",&n,&m);
    ll type;
    while(scanf("%lld",&type)!=EOF){
        if(type==1){
            ll x,y,k;
            scanf("%lld%lld%lld",&x,&y,&k);
            add(x,y,k);
        }
        if(type==2){
            ll x1,y1,x2,y2;
            scanf("%lld%lld%lld%lld",&x1,&y1,&x2,&y2);
            printf("%lld\n",getsum(x2,y2)-getsum(x2,y1-1)-getsum(x1-1,y2)+getsum(x1-1,y1-1));
        }
    }
    return 0;
}

#10119. 「一本通 4.2 例 1」數列區間最大值

題意

這是一道模板題。

輸入一串數字,給你 \(M\) 個詢問,每次詢問就給你兩個數字 \(X,Y\),要求你說出 \(X\)\(Y\) 這段區間內的最大數。

思路

模板題。不介紹。

RMQ問題

#include<algorithm>
#include<bitset>
#include<complex>
#include<deque>
#include<exception>
#include<fstream>
#include<functional>
#include<iomanip>
#include<ios>
#include<iosfwd>
#include<iostream>
#include<istream>
#include<iterator>
#include<limits>
#include<list>
#include<locale>
#include<map>
#include<memory>
#include<new>
#include<numeric>
#include<ostream>
#include<queue>
#include<set>
#include<sstream>
#include<stack>
#include<stdexcept>
#include<streambuf>
#include<string>
#include<typeinfo>
#include<utility>
#include<valarray>
#include<vector>
#include<cstring>
#include<cmath>
using namespace std;
inline int read(){
    char ch=getchar();int res=0,f=1;
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') res=res*10+ch-'0',ch=getchar();
    return res*f;
}
inline void write(int zx){
    if(zx<0) zx=-zx,putchar('-');
    if(zx<10) putchar(zx+'0');
    else{
        write(zx/10);
        putchar(zx%10+'0');
    }
}
int n,m,a[100010],f[100010][20];
void ST(){
    for(int i=1;i<=n;i++) f[i][0]=a[i];
    for(int j=1;(1<<j)<=n;j++){
        for(int i=1;i+(1<<j)-1<=n;i++){
            f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
        }
    }
}
int RMQ(int l,int r){
    int k=0;
    while((1<<(k+1))<=r-l+1) k++;
    return max(f[l][k],f[r-(1<<k)+1][k]);
}
int main(){
    n=read();m=read();
    for(int i=1;i<=n;i++) a[i]=read();
    ST();
    for(int i=1;i<=m;i++){
        int l=read(),r=read();
        write(RMQ(l,r));
        putchar('\n');
    }
    return 0;
}

#10120. 「一本通 4.2 例 2」最敏捷的機器人

題意

首先,他們面前會有一排共 \(n\) 個數,它們比賽看誰能最早把每連續 \(k\) 個數中最大和最小值寫下來,固然,這些機器人運算速度都很快,它們比賽的是誰寫得快。

思路

模板題。不介紹。

RMQ問題分別作一個最大值與最小值。

#include<algorithm>
#include<bitset>
#include<complex>
#include<deque>
#include<exception>
#include<fstream>
#include<functional>
#include<iomanip>
#include<ios>
#include<iosfwd>
#include<iostream>
#include<istream>
#include<iterator>
#include<limits>
#include<list>
#include<locale>
#include<map>
#include<memory>
#include<new>
#include<numeric>
#include<ostream>
#include<queue>
#include<set>
#include<sstream>
#include<stack>
#include<stdexcept>
#include<streambuf>
#include<string>
#include<typeinfo>
#include<utility>
#include<valarray>
#include<vector>
#include<cstring>
#include<cmath>
#define ll long long
using namespace std;
inline ll read(){
    char ch=getchar();ll res=0,f=1;
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') res=res*10+ch-'0',ch=getchar();
    return res*f;
}
inline void write(ll zx){
    if(zx<0) zx=-zx,putchar('-');
    if(zx<10) putchar(zx+'0');
    else{
        write(zx/10);
        putchar(zx%10+'0');
    }
}
ll n,m,a[100010],f[100010][20],f2[100010][20];
void ST(){
    for(ll i=1;i<=n;i++) f[i][0]=a[i];
    for(ll j=1;(1<<j)<=n;j++) {
        for(ll i=1;i+(1<<j)-1<=n;i++) {
            f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
        }
    }
}
ll RMQ(ll l,ll r){
    ll k=0;
    while((1<<(k+1))<=r-l+1) k++;
    return max(f[l][k],f[r-(1<<k)+1][k]);
}
void ST2(){
    for(ll i=1;i<=n;i++) f2[i][0]=a[i];
    for(ll j=1;(1<<j)<=n;j++) {
        for(ll i=1;i+(1<<j)-1<=n;i++) {
            f2[i][j]=min(f2[i][j-1],f2[i+(1<<(j-1))][j-1]);
        }
    }
}
ll RMQ2(ll l,ll r){
    ll k=0;
    while((1<<(k+1))<=r-l+1) k++;
    return min(f2[l][k],f2[r-(1<<k)+1][k]);
}
int main(){
    n=read();m=read();
    for(ll i=1;i<=n;i++) a[i]=read();
    ST();ST2();
    for(ll i=1;i<=n-m+1;i++){
        write(RMQ(i,i+m-1));
        putchar(' ');
        write(RMQ2(i,i+m-1));
        putchar('\n');
    }
    return 0;
}

#10121. 「一本通 4.2 例 3」不同凡響

題意

定義完美序列:一段連續的序列知足序列中的數互不相同。

想知道區間 \([L,R]\) 之間最長的完美序列長度。

思路

\(las[x]\)表示盈利\(x\)最近出現位置。

\(st[i]\)表示以第\(i\)個數結尾的最長完美序列的起始位置。
\[st[i]=max(st[i-1],las[a[i]+1])\]
\(f[i]\)表示以第\(i\)個數結尾的最長完美序列的長度
\[f[i]=i-st[i]+1\]
\(st\)的遞推式可知,\(st\)的值是一個非遞減的序列。

對於一個詢問區間\([l_i,r_i]\),該區間內的\(st\)值可能會有兩種狀況:

  • 左邊一部分的\(st\)值不在區間內,即\(<l_i\)
  • 右邊一部分的\(st\)值不在區間內,即\(\ge l_i\)

因爲\(st\)的值具備單調性,因此這個邊界能夠經過二分獲得。設求出的邊界爲\(mid\)_i,可得:
\[st[l_i...mid_i-1]<l_i\]

\[st[mid_i...r_i]\ge l_i\]

那麼整個區間\([l_i,r_i]\)的最長完美序列的長度能夠分兩部分來求。

  • 左邊:很顯然爲\(mid_i-l_i\)

  • 右邊:\(MAX(m_i...r_i)\)

因此右邊的長度要使用ST表,即RMQ來求。

整個問題的時間複雜度:
\[O((M+N) \times logN)\]

#include<algorithm>
#include<bitset>
#include<complex>
#include<deque>
#include<exception>
#include<fstream>
#include<functional>
#include<iomanip>
#include<ios>
#include<iosfwd>
#include<iostream>
#include<istream>
#include<iterator>
#include<limits>
#include<list>
#include<locale>
#include<map>
#include<memory>
#include<new>
#include<numeric>
#include<ostream>
#include<queue>
#include<set>
#include<sstream>
#include<stack>
#include<stdexcept>
#include<streambuf>
#include<string>
#include<typeinfo>
#include<utility>
#include<valarray>
#include<vector>
#include<cstring>
#include<cmath>
#define ll long long
const int N=2e5+5,M=1e6;
using namespace std;
inline ll read(){
    char ch=getchar();ll res=0,f=1;
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') res=res*10+ch-'0',ch=getchar();
    return res*f;
}
inline void write(ll zx){
    if(zx<0) zx=-zx,putchar('-');
    if(zx<10) putchar(zx+'0');
    else{
        write(zx/10);
        putchar(zx%10+'0');
    }
}
ll n,m,f[N][20],st[N],las[M<<1];
void ST(){
    for(ll j=1;(1<<j)<=n;j++){
        for(ll i=1;i+(1<<j)-1<=n;i++){
            f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
        }
    }
}
ll RMQ(ll l,ll r){
    ll k=0;
    while((1<<(k+1))<=r-l+1) k++;
    return max(f[l][k],f[r-(1<<k)+1][k]);
}
ll find(ll l,ll r){
    if(st[l]==l) return l;
    if(st[r]<l) return r+1;
    int L=l,R=r;
    while(L<=R){
        int m=L+R>>1;
        if(st[m]<l) L=m+1;
        else R=m-1;
    }
    return L;
}
int main(){
    n=read();m=read();
    for(ll i=1;i<=n;i++){
        int x=read();
        st[i]=max(st[i-1],las[x+M]+1);
        f[i][0]=i-st[i]+1;
        las[x+M]=i;
    }
    ST();
    for(ll i=1;i<=m;i++){
        ll L,R;
        L=read();R=read();L++;R++;
        ll mid=find(L,R),ans=0,tmp;
        if(mid>L) ans=mid-L;
        if(mid<=R){
            tmp=RMQ(mid,R);
            ans=max(ans,tmp);
        }
        write(ans);putchar('\n');
    }
    return 0;
}

#10122. 「一本通 4.2 練習 1」天才的記憶

題意

給你一大串數字(編號爲 \(1\)\(N\),大小可不必定哦!),在你看過一遍以後,它便消失在你面前,隨後問題就出現了,給你 \(M\) 個詢問,每次詢問就給你兩個數字 \(A,B\),要求你瞬間就說出屬於 \(A\)\(B\) 這段區間內的最大數。

思路

典型的RMQ模板題啦。

先把一開始的一大串數字塞入RMQ。而後詢問就好啦。沒有一點坑。。。

#include<algorithm>
#include<bitset>
#include<complex>
#include<deque>
#include<exception>
#include<fstream>
#include<functional>
#include<iomanip>
#include<ios>
#include<iosfwd>
#include<iostream>
#include<istream>
#include<iterator>
#include<limits>
#include<list>
#include<locale>
#include<map>
#include<memory>
#include<new>
#include<numeric>
#include<ostream>
#include<queue>
#include<set>
#include<sstream>
#include<stack>
#include<stdexcept>
#include<streambuf>
#include<string>
#include<typeinfo>
#include<utility>
#include<valarray>
#include<vector>
#include<cstring>
#include<cmath>
#define ll long long
const int N=2e5+5,M=1e6;
using namespace std;
inline ll read(){
    char ch=getchar();ll res=0,f=1;
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') res=res*10+ch-'0',ch=getchar();
    return res*f;
}
inline void write(ll zx){
    if(zx<0) zx=-zx,putchar('-');
    if(zx<10) putchar(zx+'0');
    else{
        write(zx/10);
        putchar(zx%10+'0');
    }
}
ll n,m,f[N][20],st[N],las[M<<1];
void ST(){
    for(ll j=1;(1<<j)<=n;j++){
        for(ll i=1;i+(1<<j)-1<=n;i++){
            f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
        }
    }
}
ll RMQ(ll l,ll r){
    ll k=0;
    while((1<<(k+1))<=r-l+1) k++;
    return max(f[l][k],f[r-(1<<k)+1][k]);
}
ll find(ll l,ll r){
    if(st[l]==l) return l;
    if(st[r]<l) return r+1;
    int L=l,R=r;
    while(L<=R){
        int m=L+R>>1;
        if(st[m]<l) L=m+1;
        else R=m-1;
    }
    return L;
}
int main(){
    n=read();
    for(ll i=1;i<=n;i++){
        int x=read();
        f[i][0]=x;
    }
    ST();m=read();
    for(ll i=1;i<=m;i++){
        ll L,R;
        L=read();R=read();int ans=RMQ(L,R);
        write(ans);putchar('\n');
    }
    return 0;
}

#10123. 「一本通 4.2 練習 2」Balanced Lineup

題意

FJ 準備了 \(Q\) 個可能的牛的選擇和全部牛的身高。他想知道每一組裏面最高和最低的牛的身高差異。

思路

典型的RMQ模板題啦。

先把一開始的一大串數字塞入RMQ。而後詢問就好啦。沒有一點坑。。。

注意RMQ2次就行了啊

#include<algorithm>
#include<bitset>
#include<complex>
#include<deque>
#include<exception>
#include<fstream>
#include<functional>
#include<iomanip>
#include<ios>
#include<iosfwd>
#include<iostream>
#include<istream>
#include<iterator>
#include<limits>
#include<list>
#include<locale>
#include<map>
#include<memory>
#include<new>
#include<numeric>
#include<ostream>
#include<queue>
#include<set>
#include<sstream>
#include<stack>
#include<stdexcept>
#include<streambuf>
#include<string>
#include<typeinfo>
#include<utility>
#include<valarray>
#include<vector>
#include<cstring>
#include<cmath>
#define ll long long
const int N=2e5+5,M=1e6;
using namespace std;
inline ll read(){
    char ch=getchar();ll res=0,f=1;
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') res=res*10+ch-'0',ch=getchar();
    return res*f;
}
inline void write(ll zx){
    if(zx<0) zx=-zx,putchar('-');
    if(zx<10) putchar(zx+'0');
    else{
        write(zx/10);
        putchar(zx%10+'0');
    }
}
ll n,m,f[N][20],st[N],las[M<<1],f2[N][20];
void ST(){
    for(ll j=1;(1<<j)<=n;j++){
        for(ll i=1;i+(1<<j)-1<=n;i++){
            f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
        }
    }
}
ll RMQ(ll l,ll r){
    ll k=0;
    while((1<<(k+1))<=r-l+1) k++;
    return max(f[l][k],f[r-(1<<k)+1][k]);
}
void ST2(){
    for(ll j=1;(1<<j)<=n;j++){
        for(ll i=1;i+(1<<j)-1<=n;i++){
            f2[i][j]=min(f2[i][j-1],f2[i+(1<<(j-1))][j-1]);
        }
    }
}
ll RMQ2(ll l,ll r){
    ll k=0;
    while((1<<(k+1))<=r-l+1) k++;
    return min(f2[l][k],f2[r-(1<<k)+1][k]);
}
int main(){
    n=read();m=read();
    for(ll i=1;i<=n;i++){
        int x=read();
        f2[i][0]=f[i][0]=x;
    }
    ST();ST2();
    for(ll i=1;i<=m;i++){
        ll L,R;
        L=read();R=read();int ans=RMQ(L,R)-RMQ2(L,R);
        write(ans);putchar('\n');
    }
    return 0;
}

#2597. 「NOIP2011」選擇客棧

題意

\(n\)個客棧,每一個客棧都配有咖啡館。有兩名旅客想住在同色調的客棧中,又想在兩客棧之間的咖啡館中小聚,咖啡館的價錢不能高於\(p\)

對於 \(100\%\) 的數據,有 \(2\leq n\leq2\times 10^6\)\(0<k\leq10^4\)\(0\leq p\leq100\)\(0\leq\) 最低消費 \(\leq100\)

思路

\(n\)的範圍那麼大,\(k\)的範圍那麼小。那麼暴力吧。

\(h_i\)表示目前顏色\(i\)的客棧數量,\(las_i\)表示最近的顏色爲\(i\)的客棧的編號。

而後\(O(n)\)掃一遍就行了啊。

#include<algorithm>
#include<bitset>
#include<complex>
#include<deque>
#include<exception>
#include<fstream>
#include<functional>
#include<iomanip>
#include<ios>
#include<iosfwd>
#include<iostream>
#include<istream>
#include<iterator>
#include<limits>
#include<list>
#include<locale>
#include<map>
#include<memory>
#include<new>
#include<numeric>
#include<ostream>
#include<queue>
#include<set>
#include<sstream>
#include<stack>
#include<stdexcept>
#include<streambuf>
#include<string>
#include<typeinfo>
#include<utility>
#include<valarray>
#include<vector>
#include<cstring>
#include<cmath>
#define ll long long
const int N=2e5+5,M=1e6;
using namespace std;
inline ll read(){
    char ch=getchar();ll res=0,f=1;
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') res=res*10+ch-'0',ch=getchar();
    return res*f;
}
inline void write(ll zx){
    if(zx<0) zx=-zx,putchar('-');
    if(zx<10) putchar(zx+'0');
    else{
        write(zx/10);
        putchar(zx%10+'0');
    }
}
ll n,m,color,price,now,las[N],h[N],sum[N],ans,p;
int main(){
    n=read();m=read();p=read();
    for(ll i=1;i<=n;i++){
        color=read(),price=read();//讀入
        if(price<=p) now=i;//價錢要小於或等於p
        if(now>=las[color]) sum[color]=h[color];//若是比上一個顏色相同的近,直接加上方案數
        ans+=sum[color];//更新ANS
        h[color]++;las[color]=i;//更新LAS和H
    }
    write(ans);putchar('\n');//輸出
    return 0;
}

#130. 樹狀數組 1 :單點修改,區間查詢

題意

這是一道模板題。

給定Q個操做:

  • 1 i x:給定 \(i,x\),將 \(a[i]\) 加上 \(x\)
  • 2 l r:給定 \(l,r\),求 \(\sum_{i=l}^ra[i]\) 的值(換言之,求 \(a[l]+a[l+1]+\dots+a[r]\) 的值)。

思路

模板題呀。但線段樹能夠過啊。

粘模板了。。。

#include<bits/stdc++.h>
#define N 10000000+10
#define ll long long
using namespace std;
ll n,m,a[N],add[N*4+10];
ll tree[N*4+10];
void pushup(ll id){
    tree[id]=tree[id<<1]+tree[(id<<1)|1];
}
void build(ll id,ll l,ll r){
    if(l==r) tree[id]=a[l];
    else{
        ll mid=l+((r-l)>>1);
        build(id<<1,l,mid);
        build((id<<1)|1,mid+1,r);
        pushup(id);
    }
}
void pushdown(ll id,ll l,ll r){
    if(add[id]!=0){
        add[id<<1]+=add[id];
        add[(id<<1)|1]+=add[id];
        ll mid=l+((r-l)>>1);
        tree[id<<1]+=add[id]*(mid-l+1);
        tree[(id<<1)|1]+=add[id]*(r-mid);
        add[id]=0;
    }
}
void update(ll id,ll l,ll r,ll ql,ll qr,ll val){
    if(ql<=l&&qr>=r){
        add[id]+=val;
        tree[id]+=val*(r-l+1);
        return ;
    }
    pushdown(id,l,r);
    ll mid=l+((r-l)>>1);
    if(ql<=mid) update(id<<1,l,mid,ql,qr,val);
    if(qr>=mid+1) update((id<<1)|1,mid+1,r,ql,qr,val);
    pushup(id);
}
ll query(ll id,ll l,ll r,ll ql,ll qr){
    if(ql<=l&&qr>=r) return tree[id];
    pushdown(id,l,r);
    ll mid=l+((r-l)>>1);
    ll ans=0;
    if(ql<=mid) ans+=query(id<<1,l,mid,ql,qr);
    if(qr>=mid+1) ans+=query((id<<1)|1,mid+1,r,ql,qr);
    return ans;
}
int main(){
    scanf("%lld%lld",&n,&m);
    for(ll i=1;i<=n;i++) scanf("%lld",&a[i]);
    build(1,1,n);
    for(ll i=1;i<=m;i++){
        ll type;
        scanf("%lld",&type);
        if(type==1){
            ll x,y,k;
            scanf("%lld%lld",&y,&k);
            
            update(1,1,n,y,y,k);
        }
        if(type==2){
            ll x,y;
            scanf("%lld%lld",&x,&y);
            ll temp=query(1,1,n,x,y);
            printf("%lld\n",temp);
        }
    }
    return 0;
}

#132. 樹狀數組 3 :區間修改,區間查詢

題意

這是一道模板題。\r\n\r\n給定數列 \(a[1], a[2], \dots, a[n]\),你須要依次進行 \(q\) 個操做,操做有兩類:

  • 1 l r x:給定 \(l,r,x\),對於全部 \(i\in[l,r]\),將 \(a[i]\) 加上 \(x\)(換言之,將 \(a[l], a[l+1], \dots, a[r]\) 分別加上 \(x\));
  • 2 l r:給定 \(l,r\),求 \(\sum_{i=l}^ra[i]\) 的值(換言之,求 \(a[l]+a[l+1]+\dots+a[r]\) 的值)。

思路

模板題呀。但線段樹能夠過啊。

粘模板了。。。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll n,m,a[1000010],add[1000000*4+10];
ll tree[1000000*4+10];
void pushup(ll id){
    tree[id]=tree[id<<1]+tree[(id<<1)|1];
}
void build(ll id,ll l,ll r){
    if(l==r) tree[id]=a[l];
    else{
        ll mid=l+((r-l)>>1);
        build(id<<1,l,mid);
        build((id<<1)|1,mid+1,r);
        pushup(id);
    }
}
void pushdown(ll id,ll l,ll r){
    if(add[id]!=0){
        add[id<<1]+=add[id];
        add[(id<<1)|1]+=add[id];
        ll mid=l+((r-l)>>1);
        tree[id<<1]+=add[id]*(mid-l+1);
        tree[(id<<1)|1]+=add[id]*(r-mid);
        add[id]=0;
    }
}
void update(ll id,ll l,ll r,ll ql,ll qr,ll val){
    if(ql<=l&&qr>=r){
        add[id]+=val;
        tree[id]+=val*(r-l+1);
        return ;
    }
    pushdown(id,l,r);
    ll mid=l+((r-l)>>1);
    if(ql<=mid) update(id<<1,l,mid,ql,qr,val);
    if(qr>=mid+1) update((id<<1)|1,mid+1,r,ql,qr,val);
    pushup(id);
}
ll query(ll id,ll l,ll r,ll ql,ll qr){
    if(ql<=l&&qr>=r) return tree[id];
    pushdown(id,l,r);
    ll mid=l+((r-l)>>1);
    ll ans=0;
    if(ql<=mid) ans+=query(id<<1,l,mid,ql,qr);
    if(qr>=mid+1) ans+=query((id<<1)|1,mid+1,r,ql,qr);
    return ans;
}
int main(){
    scanf("%lld%lld",&n,&m);
    for(ll i=1;i<=n;i++) scanf("%lld",&a[i]);
    build(1,1,n);
    for(ll i=1;i<=m;i++){
        ll type;
        scanf("%lld",&type);
        if(type==1){
            ll x,y,k;
            scanf("%lld%lld%lld",&x,&y,&k);
            update(1,1,n,x,y,k);
        }
        if(type==2){
            ll x,y;
            scanf("%lld%lld",&x,&y);
            ll temp=query(1,1,n,x,y);
            printf("%lld\n",temp);
        }
    }
    return 0;
}

#10127. 「一本通 4.3 練習 1」最大數

題意

給定一個正整數數列 \(a_1, a_2, a_3, \cdots , a_n\),每個數都在 \(0\sim p – 1\) 之間。能夠對這列數進行兩種操做:

  • 添加操做:向序列後添加一個數,序列長度變成 \(n + 1\)

  • 詢問操做:詢問這個序列中最後 \(L\) 個數中最大的數是多少。

程序運行的最開始,整數序列爲空。寫一個程序,讀入操做的序列,並輸出詢問操做的答案。

思路

模板題呀。

#include<bits/stdc++.h>
#define ll long long
#define MAXNUM 200000*4+10
using namespace std;
struct node{
    ll l,r,lef,rig,c;
}tree[MAXNUM];
ll n,m,q,len,las;
void build(ll l,ll r){
    ll root=++len;
    tree[root].l=l;tree[root].r=r;tree[root].lef=tree[root].rig=-1;
    if(l<r){
        ll mid=(l+r)/2;
        tree[root].lef=len+1;build(l,mid);
        tree[root].rig=len+1;build(mid+1,r);
    }
}
void update(ll root,ll x,ll k){
    if(tree[root].l==tree[root].r){tree[root].c=k;return ;}
    ll lef=tree[root].lef,rig=tree[root].rig;
    ll mid=(tree[root].l+tree[root].r)/2;
    if(x<=mid) update(lef,x,k);
    else update(rig,x,k);
    tree[root].c=max(tree[lef].c,tree[rig].c);
}
ll query(ll root,ll l,ll r){
    if(tree[root].l>=l&&tree[root].r<=r) return tree[root].c;
    ll lef=tree[root].lef,rig=tree[root].rig;
    ll mid=(tree[root].l+tree[root].r)/2;
    if(r<=mid) return query(lef,l,r);
    else if(mid<l) return query(rig,l,r);
    else return max(query(lef,l,mid),query(rig,mid+1,r));
}
int main(){
    scanf("%lld%lld",&m,&q);
    build(1,m);
    while(m--){
        char s;cin>>s;
        if(s=='A'){
            ll x;scanf("%lld",&x);n++;
            update(1,n,(x+las)%q);
        }else{
            ll x;scanf("%lld",&x);
            las=query(1,n-x+1,n);
            printf("%lld\n",las);
        }
    }
}

#10128. 「一本通 4.3 練習 2」花神遊歷各國

題意

每一次旅行中,花神會選擇一條旅遊路線,它在那一串國家中是連續的一段,此次旅行帶來的開心值是這些國家的喜歡度的總和,固然花神對這些國家的喜歡程序並非恆定的,有時會忽然對某些國家產生反感,使他對這些國家的喜歡度 \(\delta\) 變爲 \(\sqrt \delta\)(多是花神虐爆了那些國家的 OI,從而感到乏味)。

如今給出花神每次的旅行路線,以及開心度的變化,請求出花神每次旅行的開心值。

思路

爲了使時間複雜度下降,咱們能夠發現:​\(\sqrt 1=1\)因此,最大數字\(10^9\)操做較少次數便能到\(1\)。因此在操做前判斷一下,若是區間內都是\(1\)便可跳過。

#include<bits/stdc++.h>
#define ll long long
#define MAXNUM 1000000*4+10
using namespace std;
struct node{
    ll l,r,lef,rig;
    ll val,Max;
}tree[210000];
ll a[110000],len,n,m;
void build(ll l,ll r){
    ll root=++len;
    tree[root].l=l;tree[root].r=r;tree[root].lef=tree[root].rig=-1;
    if(l==r)tree[root].val=tree[root].Max=a[l];
    else{
        ll mid=(l+r)/2;
        ll lef=tree[root].lef=len+1;build(l,mid);
        ll rig=tree[root].rig=len+1;build(mid+1,r);
        tree[root].val=tree[lef].val+tree[rig].val;
        tree[root].Max=max(tree[lef].Max,tree[rig].Max);
    }
}
void update(ll root,ll l,ll r){
    if(tree[root].Max<2) return ;
    if(tree[root].l==tree[root].r){tree[root].val=sqrt(tree[root].val);tree[root].Max=tree[root].val;return ;}
    ll lef=tree[root].lef,rig=tree[root].rig,mid=(tree[root].l+tree[root].r)/2;
    if(r<=mid) update(lef,l,r);
    else if(mid<l) update(rig,l,r);
    else update(lef,l,mid),update(rig,mid+1,r);
    tree[root].val=tree[lef].val+tree[rig].val;
    tree[root].Max=max(tree[lef].Max,tree[rig].Max);
}
ll query(ll root,ll l,ll r){
    if(tree[root].l>=l&&tree[root].r<=r) return tree[root].val;
    ll lef=tree[root].lef,rig=tree[root].rig,mid=(tree[root].l+tree[root].r)/2;
    if(r<=mid) return query(lef,l,r);
    else if(mid<l) return query(rig,l,r);
    else return query(lef,l,mid)+query(rig,mid+1,r);
}
int main(){
    scanf("%lld",&n);
    for(ll i=1;i<=n;i++) scanf("%lld",&a[i]);
    build(1,n);
    scanf("%lld",&m);
    while(m--){
        char s;cin>>s;
        if(s=='2'){
            ll x,y;scanf("%lld%lld",&x,&y);
            update(1,x,y);
        }else{
            ll x,y;scanf("%lld%lld",&x,&y);
            printf("%lld\n",query(1,x,y));
        }
    }
    return 0;
}

#10129. 「一本通 4.3 練習 3」維護序列

題意

有長爲 \(n\) 的數列,不妨設爲 \(a_1,a_2,\cdots ,a_n\)。有以下三種操做形式:

  • 把數列中的一段數所有乘一個值;
  • 把數列中的一段數所有加一個值;
  • 詢問數列中的一段數的和,因爲答案可能很大,你只需輸出這個數模 \(P\) 的值。

思路

線段樹瞎搞。乘法的優先級要高一些。

注意MOD

#include<bits/stdc++.h>
#define N 10000000+10
#define ll long long
using namespace std;
ll n,m,a[N],add[N*4+10],mod,p,mul[N*4+10];
ll tree[N*4+10];
void pushup(ll id){
    tree[id]=tree[id<<1]+tree[(id<<1)|1];
    tree[id]%=mod;
}
void push_down(ll cur,ll l,ll r,ll mid){
    if(mul[cur]==1&&add[cur]==0) return;
    mul[cur<<1]=mul[cur<<1]*mul[cur]%mod;
    add[cur<<1]=(add[cur<<1]*mul[cur]%mod+add[cur])%mod;
    tree[cur<<1]=(tree[cur<<1]*mul[cur]%mod+add[cur]*(ll)(mid-l+1)%mod)%mod;
    mul[cur<<1|1]=mul[cur<<1|1]*mul[cur]%mod;
    add[cur<<1|1]=(add[cur<<1|1]*mul[cur]%mod+add[cur])%mod;
    tree[cur<<1|1]=(tree[cur<<1|1]*mul[cur]%mod+add[cur]*(ll)(r-mid)%mod)%mod;
    mul[cur]=1;
    add[cur]=0;
    return;
}
void update(ll id,ll l,ll r,ll ql,ll qr,ll val){
    if(ql<=l&&qr>=r){
        add[id]+=val;add[id]%=mod;
        tree[id]=(tree[id]+(ll)(r-l+1)*val%mod)%mod;
        return ;
    }
    push_down(id,l,r,l+r>>1);
    ll mid=l+((r-l)>>1);
    if(ql<=mid) update(id<<1,l,mid,ql,qr,val);
    if(qr>=mid+1) update((id<<1)|1,mid+1,r,ql,qr,val);
    pushup(id);
}
void updatemul(ll cur,ll L,ll R,ll l,ll r,ll x){
    if(L>=l&&R<=r){
        mul[cur]=mul[cur]*(ll)x%mod;
        add[cur]=add[cur]*(ll)x%mod;
        tree[cur]=tree[cur]*(ll)x%mod;
        return;
    }
    ll mid=L+R>>1;
    push_down(cur,L,R,mid);
    if(l<=mid) updatemul(cur<<1,L,mid,l,r,x);
    if(r>mid) updatemul(cur<<1|1,mid+1,R,l,r,x);
    pushup(cur);
}
ll query(ll id,ll L,ll R,ll l,ll r){
    if(L>=l&&R<=r) return tree[id]%mod;
    ll mid=L+R>>1;
    ll ans=0;
    push_down(id,L,R,mid);
    if(l<=mid) ans=(ans+query(id<<1,L,mid,l,r))%mod;
    if(r>mid) ans=(ans+query(id<<1|1,mid+1,R,l,r))%mod;
    pushup(id);
    return ans;
}
void build(ll L,ll R,ll x,ll y,ll cur){
    mul[cur]=1;add[cur]=0;tree[cur]+=y;
    if(L==R) return;
    ll mid=L+R>>1;
    if(x>mid) build(mid+1,R,x,y,cur<<1|1);
    else build(L,mid,x,y,cur<<1);
    pushup(cur);
}
int main(){
    scanf("%lld%lld",&n,&mod);
    for(ll i=1;i<=n;i++) scanf("%lld",&a[i]),build(1,n,i,a[i]%mod,1);
    scanf("%lld",&m);
    for(ll i=1;i<=m;i++){
        ll type;
        scanf("%lld",&type);
        if(type==1){
            ll x,y,k;
            scanf("%lld%lld%lld",&x,&y,&k);
            updatemul(1,1,n,x,y,k);
        }
        if(type==2){
            ll x,y,k;
            scanf("%lld%lld%lld",&x,&y,&k);
            update(1,1,n,x,y,k);
        }
        if(type==3){
            ll x,y;
            scanf("%lld%lld",&x,&y);
            ll temp=query(1,1,n,x,y);
            printf("%lld\n",temp%mod);
        }
    }
    return 0;
}

2018.1.29

#10202. 「一本通 6.2 練習 5」櫻花

題意

求不定方程:
\[ \frac{1}{x}+\frac{1}{y}=\frac{1}{n!} \]
的正整數解 \((x,y)\) 的數目。

思路

\[ \frac{1}{x}+\frac{1}{y}=\frac{1}{n!} \]

\[ \frac{y}{xy}+\frac{x}{xy}=\frac{1}{n!} \]

\[ \frac{x+y}{xy}=\frac{1}{n!} \]

\[ n!\times(x+y)=xy \]

\[ x+y=\frac{xy}{n!} \]

\[ (x-n!)*(y-n!)=(n!)^2 \]

#include<bits/stdc++.h>
using namespace std;
long long f[1000010],v[1000010],tot,ans[1000010],Ans=0;
long long n;
void prime(){
    for(long long i=2;i<=1000000;i++){
        if(!v[i]) v[i]=i,f[++tot]=i;
        for(long long j=1;j<=tot;j++){
            if(f[j]>v[i]||f[j]>1000000/i) break;
            v[i*f[j]]=f[j];
        }
    }
}
int main(){
    scanf("%lld",&n);
    prime();long long tmp=n;
    memset(ans,0,sizeof(ans));Ans=1;
    for(int i=1;f[i]<=n&&i<=tot;i++){
        long long tmp=0;
        for(long long j=f[i];j<=n;j*=f[i]){
            tmp+=n/j;
            tmp%=1000000007;
        }
        Ans*=2*tmp+1;
        Ans%=1000000007;
    }
    printf("%lld\n",Ans);
}

#10147. 「一本通 5.1 例 1」石子合併

題意

\(n\) 堆石子繞圓形操場排放,現要將石子有序地合併成一堆。規定每次只能選相鄰的兩堆合併成新的一堆,並將新的一堆的石子數記作該次合併的得分。

請編寫一個程序,讀入堆數 \(n\) 及每堆的石子數,並進行以下計算:

  1. 選擇一種合併石子的方案,使得作 \(n-1\) 次合併得分總和最大。
  2. 選擇一種合併石子的方案,使得作 \(n-1\) 次合併得分總和最小。

思路

DP水過去。。。

求最大值

\(f[i][j]\)表示區間\([i,j]\)得分的最大值。

很容易能夠想到:
\[f[i][j]=max(f[i][k]+f[k+1][j]+dist(i,j)))\]

\[dist(i,j)=a[i]+a[i+1]+...+a[j-1]+a[j]\]

因此咱們設\(sum[i]=a[1]+a[2]+...+a[i-1]+a[i]\)

可得:
\[sum[i]=sum[i-1]+a[i]\]
那麼:
\[f[i][j]=max(f[i][j],f[i][k]+f[k+1][j]+sum[j]-sum[i-1])\]
可是,仔細讀題,發現是環。。。!

因此咱們將環轉換成鏈,即將\(a\)數組日後\(n\)個單位複製一遍。

最小值

與最大值差很少。改一個符號\(max------>min\)

#include<algorithm>
#include<bitset>
#include<complex>
#include<deque>
#include<exception>
#include<fstream>
#include<functional>
#include<iomanip>
#include<ios>
#include<iosfwd>
#include<iostream>
#include<istream>
#include<iterator>
#include<limits>
#include<list>
#include<locale>
#include<map>
#include<memory>
#include<new>
#include<numeric>
#include<ostream>
#include<queue>
#include<set>
#include<sstream>
#include<stack>
#include<stdexcept>
#include<streambuf>
#include<string>
#include<typeinfo>
#include<utility>
#include<valarray>
#include<vector>
#include<cctype>
#include<cerrno>
#include<cfloat>
#include<ciso646>
#include<climits>
#include<clocale>
#include<cmath>
#include<csetjmp>
#include<csignal>
#include<cstdarg>
#include<cstddef>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#define E 200010
using namespace std;
inline int read(){
    int res=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') res=res*10+ch-'0',ch=getchar();
    return res*f;
}
inline void write(int x){
    if(x<0) putchar('-'),x=-x;
    if(x<10) putchar(x+'0');
    else{
        write(x/10);
        putchar(x%10+'0');
    }
}
int n,f[210][210],a[210],sum[210],ans;
int main(){
    n=read();
    for(int i=1;i<=n;i++) a[i]=read(),a[i+n]=a[i];
    for(int i=1;i<=n*2;i++) sum[i]=sum[i-1]+a[i];
    memset(f,63,sizeof(f));
    for(int i=1;i<=n*2;i++) f[i][i]=0;
    for(int L=2;L<=n;L++){
        for(int i=1;i<=n*2-L+1;i++){
            int j=i+L-1;
            for(int k=i;k<j;k++){
                f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+sum[j]-sum[i-1]);
            }
        }
    }
    ans=2e9;
    for(int i=1;i<=n;i++){
        ans=min(ans,f[i][i+n-1]);
    }
    write(ans);putchar('\n');
    memset(f,0,sizeof(f));
    for(int i=1;i<=n*2;i++) f[i][i]=0;
    for(int L=2;L<=n;L++){
        for(int i=1;i<=n*2-L+1;i++){
            int j=i+L-1;
            for(int k=i;k<j;k++){
                f[i][j]=max(f[i][j],f[i][k]+f[k+1][j]+sum[j]-sum[i-1]);
            }
        }
    }
    ans=0;
    for(int i=1;i<=n;i++){
        ans=max(ans,f[i][i+n-1]);
    }
    write(ans);putchar('\n');
    return 0;
}

#10148. 「一本通 5.1 例 2」能量項鍊

題意

\(n\)顆珠子,每一個珠子都有本身的標記,現將珠子串成項鍊(環),每相鄰的兩顆珠子能夠經過聚合釋放出能量=三顆珠子的標記之積,併合併成一顆更大的珠子,問聚合成一顆珠子後最大釋放的能量。

好比有一串項鍊:\(2-->3-->5-->10\),那麼把第一顆與第四顆珠子合併後產生的能量\(=2\times3\times10\)。那麼這一串項鍊最多可釋放:\((((4\bigotimes1)\bigotimes2)\bigotimes3)=(10\times2\times3)+10\times3\times5+10\times10\times5=710\)

思路

\(f[i][j]\)表示區間\([i,j]\)的珠子合併後產生能量的最大值。
\[f[i][j]=max(f[i][k]+f[k+1][j]+a[i]*a[j+1]*a[k+1])\]
也是把環展開就行了。。。233333333333.............

#include<algorithm>
#include<bitset>
#include<complex>
#include<deque>
#include<exception>
#include<fstream>
#include<functional>
#include<iomanip>
#include<ios>
#include<iosfwd>
#include<iostream>
#include<istream>
#include<iterator>
#include<limits>
#include<list>
#include<locale>
#include<map>
#include<memory>
#include<new>
#include<numeric>
#include<ostream>
#include<queue>
#include<set>
#include<sstream>
#include<stack>
#include<stdexcept>
#include<streambuf>
#include<string>
#include<typeinfo>
#include<utility>
#include<valarray>
#include<vector>
#include<cctype>
#include<cerrno>
#include<cfloat>
#include<ciso646>
#include<climits>
#include<clocale>
#include<cmath>
#include<csetjmp>
#include<csignal>
#include<cstdarg>
#include<cstddef>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#define E 200010
using namespace std;
inline int read(){
    int res=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') res=res*10+ch-'0',ch=getchar();
    return res*f;
}
inline void write(int x){
    if(x<0) putchar('-'),x=-x;
    if(x<10) putchar(x+'0');
    else{
        write(x/10);
        putchar(x%10+'0');
    }
}
//queue<int> q;
//set<int> s;
//priority_queue<int> q1;
//priority_queue<int,vector<int>,greater<int> > q2;
//list<int> l;
//stack<int> s;
int n,f[210][210],a[210],sum[210],ans;
int main(){
    n=read();
    for(int i=1;i<=n;i++) a[i]=read(),a[i+n]=a[i];
    memset(f,0,sizeof(f));
    for(int i=1;i<=n*2;i++) f[i][i]=0;
    for(int L=2;L<=n;L++){
        for(int i=1;i<=n*2-L+1;i++){
            int j=i+L-1;
            for(int k=i;k<=j-1;k++){
                f[i][j]=max(f[i][j],f[i][k]+f[k+1][j]+a[i]*a[j+1]*a[k+1]);
            }
        }
    }
    ans=0;
    for(int i=1;i<=n;i++){
        ans=max(ans,f[i][i+n-1]);
    }
    write(ans);putchar('\n');
    return 0;
}

2019.1.30

#10149. 「一本通 5.1 例 3」凸多邊形的劃分

題意

給定一個具備 \(N\) 個頂點的凸多邊形,將頂點從 \(1\)\(N\) 標號,每一個頂點的權值都是一個正整數。將這個凸多邊形劃分紅 \(N-2\) 個互不相交的三角形,試求這些三角形頂點的權值乘積和至少爲多少。

思路

首先隨便搞一個多邊形:

而後給它順時針每一個頂點表上序號:

而後枚舉\(i,j\),要求:\(i+1<j\),而後給\(i,j\)連一條線,分割出來另外一個多邊形:多邊形23456

而後在\(i,j\)範圍內枚舉\(k\),使得多邊形23456又能夠分割。

分割成以下圖:

\(f[i][j]\)表示把\(i,j\)的多邊形切割成三角形後的權值乘積之和的最小值。

可得:
\[f[i][j]=min\{f[i][k]+f[k][j]+a[i]*a[j]*a[k]\}(0<i<j<k\leq n)\]
初始化:
\[f[i][j]=inf(0<i\leq n,0<j\leq n)\]

\[f[i][i+1]=0(0<i<n)\]

時間複雜度:\(O(n^3)\)

輸出結果:\(f[1][n]\)

固然,這道題範圍特別大:對於 \(100\\%\) 的數據,有 \(N\le 50\),每一個點權值小於 \(10^9\)。三個數相乘最高可達\(10^{27}\),因此須要使用高精度。這裏使用了C++大數類,轉自代號4101

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1000;  
struct bign{  
    int d[maxn], len;  
  
    void clean() { while(len > 1 && !d[len-1]) len--; }  
  
    bign()          { memset(d, 0, sizeof(d)); len = 1; }  
    bign(int num)   { *this = num; }   
    bign(char* num) { *this = num; }  
    bign operator = (const char* num){  
        memset(d, 0, sizeof(d)); len = strlen(num);  
        for(int i = 0; i < len; i++) d[i] = num[len-1-i] - '0';  
        clean();  
        return *this;  
    }  
    bign operator = (int num){  
        char s[20]; sprintf(s, "%d", num);  
        *this = s;  
        return *this;  
    }  
  
    bign operator + (const bign& b){  
        bign c = *this; int i;  
        for (i = 0; i < b.len; i++){  
            c.d[i] += b.d[i];  
            if (c.d[i] > 9) c.d[i]%=10, c.d[i+1]++;  
        }  
        while (c.d[i] > 9) c.d[i++]%=10, c.d[i]++;  
        c.len = max(len, b.len);  
        if (c.d[i] && c.len <= i) c.len = i+1;  
        return c;  
    }  
    bign operator - (const bign& b){  
        bign c = *this; int i;  
        for (i = 0; i < b.len; i++){  
            c.d[i] -= b.d[i];  
            if (c.d[i] < 0) c.d[i]+=10, c.d[i+1]--;  
        }  
        while (c.d[i] < 0) c.d[i++]+=10, c.d[i]--;  
        c.clean();  
        return c;  
    }  
    bign operator * (const bign& b)const{  
        int i, j; bign c; c.len = len + b.len;   
        for(j = 0; j < b.len; j++) for(i = 0; i < len; i++)   
            c.d[i+j] += d[i] * b.d[j];  
        for(i = 0; i < c.len-1; i++)  
            c.d[i+1] += c.d[i]/10, c.d[i] %= 10;  
        c.clean();  
        return c;  
    }  
    bign operator / (const bign& b){  
        int i, j;  
        bign c = *this, a = 0;  
        for (i = len - 1; i >= 0; i--)  
        {  
            a = a*10 + d[i];  
            for (j = 0; j < 10; j++) if (a < b*(j+1)) break;  
            c.d[i] = j;  
            a = a - b*j;  
        }  
        c.clean();  
        return c;  
    }  
    bign operator % (const bign& b){  
        int i, j;  
        bign a = 0;  
        for (i = len - 1; i >= 0; i--)  
        {  
            a = a*10 + d[i];  
            for (j = 0; j < 10; j++) if (a < b*(j+1)) break;  
            a = a - b*j;  
        }  
        return a;  
    }  
    bign operator += (const bign& b){  
        *this = *this + b;  
        return *this;  
    }  
  
    bool operator <(const bign& b) const{  
        if(len != b.len) return len < b.len;  
        for(int i = len-1; i >= 0; i--)  
            if(d[i] != b.d[i]) return d[i] < b.d[i];  
        return false;  
    }  
    bool operator >(const bign& b) const{return b < *this;}  
    bool operator<=(const bign& b) const{return !(b < *this);}  
    bool operator>=(const bign& b) const{return !(*this < b);}  
    bool operator!=(const bign& b) const{return b < *this || *this < b;}  
    bool operator==(const bign& b) const{return !(b < *this) && !(b > *this);}  
  
    string str() const{  
        char s[maxn]={};  
        for(int i = 0; i < len; i++) s[len-1-i] = d[i]+'0';  
        return s;  
    }  
};  
istream& operator >> (istream& in, bign& x){  
    string s;  
    in >> s;  
    x = s.c_str();  
    return in;  
}  
ostream& operator << (ostream& out, const bign& x){  

    out << x.str();  
    return out;  
}
#define ll bign
ll f[55][55],a[55];
int n;
int main(){
    cin>>n;
    for(int i=1;i<=n;i+=1) cin>>a[i];
    memset(f,63,sizeof(f));
    for(int i=1;i<=n;i++) f[i][i+1]=0;
    for(int L=2;L<=n-1;L++){
        for(int i=1;i<=n-L;i++){
            int j=i+L;
            for(int k=i+1;k<=j-1;k++){
                f[i][j]=min(f[i][k]+f[k][j]+a[i]*a[j]*a[k],f[i][j]);
            }
        }
    }
    cout<<f[1][n];putchar('\n');
    return 0;
}
//f[i][j]=min{f[i][k]+f[k][j]+a[i]*a[j]*a[k]}(0<i<k<j<=n)
//f[i][j]=inf
//f[i][i+1]=0;
//end:f[1][n]
//Time:O(n^3)

#10153. 「一本通 5.2 例 1」二叉蘋果樹

題意

有一棵二叉蘋果樹,若是數字有分叉,必定是分兩叉,即沒有隻有一個兒子的節點。這棵樹共 \(N\) 個節點,標號 \(1\)\(N\),樹根編號必定爲 \(1\)

咱們用一根樹枝兩端鏈接的節點編號描述一根樹枝的位置。一棵有四根樹枝的蘋果樹,由於樹枝太多了,須要剪枝。可是一些樹枝上長有蘋果,給定須要保留的樹枝數量,求最多能留住多少蘋果。
tree.png

思路

\(f[i][j]\)表示以i爲根節點,保留j個節點的最大蘋果數量

\[f[i][j]=max{f[l[i]][k]+f[r[i]][j-k-1]+a[i]}(0<=k<=j-1)\]

\[f[i][j]=0(0<i<=n,0<=j<=Q+1)\]

\[f[i][j]=a[i](j!=0,l[i]==0,r[i]==0)\]

\[Answer:f[1][Q+1]\]

#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline ll read(){
    char ch=getchar();ll res=0,f=1;
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') res=res*10+ch-'0',ch=getchar();
    return res*f;
}
inline void write(ll x){
    if(x<0) putchar('-'),x=-x;
    if(x<10) putchar(x+'0');
    else{
        write(x/10);
        putchar(x%10+'0');
    }
}
ll n,Q,f[110][110],a[110],l[110],r[110],mp[110][110];
void MakeTree(int x){
    for(int i=1;i<=n;i++){
        if(mp[x][i]!=-1){
            l[x]=i;a[i]=mp[x][i];
            mp[x][i]=mp[i][x]=-1;
            MakeTree(i);
            break; 
        }
    }//Make Left Son
    for(int i=1;i<=n;i++){
        if(mp[x][i]!=-1){
            r[x]=i;a[i]=mp[x][i];
            mp[x][i]=mp[i][x]=-1;
            MakeTree(i);
            break;
        }
    }//Make Right Son
}
int DP(int x,int j){
    
    if(j==0){f[x][j]=0;return 0;}
    if((!l[x])&&(!r[x])){f[x][j]=a[x];return a[x];}
    if(f[x][j]>0) return f[x][j];
    for(int k=0;k<j;k++) f[x][j]=max(f[x][j],DP(l[x],k)+DP(r[x],j-k-1)+a[x]);
    return f[x][j];
}
int main(){
    n=read();Q=read();Q++;
    memset(mp,-1,sizeof(mp));
    for(int i=1;i<=n-1;i++){
        int x=read(),y=read(),z=read();
        mp[y][x]=mp[x][y]=z;
    }
    MakeTree(1);
    write(DP(1,Q));putchar('\n');
    /*
    cout<<"-----------------------------------"<<endl;
    for(int i=1;i<=n;i++){
        cout<<"Node "<<i<<":\n";
        cout<<"Left Son:"<<l[i]<<" Right Son:"<<r[i]<<endl;
        cout<<"Val:"<<a[i]<<endl;
        cout<<"DP :\n";
        for(int j=0;j<=Q;j++){
            cout<<"Has "<<j<<":"<<f[i][j]<<endl;
        }
        cout<<endl;
    } 
    */
    return 0;
}
//設f[i][j]表示以i爲根節點,保留j個節點的最大蘋果數量
//f[i][j]=max{f[l[i]][k]+f[r[i]][j-k-1]+a[i]}(0<=k<=j-1) 
//f[i][j]=0(0<i<=n,0<=j<=Q+1)
//f[i][j]=a[i](j!=0&&l[i]==0&&r[i]==0)
//Answer:f[1][Q+1]
/*
Sample Input:
5 2
1 3 1
1 4 10
2 3 20
3 5 20
Sample Output:
21
*/

#10154. 「一本通 5.2 例 2」選課

題意

有不少課程,但部分課程有先修課。學生不可能學完大學開設的全部課程,所以必須在入學時選定本身要學的課程。每一個學生可選課程的總數是給定的。請找出一種選課方案使得你能獲得的學分最多,並知足先修課優先的原則。假定課程間不存在時間上的衝突。

思路

瞎搞樹形DP。

發現這是揹包。

仍是分組揹包。(^▽^)

因而就愉快地解決了。

\(f[x][t]\)表示以x爲根節點的子樹中選t門課可以得到的最高學分。(因此說是揹包問題嘛)
\[Answer:f[0][m]\]

#include<algorithm>
#include<bitset>
#include<complex>
#include<deque>
#include<exception>
#include<fstream>
#include<functional>
#include<iomanip>
#include<ios>
#include<iosfwd>
#include<iostream>
#include<istream>
#include<iterator>
#include<limits>
#include<list>
#include<locale>
#include<map>
#include<memory>
#include<new>
#include<numeric>
#include<ostream>
#include<queue>
#include<set>
#include<sstream>
#include<stack>
#include<stdexcept>
#include<streambuf>
#include<string>
#include<typeinfo>
#include<utility>
#include<valarray>
#include<vector>
#include<cstring>
#include<cmath>
#define ll long long 
#define eps 1e-4
using namespace std;
inline int read(){
    int ret=0,f=1;char ch=getchar();
    while (ch<'0'||ch>'9') {if (ch=='-') f=-f;ch=getchar();}
    while (ch>='0'&&ch<='9') ret=ret*10+ch-'0',ch=getchar();
    return ret*f;
}
inline void write(int zx){
    if(zx<0){zx=-zx;putchar('-');}
    if(zx<10) putchar(zx+'0');
    else{
        write(zx/10);
        putchar(zx%10+'0');
    }
}
int n,m,fir[310],nxt[310];
int s[310],w[310],f[310][310];
int DP(int x){
    if(fir[x]==-1) return 0;
    int sum=0;
    for(int i=fir[x];i!=-1;i=nxt[i]){
        int tmp=DP(i);
        sum+=tmp+1;
        for(int j=sum;j>=0;j--)
            for(int k=0;k<=tmp;k++)
                if(j-k-1>=0) f[x][j]=max(f[x][j],f[x][j-k-1]+f[i][k]);
    }
    return sum;
}
int main(){
    n=read();m=read();
    memset(fir,-1,sizeof(fir));
    for(int i=1;i<=n;i++){
        s[i]=read();w[i]=read();
        nxt[i]=fir[s[i]];
        fir[s[i]]=i;
    }
    for(int i=1;i<=n;i++) f[i][0]=w[i];
    f[0][0]=0;
    DP(0);
    write(f[0][m]);
    putchar('\n');
    return 0;
}

#10155. 「一本通 5.2 例 3」數字轉換

題意

若是一個數 \(x\) 的約數和 \(y\) (不包括他自己)比他自己小,那麼 \(x\) 能夠變成 \(y\)\(y\) 也能夠變成 \(x\)。例如 \(4\) 能夠變爲 \(3\)\(1\) 能夠變爲 \(7\)。限定全部數字變換在不超過 \(n\) 的正整數範圍內進行,求不斷進行數字變換且不出現重複數字的最多變換步數。

思路

求樹的最長鏈

\(d1[i]\)爲以\(i\)爲根的子樹中,i到葉子節點距離最大值

\(d2[i]\)爲以\(i\)爲根的子樹中,i的葉子節點距離次大值(除了最大值所在的子樹)

若j爲i的兒子,那麼:

  • \(d1[j]+dis[i][j]>d1[i]\),則\(d2[i]=d1[i];d1[i]=d2[j]=dis[i][j];\)
  • 不然,若\(d1[j]+dis[i][j]>d2[i]\),則$d2[i]=d1[j]+dis[i][j]; $

最後掃描全部節點,最長鏈=max{d1[i]+d2[i]}

#include<algorithm>
#include<bitset>
#include<complex>
#include<deque>
#include<exception>
#include<fstream>
#include<functional>
#include<iomanip>
#include<ios>
#include<iosfwd>
#include<iostream>
#include<istream>
#include<iterator>
#include<limits>
#include<list>
#include<locale>
#include<map>
#include<memory>
#include<new>
#include<numeric>
#include<ostream>
#include<queue>
#include<set>
#include<sstream>
#include<stack>
#include<stdexcept>
#include<streambuf>
#include<string>
#include<typeinfo>
#include<utility>
#include<valarray>
#include<vector>
#include<cstring>
#include<cmath>
#define ll long long 
#define eps 1e-4
using namespace std;
//priority_queue<int,vector<int>,greater<int> > q1;
//priority_queue<int> q2;
//set<int> s;
//list<int> l;
//map<int> mp;
inline int read(){
    int ret=0,f=1;char ch=getchar();
    while (ch<'0'||ch>'9') {if (ch=='-') f=-f;ch=getchar();}
    while (ch>='0'&&ch<='9') ret=ret*10+ch-'0',ch=getchar();
    return ret*f;
}
inline void write(int zx){
    if(zx<0){zx=-zx;putchar('-');}
    if(zx<10) putchar(zx+'0');
    else{
        write(zx/10);
        putchar(zx%10+'0');
    }
}
int sum[500001],n,d1[500001],d2[500001],ans;
void Pri(){
    for(int i=1;i<=n;i++){
        for(int j=2;j<=n/i;j++){
            if(i*j>n) break;
            sum[i*j]+=i;
        }
    }
}
void dp(){
    for(int i=n;i>=1;i--){
        if(sum[i]<i){
            if(d1[i]+1>d1[sum[i]]){
                d2[sum[i]]=d1[sum[i]];
                d1[sum[i]]=d1[i]+1;
            }else if(d1[i]+1>d2[sum[i]]) d2[sum[i]]=d1[i]+1;
        }
    }
}
int main(){
    n=read();
    Pri();
    dp();
    for(int i=1;i<=n;i++) ans=max(ans,d1[i]+d2[i]);
    write(ans);putchar('\n');
    return 0;
}
相關文章
相關標籤/搜索