1. 程式人生 > 實用技巧 >【清北學堂週末刷題班】 Day3

【清北學堂週末刷題班】 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;
}

總結

還好的是這一次沒有爆零,畢竟我經常爆零,希望以後少爆零甚至不爆零,而且只是寫暴力還是可以拿到很多的分數的,因此遇到困難的時候千萬不要放棄,而且要對於比較長的題目分析一下樣例之類的,在紙上多畫畫,多寫一寫,這樣就能做對題目了,下一次奧利給!