長沙理工集訓隊-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的位置。
他想運用二分查詢演算法, 程式碼如下:
顯然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;
}