9.2模擬賽
cogs 比賽名稱 樹立信心的模擬賽
T1 2739. 凱倫和咖啡
時間限制:1 s 內存限制:512 MB
【題目描述】
為了在上課時保持清醒,凱倫需要一些咖啡。咖啡愛好者凱倫想知道最佳的溫度來沖煮完美的咖啡。因此,她花了一些時間閱讀幾本食譜,其中包括廣受好評的“咖啡的藝術”。
她知道有n個食譜,其中第i個食譜建議應當在li和ri度之間沖煮以達到最佳的味道。凱倫認為如果至少k個食譜推薦某個溫度,那麽那個溫度是可以接受的。
凱倫的性格比較多變,因此她會問q個問題,對於每一個問題,她會給出一個溫度區間[a,b],你要告訴她有多少可接受的整數溫度在這個範圍內。
必須要放上圖片...=n=
【輸入格式】
第一行輸入包含三個整數,n,k(1≤k≤n≤200000)和q(1≤q≤200000),如題中所描述。
接下來n行描述每一個食譜,具體來說,其中的第i行包含兩個整數li和ri(1≤li≤ri≤200000),描述第i個食譜建議咖啡在li和ri度之間進行沖煮(包括端值)。
接下來q行為q個詢問。這些行中的每一行都包含a和b,(1≤a≤b≤200000),表示她想知道a和b度之間的可接受的整數溫度的數量,包括a和b。
【輸出格式】
對於每個詢問,一行輸出一個答案。
【樣例輸入】
3 2 4
91 94
92 97
97 99
92 94
93 97
95 96
90 100
【樣例輸出】
3
3
0
4
【提示】
數據進行了更新,卡掉了部分暴力程序。
題目大意 :
有n個線段覆蓋,q個詢問,問區間[a,b]之間有多少個點被大於等於k的線段覆蓋著。
題解:
差分+樹狀數組維護前綴和。
暴力的想法是,每一個線段覆蓋的區間都打標記,即 for(i=x--y)f[i]++;
最後在查詢時看看區間裏 有多少個點的標記是>=k的。時間復雜度O(n^2+nq)
慢的原因是每個點挨個打標記太浪費時間了,所以當[x,y]被線段覆蓋時,只需打
兩個標記a[x]++,a[y+1]--,這樣處理a數組的前綴和,sum[i]就是i這個點被覆蓋的
線段個數,我們將被覆蓋的線段個數大於等於k放入樹狀數組求值就可以了。
1A,時間0.314s排名第三 耶=u=
代碼
#include<iostream> #include<cstdio> using namespace std; int n,k,q,x,y,qx,qy,maxn; int a[200002],sum[200002],tree[200002]; int lowbit(int x){ return x&(-x); } void add(int x){ while(x<=maxn+10){ tree[x]++; x+=lowbit(x); } } int query(int a,int b){ int suma=0,sumb=0;a--; while(a){ suma+=tree[a]; a-=lowbit(a); } while(b){ sumb+=tree[b]; b-=lowbit(b); } return sumb-suma; } int main(){ freopen("coffee.in","r",stdin); freopen("coffee.out","w",stdout); scanf("%d%d%d",&n,&k,&q); //n個食譜,至少k個,q次詢問。 for(int i=1;i<=n;i++){ scanf("%d%d",&x,&y); a[x]++;a[y+1]--; maxn=max(maxn,max(x,y)); } for(int i=1;i<=maxn;i++){ sum[i]=sum[i-1]+a[i]; if(sum[i]>=k)add(i); } for(int i=1;i<=q;i++){ scanf("%d%d",&qx,&qy); printf("%d\n",query(qx,qy)); } fclose(stdin); fclose(stdout); return 0; }
T2 2739. 凱倫和咖啡
★★ 輸入文件:games.in
輸出文件:games.out
簡單對比
時間限制:2 s 內存限制:512 MB
【題目描述】
在她上學的路上,她沈迷於一款益智遊戲無法自拔。
遊戲是這樣的:在每一關,你都有一個n行m列的網格。每個單元最初為數字0。每一步你可以選擇一行或一列,並將該行或列中的所有單元格添加1。為了通關,你需要在所有的移動之後使第i行第j列的數字為g[i][j]。凱倫想知道一種使用最少步數通關的方法。
【輸入格式】
第一行輸入包含兩個整數,分別為n和m(1≤n,m≤100),表示網格中的行數和列數。
接下來的n行每行都包含m個整數。第i行的第j個整數表示g[i][j](0≤g[i][j]≤500)。
【輸出格式】
如果無法通關,輸出-1。
否則,第一行輸出一個整數k表示最小步數
接下來k行包含以下兩項,用來描述一步操作
row x,(1<=x<=n)表示選第x行。
col x,(1<=x<=m)表示選第x列。
如果有多個答案,輸出字典序最小的一個。(就是先輸出行,按行號從小到大,再輸出列,按列號從小到大)
【樣例輸入1】
3 5
2 2 2 3 2
0 0 0 1 0
1 1 1 2 1
【樣例輸出1】
4
row 1
row 1
row 3
col 4
【樣例輸入2】
3 3
0 0 0
0 1 0
0 0 0
【樣例輸出2】
-1
【樣例輸入3】
3 3
1 1 1
1 1 1
1 1 1
【樣例輸出3】
3
row 1
row 2
row 3
【來源】
題目大意:要求一個都是0的矩陣,每一步可以把一行或者是一列都+1,求多少步可以變成目標矩陣。
題解:
80分貪心。啊啊我知道為什麽錯了...因為要最小步數 所以要判斷行和列誰大,萬一這個矩陣又高又瘦呢...
先消除行不就不劃算了麽....
每一列或者是行都加1太麻煩了,所以我們的問題轉換成目標矩陣每一步能把一行或者是一列-1,求幾步
能變成都是0的矩陣。
貪心的做法是,統計每一行和每一列的最小值。如果某一行或者是某一列的的最小值為0,那麽就不能一次性
將這一行或一列都-1。因為要求字典序最小,我們先看行,每行減去它的非零最小值,非0最小值是這一行的
最小值不能為0,更新列的最小值,然後在減去每一列的非零最小值,判斷是否矩陣的值都為0;
代碼:
80貪心。AC代碼
#include<iostream> #include<cstring> #include<cstdio> using namespace std; int n,m,k; int minrow[520],mincol[520],a[520][520]; struct A { int id,s,js;//id表示行(1)還是列(0),s表示哪一個。 } ans[255000]; bool check() { for(int i=1; i<=n; i++) for(int j=1; j<=m; j++) if(a[i][j])return false; return true; } int main() { freopen("games.in","r",stdin); freopen("games.out","w",stdout); scanf("%d%d",&n,&m); //n行 m列 memset(minrow,127/3,sizeof(minrow)); memset(mincol,127/3,sizeof(mincol)); for(int i=1; i<=n; i++) { for(int j=1; j<=m; j++) { scanf("%d",&a[i][j]); minrow[i]=min(minrow[i],a[i][j]);//第i行最小的數 mincol[j]=min(mincol[j],a[i][j]);//第j列最小的數 } } if(m>=n) { for(int i=1; i<=n; i++) { if(minrow[i]==0)continue; for(int j=1; j<=m; j++) { a[i][j]-=minrow[i]; mincol[j]=min(mincol[j],a[i][j]); } ans[++k].id=1; ans[k].s=i; ans[k].js=minrow[i]; minrow[i]=0; } for(int i=1; i<=m; i++) { if(mincol[i]==0)continue; for(int j=1; j<=n; j++) { a[j][i]-=mincol[i]; } ans[++k].id=0; ans[k].s=i; ans[k].js=mincol[i]; mincol[i]=0; } } else { for(int i=1; i<=m; i++) { if(mincol[i]==0)continue; for(int j=1; j<=n; j++) { a[j][i]-=mincol[i]; minrow[j]=min(minrow[j],a[j][i]); } ans[++k].id=0; ans[k].s=i; ans[k].js=mincol[i]; mincol[i]=0; } for(int i=1; i<=n; i++) { if(minrow[i]==0)continue; for(int j=1; j<=m; j++) { a[i][j]-=minrow[i]; mincol[j]=min(mincol[j],a[i][j]); } ans[++k].id=1; ans[k].s=i; ans[k].js=minrow[i]; minrow[i]=0; } } if(check()) { int p=0; for(int i=1; i<=k; i++)p+=ans[i].js; printf("%d\n",p); for(int i=1; i<=k; i++) { if(ans[i].id==1) { for(int j=1; j<=ans[i].js; j++) printf("row %d\n",ans[i].s); } else { for(int j=1; j<=ans[i].js; j++) printf("col %d\n",ans[i].s); } } } else printf("-1"); fclose(stdin); fclose(stdout); return 0; }
T3 2747. 凱倫和超市
★★★ 輸入文件:market.in
輸出文件:market.out
簡單對比
時間限制:2 s 內存限制:512 MB
【題目描述】
在回家的路上,凱倫決定停在超市買些雜貨。
她需要買很多商品,但由於她是學生,她的預算還是相當有限的。其實她只能花最高b美元。
超市裏有n種商品,第i種商品價格為c[i]美元。當然,每種商品只能買一次。最近,超市一直在努力增加業務,作為忠實客戶的凱倫,獲得了n張優惠券,如果凱倫買了第i件商品,她可以用第i張優惠券來降低d[i]的價格。當然,如果不買相應的商品就無法使用優惠券。
然而,優惠券有一定限制:對於所有i≥2,為了使用第i張優惠券,凱倫也必須使用第xi張優惠券(這可能意味著使用更多的優惠券來滿足該優惠券的要求)。凱倫想知道,用她的錢最多可以買多少商品。
【輸入格式】
第一行輸入包含兩個整數n和b(1≤n≤5000,1≤b≤10^9),商店中的貨物數量和凱倫的錢數。
接下來的n行描述以下項:
其中第i行開始是兩個整數分別為次c[i]和d[i](1<=d[i]<c[i]<=10^9),表示第i個商品的價錢和用第i張優惠券買這個商品可以減少的價錢,如果i>=2,接下來是另一個整數x[i](1<=x[i]<i),表示第x[i]張優惠券應該先被使用。
【輸出格式】
輸出一行,一個整數,即凱倫能買到的最大的商品數。
【樣例1】
input
6 16
10 9
10 5 1
12 2 1
20 18 3
10 2 3
2 1 5
output
4
【樣例2】
input
5 10
3 1
3 1 1
3 1 2
3 1 3
3 1 4
output
5
【提示】
在此鍵入。
【來源】
在此鍵入。
題目大意:n個商品 b個錢,每個商品都有優惠券可以減少一定的價錢,但是除了第一個商品,使用優惠券必須要滿足使用第xi優惠券。
每種物品只能買一次...
題解:tan 90
一開始以為是有依賴性背包問題...想想不是...
有覺得是拓撲+dp...推不出來。
還是dfs好了,0分懵比。ORZ
代碼:
懵比0分dfs
#include<iostream> #include<cstdio> using namespace std; int n,b,pre,ans,sumedge,head[5005],c[5005],d[5005]; struct Edge{ int x,y,nxt; Edge(int x=0,int y=0,int nxt=0): x(x),y(y),nxt(nxt){} }edge[5019]; void add(int x,int y){ edge[++sumedge]=Edge(x,y,head[x]); head[x]=sumedge; } void dfs(int x,int use,int buy,int sum,int all){ ans=max(ans,sum); if(all>b)return; if(x==0){ dfs(x+1,1,1,sum+1,all+c[1]-d[1]); dfs(x+1,0,1,sum+1,all+c[1]); dfs(x+1,-1,0,sum,all); } if(use==1){ for(int i=head[x];i;i=edge[i].nxt){ int v=edge[i].y; dfs(v,1,1,sum+1,all+c[v]-d[v]); dfs(v,0,1,sum+1,all+c[v]); dfs(v,-1,0,sum,all); } }else if(use==0){ for(int i=head[x];i;i=edge[i].nxt){ int v=edge[i].y; dfs(v,-1,0,sum,all); dfs(v,0,1,sum+1,all+c[v]); } }else if(use==-1){ for(int i=head[x];i;i=edge[i].nxt){ int v=edge[i].y; dfs(v,-1,0,sum,all); dfs(v,0,1,sum+1,all+c[v]); } } return ; } int main(){ freopen("market.in","r",stdin); freopen("market.out","w",stdout); scanf("%d%d",&n,&b); //貨物的數量 凱倫的錢數。 scanf("%d%d",&c[1],&d[1]); //第1個商品的價錢和第一個商品用優惠券減少的價錢。 for(int i=2;i<=n;i++){ scanf("%d%d%d",&c[i],&d[i],&pre); add(pre,i); } dfs(0,0,0,0,0); //買了幾個物品,這個物品用沒用優惠券 //買沒買 買個幾個物品 花了多少錢。 printf("%d\n",ans); fclose(stdin); fclose(stdout); return 0; }
正解是樹形dp...因為我還沒做過...所以我先做幾道題再回來補這個坑.=u=
9.2模擬賽