【清北學堂週末刷題班】 Day3
【清北學堂週末刷題班】 Day3
50分 A 30分 B20分
A
【問題描述】
你是能看到第一題的friends呢。
——hja
眾所周知,小蔥同學擅長計算,尤其擅長計算組合數,但這個題和組合數沒什麼關係。
給定一個只包含乘號、加號和數字的表示式,你需要替換其中一個位置的符號將表示式變為另一個表示式(可以替換為同樣的字元,但前導零是不允許的),求所有合法情況得出的表示式的值的和。
【輸入格式】
一行一個字串代表表示式。
【輸出格式】
一行一個整數代表答案。
【樣例輸入】
233
【樣例輸出】
9633
【樣例解釋】
不合法的串僅包括033,∗33,23∗,23+,+33
【資料規模與約定】
對於40%的資料,字串長度不超過3。
對於80%的資料,字串長度不超過10。
對於100%的資料,字串長度不超過100。
思路
我考試的時候大概是想的是列舉,但是列舉資料量比較大,而且程式碼不太好實現,比較浪費時間,所以我只寫了一個40%的規模,不超過3的字串,但是隻考慮了字元數量等於三的情況,白給了10分,剩下的我還沒有聽回放,先鴿一下
程式碼
30分
#include <iostream> #include <cstdio> #include <algorithm> #include <cstring> #include <string> using namespace std; char a,b,c; long long ans; int main() { cin>>a>>b>>c; for(int i=1;i<=9;i++) ans+=i*100+(b-'0')*10+(c-'0'); for(int i=0;i<=9;i++) ans+=(a-'0')*100+i*10+(c-'0'); for(int i=0;i<=9;i++) ans+=(a-'0')*100+(b-'0')*10+i; ans+=(a-'0')+(c-'0'); ans+=(a-'0')*(c-'0'); cout<<ans<<endl; return 0; }
100分(等待填坑)
B
【問題描述】
你是能看到第二題的friends呢。
——aoao
眾所周知,小蔥同學擅長計算,尤其擅長計算組合數,但這個題和組合數沒什麼關係。
現在有\(N\)個任務,完成第i個任務需要\(t_i\)的時間。你只有一臺機器,一臺機器只能完成一個任務,完成了之後就會報廢;但是你有小魔仙啦啦棒,你可以使用M的時間將一臺機器變為兩臺機器,不同機器可以同時做任務,但一個任務只能由一臺機器來做,問完成所有任務所需要的最少時間?注意小魔仙啦啦棒有無數根,即如果你有多臺機器,你可以同時對這多臺機器使用魔法。不能對正在工作的機器使用魔法,也不能讓正在施法的機器進行工作。如果一臺機器已經報廢,也可以對其使用魔法,但會得到一臺報廢的機器和一臺沒報廢的機器。在一些機器工作的時候你可以對其他沒有工作的機器使用魔法。
【輸入格式】
第一行兩個整數\(N,M\)。
第二行\(N\)個整數代表\(t_i\)。
【輸出格式】
一行一個整數代表答案。
【樣例輸入】
2 2
1 2
【樣例輸出】
4
【資料規模與約定】
對於30%的資料,\(N\leq10\)
對於60%的資料,\(N\leq 20\)
對於另外20%的資料,所有\(t_i\)相同。
對於100%的資料,\(1\le N \leq 200, 1\le M,t_i \le 1000\)
思路
最開始讀完題目,仔細一想,發現了題目是這樣的:有N個任務,1個機器,可以對所有機器複製,複製機會無限,並且每次複製耗時為M,並且使用完的機器會報廢掉。我就想到了:對於N個任務,要複製到使機器的數量\(2^x\ge N\),求一個最小的x,然後複製時間為\(M*x\),接著一起執行任務,總的任務的執行時間為\(M*x+max(t_i)\)這是他的最終答案,但是這個貪心真的有問題,真的有問題,因為在複製的時候有一些情況會多出一些機器,而複製的那些時間明顯同時可以執行任務,所以這樣貪心一定是錯誤的,目標得分20分。
然後今天下午,聽老師講了,有兩種思路,第一種思路為動態規劃,另一種思路類似於合併果子進行貪心
動態規劃
通過分析,這個實際上是棵二叉樹,然後一通分析猛如虎,老師講完,我還在原地杵,然而我並沒有看懂,所以我只記住(不確定)了一個類狀態轉移方程
\[f[l][r]=max_{l\leq k \leq r}(f[l][k]+f[k+1][r]+M) \]貪心
類似於合併果子,用priority_queue 優先佇列,我有時間實現一下下
程式碼
20分垃圾程式碼
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string>
using namespace std;
long long n,m;
long long t[205];
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>t[i];
long long x=0;
for(int i=1;;i++)
{
if((1<<i)>=n)
{
x=i;
break;
}
}
long long maxn=-999;
for(int i=1;i<=n;i++)
maxn=max(t[i],maxn);
cout<<(long long)(x*m+maxn)<<endl;
return 0;
}
100分(std)再過個5,6天自己寫一下
//動態規劃
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=210;
int n,m,z[maxn],f[maxn][maxn];
int main()
{
freopen("in","r",stdin);
freopen("out","w",stdout);
scanf("%d%d",&n,&m);
for (int a=1;a<=n;a++)
scanf("%d",&z[a]);
sort(z+1,z+n+1);
memset(f,0x3f,sizeof(f));
for (int a=1;a<=n;a++)
f[a][a]=z[a];
for (int len=2;len<=n;len++)
for (int l=1,r=len;r<=n;l++,r++)
for (int k=l;k<r;k++)
f[l][r] = min(f[l][r], max(f[l][k],f[k+1][r])+m);
printf("%d\n",f[1][n]);
return 0;
}
//貪心
C
【問題描述】
你是能看到第三題的friends呢。
——laekov
眾所周知,小蔥同學擅長計算,尤其擅長計算組合數,但這個題和組合數沒什麼關係。
現在你有一個大小為M的資料單元,一開始M個位置上面的數都為0。現在你會不斷得到一個新的數,設這個數為v,那麼我們有p的概率保留這個數,如果保留這個數,則其會等概率替換掉M個位置中的任意一個;如果選擇不保留這個數,則會直接扔掉這個數。現在給你N個數,問如果我們從第p1個數訪問到p2這個過程結束後,我們最後得到的資料單元裡面的數的和的期望是多少。
【輸入格式】
第一行三個整數N,M,K代表數的個數、資料單元大小和操作次數。
接下來NN行每行三個數v,a,b,分別代表數的值和其被保留的概率為\(\frac ab\)。
接下來K行,每行第一個整數opt代表操作型別。如果opt=1代表一組詢問操作,接下來會給出p1,p2;如果opt=2,代表一組修改操作,接下來給定p,v,a,b,代表第p個位置需要被修改為值為v,概率為\(\frac ab\)。如果opt=3代表另一種修改操作,接下來給定l,r,v,代表將第l個數到第r個數的值全部增加v,概率不變。
【輸出格式】
對於每次詢問操作,輸出一行一個整數代表答案對\(10^9+7\)取模之後的結果。
【樣例輸入】
5 2 5
1 1 2
2 2 3
3 3 4
4 4 5
5 5 6
1 1 5
1 5 1
2 3 3 3 5
1 1 5
1 5 1
【樣例輸出】
735416679
656250009
751666679
515000008
【資料規模與約定】
對於20%的資料,\(N,K\leq 1000\)
對於另外20%的資料,\(M=1\)
對於另外20%的資料,\(opt=1\)。
對於另外20%的資料,\(opt\neq 3\)。
對於100%的資料,\(1\leq M \leq N,K\leq 10^5 ,0\leq a \leq b \leq 10^9 ,1\leq v \leq 10^9\)
思路
然而並沒有聽懂(到時候再回放一下)
程式碼
100分 (std)
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cctype>
using namespace std;
const int BUF_SIZE = 30;
char buf[BUF_SIZE], *buf_s = buf, *buf_t = buf + 1;
#define PTR_NEXT() \
{ \
buf_s ++; \
if (buf_s == buf_t) \
{ \
buf_s = buf; \
buf_t = buf + fread(buf, 1, BUF_SIZE, stdin); \
} \
}
#define readint(_n_) \
{ \
while (*buf_s != '-' && !isdigit(*buf_s)) \
PTR_NEXT(); \
bool register _nega_ = false; \
if (*buf_s == '-') \
{ \
_nega_ = true; \
PTR_NEXT(); \
} \
int register _x_ = 0; \
while (isdigit(*buf_s)) \
{ \
_x_ = _x_ * 10 + *buf_s - '0'; \
PTR_NEXT(); \
} \
if (_nega_) \
_x_ = -_x_; \
(_n_) = (_x_); \
}
#define wmt 1,n,1
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const int maxn=100010;
const int mo=1000000007;
int n,m,k,invm;
int mul(int a,int b)
{
int ans=1;
while (b)
{
if (b&1) ans=1ll*ans*a%mo;
a=1ll*a*a%mo;
b>>=1;
}
return ans;
}
struct node
{
int l_to_r,r_to_l;
int prob_decay;
int col;
int lr_prob,rl_prob;
void init(int v,int a,int b)
{
int p = 1ll*a*mul(b,mo-2)%mo;
l_to_r = r_to_l = 1ll*v*p%mo;
prob_decay = (1-1ll*p*invm%mo+mo)%mo;
col = 0;
lr_prob = rl_prob = p;
}
}z[maxn<<2];
node operator+(const node &l,const node &r)
{
node p;
p.l_to_r = ( 1ll * l.l_to_r * r.prob_decay + r.l_to_r) % mo;
p.r_to_l = ( 1ll * r.r_to_l * l.prob_decay + l.r_to_l) % mo;
p.prob_decay = 1ll*l.prob_decay*r.prob_decay % mo;
p.col = 0;
p.lr_prob = (1ll * l.lr_prob * r.prob_decay + r.lr_prob)%mo;
p.rl_prob = (1ll * r.rl_prob * l.prob_decay + l.rl_prob)%mo;
return p;
}
node color(node p,int v)
{
p.col = (p.col + v)%mo;
p.l_to_r = (p.l_to_r + 1ll * p.lr_prob * v)%mo;
p.r_to_l = (p.r_to_l + 1ll * p.rl_prob * v)%mo;
return p;
}
void update(int rt)
{
z[rt] = z[rt<<1] + z[rt<<1|1];
}
void push_col(int rt)
{
z[rt<<1] = color(z[rt<<1],z[rt].col);
z[rt<<1|1] = color(z[rt<<1|1],z[rt].col);
z[rt].col=0;
}
void build(int l,int r,int rt)
{
if (l==r)
{
int v,a,b;
readint(v);
readint(a);
readint(b);
z[rt].init(v,a,b);
return;
}
int m=(l+r)>>1;
build(lson);
build(rson);
update(rt);
}
void modify(int l,int r,int rt,int p)
{
if (l==r)
{
int a,b,v;
readint(v);
readint(a);
readint(b);
z[rt].init(v,a,b);
return;
}
push_col(rt);
int m=(l+r)>>1;
if (p<=m) modify(lson,p);
else modify(rson,p);
update(rt);
}
void modify(int l,int r,int rt,int nowl,int nowr,int v)
{
if (nowl<=l && r<=nowr)
{
z[rt] = color(z[rt],v);
return;
}
push_col(rt);
int m=(l+r)>>1;
if (nowl<=m) modify(lson,nowl,nowr,v);
if (m<nowr) modify(rson,nowl,nowr,v);
update(rt);
}
node query(int l,int r,int rt,int nowl,int nowr)
{
if (nowl<=l && r<=nowr) return z[rt];
int m=(l+r)>>1;
push_col(rt);
if (nowl<=m)
{
if (m<nowr) return query(lson,nowl,nowr)+query(rson,nowl,nowr);
else return query(lson,nowl,nowr);
}
else return query(rson,nowl,nowr);
}
int main()
{
readint(n);
readint(m);
readint(k);
invm = mul(m,mo-2);
build(wmt);
for (int a=1;a<=k;a++)
{
int opt;
readint(opt);
if (opt==1)
{
int p1,p2;
readint(p1);
readint(p2);
if (p1<=p2) printf("%d\n",query(wmt,p1,p2).l_to_r);
else printf("%d\n",query(wmt,p2,p1).r_to_l);
}
else if (opt==2)
{
int p;
readint(p);
modify(wmt,p);
}
else
{
int l,r,v;
readint(l);
readint(r);
readint(v);
modify(wmt,l,r,v);
}
}
return 0;
}
D
【問題描述】
你是能看到第四題的friends呢。
——laekov
眾所周知,小蔥同學擅長計算,尤其擅長計算組合數,但這個題和組合數沒什麼關係。
你需要從\((0,0)\)出發走到\((x,y)\),你只能走到整點,並且你每次移動距離的平方不能超過\(d\),問你最少需要走多少步走到\((x,y)\)。
【輸入格式】
若干行,每行三個整數\(x,y,d\)
【輸出格式】
對於每組資料,輸出一行代表答案。
【樣例輸入】
233 233 1
-1 3 9
【樣例輸出】
466
2
【資料範圍與規定】
對於30%的資料,\(|x|,|y|\leq 100\)
對於60%的資料, \(|x|,|y| \leq 1000\)
對於100%的資料,\(|x|,|y|\leq 10^9, 1 \leq d \le 10^9\)測試資料不超過100組。
思路
這道題我看到之後是想了一下算曼哈頓距離,但是很快發現不對,然後想了一下算一個歐幾里得距離,然後看是否能到整點,但是由於時間問題,也沒有寫出來,通過老師的講解,應該是平面凸包問題(實際上也可以不用平面凸包來做),然後再一個就是如果多畫畫圖的話還是可以做出來的,所以以後做的時候,一定要拿筆,一定要拿筆!!!,em具體思路我還是沒有太理解,看看回放吧。
程式碼
100分(std)
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<vector>
#include<cmath>
using namespace std;
const int maxn=100010;
struct point
{
long long x,y;
point(){}
point(long long a,long long b){x=a;y=b;}
}p[maxn];
point operator+(const point &p1,const point &p2)
{
return point(p1.x+p2.x,p1.y+p2.y);
}
point operator-(const point &p1,const point &p2)
{
return point(p1.x-p2.x,p1.y-p2.y);
}
long long operator*(const point &p1,const point &p2)
{
return p1.x*p2.y-p1.y*p2.x;
}
int sgn(long long a)
{
if (a==0) return 0;
if (a>0) return 1;
return -1;
}
class worker {
public:
vector <int> work(vector <int> x, vector <int> y, vector <int> d) {
vector <int> res;
for (int t=0;t<x.size();t++)
{
point q=point(abs(x[t]),abs(y[t]));
int td=d[t];
if (q.x==0 && q.y==0)
{
res.push_back(0);
continue;
}
if (q.x==0 || q.y==0)
{
res.push_back((q.x+q.y-1)/(int(sqrt(td)))+1);
continue;
}
int n=0;
for (int a=0;a*a<=td;a++)
{
int b=sqrt(td-a*a);
if (a>b) break;
p[++n] = point(a,b);
while (n>2 && (p[n]-p[n-2])*(p[n-1]-p[n-2]) < 0)
{
n--;
p[n]=p[n+1];
}
}
for (int a=n+1;a<=n+n;a++)
p[a] = point(p[n+n+1-a].y,p[n+n+1-a].x);
n<<=1;
int x;
for (int a=1;a<n;a++)
if (sgn(q*p[a])*sgn(q*p[a+1])<=0)
{
x=a;
break;
}
long long l=0;
long long r=2;
if (p[x].y) r+=q.y/p[x].y;
if (p[x+1].x) r+=q.x/p[x+1].x;
while (l+1!=r)
{
long long m=(l+r)>>1;
point p1=p[x];
point p2=p[x+1];
p1.x*=m;p1.y*=m;
p2.x*=m;p2.y*=m;
if ((q-p1)*(p2-p1) >=0) r=m;
else l=m;
}
res.push_back(int(r));
}
return res;
}
};
int main()
{
worker obj;
vector<int> x;
vector<int> y;
vector<int> d;
int a,b,c;
while (~scanf("%d%d%d",&a,&b,&c))
{
x.push_back(a);
y.push_back(b);
d.push_back(c);
}
vector<int> res=obj.work(x,y,d);
for (int a=0;a<res.size();a++)
printf("%d\n",res[a]);
return 0;
}
總結
還好的是這一次沒有爆零,畢竟我經常爆零,希望以後少爆零甚至不爆零,而且只是寫暴力還是可以拿到很多的分數的,因此遇到困難的時候千萬不要放棄,而且要對於比較長的題目分析一下樣例之類的,在紙上多畫畫,多寫一寫,這樣就能做對題目了,下一次奧利給!