1. 程式人生 > >長沙理工集訓隊-9.11日組隊賽

長沙理工集訓隊-9.11日組隊賽

好久沒寫部落格了,寫篇部落格放鬆一下。

外網OJ:http://csustacm.com:4803/

1題我就不寫了這題寫了也沒啥意義。

2.黃金礦工

Description

 

遊戲中有n個寶石,每個寶石有一個價值vi,每次挖出這個寶石需要時間ti。因為有些寶石被另外一個寶石擋住了(兩個寶石在同一直線上),一個寶石最多擋住一個寶石,一個寶石最多被一個寶石擋住。要先撿起擋路的寶石,才能撿起該寶石。每個寶石的擋路寶石為fi,如果沒有擋路寶石fi = 0,即它自己(題目保證沒有環,且不存在)。

遊戲的時間限制是t秒,在t秒內你獲得最大價值和是多少?

 

Input

 

第一行一個整數T,表示接下來有T組資料。(T <= 50)

每組資料格式如下:

第一行兩個整數n(1<=n<=200),t(1<=t<=100,000,000)

接下來n行,每行三個整數vi(1<=vi<=50),ti(1<=ti<=1000,000),(0<=fi<=n)

 

Output

 

輸出獲得的最大價值和

 

Sample Input 1 

1
5 10
2 1 0
5 3 1
3 2 0
1 4 3
4 6 4

Sample Output 1

11

題意:挖寶石,挖某個寶石前可能有一個寶石,一個寶石也只能阻難一個寶石,挖某個寶石要消耗時間ti獲得價值vi,問T=t秒最多可以挖寶石的最大價值。

題解:看了下資料範圍,肯定是以價值DP,求價值的最小時間,如果時間小於所給定的時間就可以挖到相應價值。

首先處理下,每個寶石前後最多隻有一個,肯定是一條鏈,把每條鏈處理一下(假如一條鏈是1->2->3,那麼這條鏈上就有3個節點分別儲存2個值,挖到1,(v1,t1)挖到2,(v1+v2,t1+t2)挖到3,(v1+v2+v3,t1+t2+t3))。最多隻有200條鏈,所以不用擔心超時。

每條鏈儲存 兩個值,價值和所需要的時間。然後在DP就行了。因為每條鏈上只能選一個值所以DP肯定要開二維。

#include<bits/stdc++.h>
#define fi first
#define se second
using namespace std;
#define debug(x) cout<<"["<<x<<"]"<<endl;

typedef long long LL;
typedef unsigned long long uLL;
typedef pair<int, int> P;
const int maxn=1e3+7;
const int inf=0x3f3f3f3f;

int n, m, tot;
int son[maxn];
int in[maxn], v[maxn], t[maxn], vis[maxn];
vector<P> ar[maxn];
int dp[205][10005];
void dfs(int u,int val,int tim) {  //用DFS,一條鏈
    vis[u] = 1;
    ar[tot].push_back(make_pair(val+v[u],tim+t[u]));
    if(son[u] == 0)return;
    dfs(son[u],val+v[u],tim+t[u]);
}
int main() {
    int tim;
    scanf("%d", &tim);
    while(tim--) {
        scanf("%d%d", &n, &m);
        int sum = 0;
        for(int i = 0; i <= n; ++i) {
            son[i]=vis[i]=0;
            ar[i].clear();
        }
        for(int i = 1, u; i <= n; ++i) {
            scanf("%d%d%d", &v[i], &t[i], &u);
            sum += v[i];
            //if(u == 0)continue;
            son[u] = i;
        }
        tot = 0;
        for(int i = 1; i <= n; ++i) {
            if(vis[i] == 0) {//如果這個節點沒有父親,就說明這有一條鏈。
                dfs(i, 0, 0);
                tot++;
            }
        }
        memset(dp, 0x3f, sizeof(dp));        
        for(int i = 0; i < tot; ++i) {    。
            ar[i].push_back(make_pair(0, 0));//每個鏈肯定可以一個都不挖,所以0 ,0要加進去。
            sort(ar[i].begin(),ar[i].end());
        }
        int sz = ar[0].size();
        for(int i = 0; i < sz; ++i) {//dp初始化
            dp[0][ar[0][i].fi] = ar[0][i].se;
        }
        for(int i = 1; i < tot; ++i) {
            sz = ar[i].size();
            for(int j = 0; j < sz; ++j) {
                for(int k = sum; k >= ar[i][j].fi; --k) {
                    dp[i][k] = min(dp[i-1][k-ar[i][j].fi]+ar[i][j].se,dp[i][k]);
                }
            }
        }
        int ans = 0;
        for(int i = sum; i >= 0; --i) {
            if(dp[tot-1][i]<=m) {  //找第一個小於等於給定時間的價值
                ans = i;
                break;
            }
        }
        printf("%d\n", ans);
    }
    return 0;
}

3.精靈王國

Description

 

       小J離開了神祕群島之後,來到了繁華的精靈王國。

       精靈王國中有n個城市,現在已知第 i 個城市和第 i + 1個城市之間有一條長度為d[i]的雙向道路。(特別的,第n個城市和第1個城市之間有一條長度為d[n]的雙向道路)。

        隨著經濟的發展,精靈王國的城市之間建立了m條地鐵,第i條地鐵可以從城市u[i]前往v[i],也可以從v[i]前往u[i],同時地鐵的長度為w[i]。

        現在小J在各個城市之間旅遊,小J想知道從城市x前往城市y旅遊需要花費多長的時間?

Input

 

第一行為2個整數n、m。

第二行為n個正整數d[i]。

接下來m行每行三個正整數u[i]、v[i]、w[i]。

第m+3行為一個正整數Q,表示詢問次數。

接下來Q行每行兩個正整數x、y,表示一次從城市x到城市y的旅行。

資料範圍:1<=n,q<=1e5,1<=m<=30,1<=u[i],v[i],x,y<=n,1<=d[i],w[i]<=1e9;

Output

 

輸出Q行每行一個正整數表示該次旅行的最短時間。

Sample Input 1 

4 1
1 2 3 6
1 3 2
5
1 2
1 4
1 3
2 3
4 3

Sample Output 1

1
5
2
2
3

看起來挺難的,其實是到原題。。,把資料範圍改了一下,見牛客第二場挑戰賽。

看起來很難,實際上簡單的一匹,只有30條鐵路,直接把有鐵路的60個點直接全部跑一次最短路,然後問兩個點之間的最短距離,要麼坐了地鐵,那麼就是到60個點中的一個最短路加上從這個有鐵路的點到另一個點的最短路,要麼就是不做地鐵,不做地鐵一個字首和就行了。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef unsigned long long uLL;
typedef pair<int,int> P;

#define bug printf("*********\n");
#define debug(x) cout<<"["<<x<<"]" <<endl;
#define mem(a,b) memset(a,b,sizeof(a));

const long long mod=1e9+7;
const int maxn=1e5+5;
const int inf=0x3f3f3f3f;

int n,m;
int d[maxn];
LL dp[maxn];
vector<int> v;
bool u[maxn];
struct edge {
    int to,next;
    LL w;
} eg[maxn*3];
int tot,head[maxn];
void add(int u,int v,int w) {
    eg[tot].to=v;
    eg[tot].w=w;
    eg[tot].next=head[u];
    head[u]=tot++;
}
LL dis[65][maxn];
int main() {
    scanf("%d%d",&n,&m);
    mem(head,-1);
    for(int i =  1; i <= n ; i ++) {
        scanf("%d",&d[i]);
        dp[i]=dp[i-1]+d[i];
        if(i==n) {
            add(1,n,d[i]);
            add(n,1,d[i]);
        } else {
            add(i,i+1,d[i]);
            add(i+1,i,d[i]);
        }
    }
    while(m--) {
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        if(u[a]==0) {
            v.push_back(a);
            u[a]=1;
        }
        if(u[b]==0) {
            v.push_back(b);
            u[b]=1;
        }
        add(a,b,c);
        add(b,a,c);
    }
    mem(dis,inf);
    priority_queue<P,vector<P>,greater<P> > q;
    for(int i = 0 ; i < v.size(); i ++) {
        dis[i][v[i]]=0;
        q.push(P(0,v[i]));
        while(q.size()) {
            int u = q.top().second;
            q.pop();
            for(int j = head[u]; j!=-1; j=eg[j].next) {
                edge &e=eg[j];
                if(dis[i][e.to]>dis[i][u]+e.w) {
                    dis[i][e.to]=dis[i][u]+e.w;
                    q.push(P(dis[i][e.to],e.to));
                }
            }
        }
    }
    int Q;
    scanf("%d",&Q);
    while(Q--) {
        int a,b;
        scanf("%d%d",&a,&b);
        if(a>b)swap(a,b);
        LL ans=min(dp[b-1]-dp[a-1],dp[n]-(dp[b-1]-dp[a-1]));
        for(int i =0 ; i <v.size(); i++) {
//                    debug(dis[i][a]+dis[i][b]);
            ans=min(ans,dis[i][a]+dis[i][b]);
        }
        printf("%lld\n",ans);
    }

    return 0;
}

5.zzq的數學教室2

Description

 

zzq想保研,他的成績單上有一排非遞減順序的成績,面試時老師想知道他數學成績的位置,zzq知道他的數學成績是x分,他要找到第一個出現x的位置。

他想運用二分查詢演算法, 程式碼如下:

image.png

顯然L就是最終的位置。

可是現在他的成績全被lcy學姐打亂了(隨機把數字亂放)。

他想知道最後找到的位置仍然是原來的位置的概率, 請你幫幫他。

概率是在模1e9 + 7意義下的, 即 p / q = p * inv(q) 。inv(q)是q在模1e9 + 7 意義下的逆元。

Input

 

輸入第一行一個正整數N。

第二行N個正整數a[i],代表的是原來的成績單,呈非遞減順序。

第三行一個數字x,代表他的數學成績。

1 <= N <= 1e5

1 <= a[i] <= 1e9

x保證是某一個a[i]。

Output

 

輸出一個整數代表概率。

Sample Input 1 

8
1 1 1 3 7 9 9 10
1

Sample Output 1

1

Sample Input 2 

3
1 2 2
2

Sample Output 2

333333336

Hint

對於第二個樣例,lcy學姐可能打亂成這3種等概率的情況:

1 2 2

2 1 2

2 2 1

其中只有第一種會結果正確。

概率是1 / 3。

題解:水題,直接把小於x的個數,a,和大於x的個數算出來b,然後照這個二分寫法一路把答案算下去就行了;具體看程式碼。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef unsigned long long uLL;
typedef pair<int,int> P;

#define bug printf("*********\n");
#define debug(x) cout<<"["<<x<<"]" <<endl;
#define mem(a,b) memset(a,b,sizeof(a));

const long long mod=1e9+7;
const int maxn=1e5+5;
const int inf=0x3f3f3f3f;

int l,r;
LL pow(LL x,LL n) {
    LL ans=1;
    while(n) {
        if(n&1)ans=ans*x%mod;
        x=x*x%mod;
        n>>=1;
    }
    return ans;
}
int n,x;
int a[maxn];
int main() {
    scanf("%d",&n);
    for(int i = 1 ; i <= n ; i ++) {
        scanf("%d",&a[i]);
    }
    scanf("%d",&x);
    LL mx=0,mi=0;
    for(int i = 1; i<=n; i++) {
        if(a[i]>=x) {
            mx++;//大於等於x的個數
        } else {
            mi++;//小於等於x的個數
        }
    }
    int l=1,r=n;
    LL ans=1;
    while(l<=r) {
        int mid=(l+r)/2;
        if(a[mid]>=x) {
            ans=ans*mx%mod*pow(mi+mx,mod-2)%mod;//需要一個選一個大於等於x的數,選到的概率是(mx/(mi+mx));
            mx--;
            r=mid-1;
        } else {
            ans=ans*mi%mod*pow(mi+mx,mod-2)%mod;//同上。
            mi--;
            l=mid+1;
        }
    }
    printf("%lld\n",ans);
    return 0;
}

 

6.zzq的數學教室

Description

 

眾所周知,摸魚是qwb的一大愛好。即使是在zzq的數學課上,qwb也是在瘋狂摸魚。這被眼尖的zzq發現了,所以zzq決定考考摸魚的qwb,如果qwb答不出來,他的平時分自然就歸零了。

現在zzq把數字1~n從左至右排成一排(第i個數的值為i),接下來進行m輪操作,每次操作描述如下:將奇數位置的數字取出形成序列A,將偶數位置的數字取出形成序列B,將A序列拼接在B序列之後,構成新的序列。

現在問題來了:進行m次操作後,第k個位置的數字是多少呢?

Input

 

第一行,輸入2個正整數n,q

接下來q行,每行2個整數m和k,表示zzq想知道在m次操作之後第k個位置上的數是多少。

資料範圍:

n<=5000

q<=1e6

m<=1e6

k<=n;

Output

 

輸出q行,每行輸出第k個位置的數字。

Sample Input 1 

5 2
1 2
2 3

Sample Output 1

4
2

水題

#include<bits/stdc++.h>
using namespace std;
#define debug(x) cout<<"["<<x<<"]"<<endl;

typedef long long LL;
typedef unsigned long long uLL;
typedef pair<int, int> P;
const int maxn=5e3+7;
const int inf=0x3f3f3f3f;

int n,q;
int ar[maxn], br[maxn];
int ans[maxn][maxn];
int main(){
    while(~scanf("%d%d", &n, &q)){
        for(int i = 1; i <= n; ++i){
            ar[i] = ans[0][i] = i;
        }
        int tot, tim = 1;
        do{
            tot = 1;
            for(int i = 2; i <= n; i += 2){
                ans[tim][tot] = ans[tim-1][i];
                //printf("%d ", ans[tim][tot]);
                tot++;
            }
            for(int i = 1; i <= n; i += 2){
                ans[tim][tot] = ans[tim-1][i];
                //printf("%d ", ans[tim][tot]);
                tot++;
            }
            int flag = 0;
            for(int i = 1; i <= n; ++i){
                if(ans[tim][i] != ar[i])flag = 1;
            }
            if(flag == 0)break;
            tim ++;
        }while(1);
        //printf("*%d\n",tim);
        int m, k;
        while(q--){
            scanf("%d%d", &m, &k);
            m %= tim;
            if(m == 0)m = tim;
            printf("%d\n", ans[m][k]);
        }
    }
    return 0;
}

 

7.玩遊戲

Description

 

dr喜歡玩遊戲,現在有n個遊戲,每個遊戲時間為[Li,Ri),現在問題是,找出最長的一段遊戲時間,使得該時間段被至少k個遊戲完全覆蓋(這k個區間要每一個都要完全覆蓋你選出來的這個區間)。

Input

 

多組輸入

第一行n,k(1<=n,k<1e6)

接下來n行,每行兩個數l,r(1<=l<r<=1e9)

Output

 

輸出這個區間的長度

Sample Input 1 

3 2 
1 5 
1 4 
1 3

Sample Output 1

3

貪心就好,每次從最先結束的一個線段開始選,然後找最小的k小於當前線段結束點的起點,然後滿足條件的區間就是當前區間的終點減去k個起始點中最大值。找完這個線段的終點後把這個區間刪掉,然後依次類推下去知道找完所有的線段。本來每次找k個小於當前線段的結束點起始點需要一個操作,但是因為資料有點水,被我水過去了。。。

#include<bits/stdc++.h>
using namespace std;
#define debug(x) cout<<"["<<x<<"]"<<endl;

typedef long long LL;
typedef unsigned long long uLL;
typedef pair<int, int> P;
const int maxn=1e6+7;
const int inf=0x3f3f3f3f;
struct th {
    int st,en,id;
    bool operator <(const th a)const {
        if(en==a.en) {
            return st<a.st;
        } else return en<a.en;
    }
} a[maxn];
bool u[maxn];
priority_queue<P,vector<P>,greater<P> >q;
int main() {
    int n,m;
    while(~scanf("%d%d",&n,&m)) {
        for(int i=0; i<n; i++) {
            scanf("%d%d",&a[i].st,&a[i].en);
            a[i].id=i;
            q.push(P(a[i].st,i));
        }
        sort(a,a+n);
        int cnt=0,ans=0,L=0;
        memset(u,0,sizeof(u));
        for(int i =0; i<n; i++) {
            if(!u[a[i].id])cnt++;
            u[a[i].id]=1;
            L=a[i].st;    //本來這是要找最大值的,但是資料有點水,直接就過去了。。。
            while(cnt<m&&q.size()) {
                if(u[q.top().second]==1) {
                    q.pop();
                    continue;
                } else if(q.top().first>=a[i].en) {
                    break;
                } else {
                    cnt++;
                    u[q.top().second]=1;
                    L=max(L,q.top().first);
                    q.pop();
                }
            }
            if(cnt==m) {
                ans=max(ans,a[i].en-L);
            }
            cnt--;
        }
        while(q.size())q.pop();
        printf("%d\n",ans);
    }
    return 0;
}

9.簽到題

Description

 

“素數就是因子只包含1和它本身的數”zzq如是說道。

現在zzq的數學課下課了,他發現qwb在他的課摸魚,於是要出一個題考qwb:N!的素因子有多少個?

如果qwb做不出來就要被py交易!但是qwb完全不知道zzq上課講了什麼,於是向從來不摸魚的你求助了(劃重點:這是簡單題)。

Input

 

第一行輸入一個整數T(T \leq 10T≤10),表示有T組資料。

每組資料輸入站一行,輸入一個整數N(N \leq 10^5N≤105)

Output

 

對於每組資料,輸出N!有多少個素因子

Sample Input 1 

2
1
4

Sample Output 1

0
4

如題目。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef unsigned long long uLL;

const int maxn=1e5+7;
const int inf=0x3f3f3f3f;

int prim[maxn], p[maxn], pcnt;
int sum[maxn];
int main() {
    int t;
    prim[0]=1;
    prim[1]=1;
    pcnt = 0;
    for(int i =2; i < maxn; i++) {
        if(!prim[i]) p[pcnt++] = i;
        for(int j = 0; j < pcnt&&i*p[j]<maxn; ++j) {
            prim[i*p[j]] = 1;
            if(i%p[j] == 0)break;
        }
    }
    sum[0] = 0;
    sum[1] = 0;
    sum[2] = 1;
    for(int i = 3; i < maxn; ++i) {
        int tmp = i,cnt = 0;
        if(prim[i] == 0) {
            sum[i]=sum[i-1]+1;
            continue;
        }
        for(int j = 0; j < pcnt; ++j) {
            while(tmp % p[j] == 0) {
                tmp /= p[j];
                cnt++;
            }
            if(tmp == 1)break;
        }
        if(tmp!=1)cnt++;
        sum[i] = sum[i-1] + cnt;
    }
    int n;
    scanf("%d",&t);
    while(t--) {
        scanf("%d", &n);
        printf("%d\n", sum[n]);
    }
    return 0;
}