1. 程式人生 > 實用技巧 >涼心模擬總結

涼心模擬總結

還是自己太菜了,Day1並沒有發現這套題需要寫讀入優化,所以說第一天很涼。

Day 1

\(\color{blue} \text {餐館(restaurant)}\)

\(\color{blue} \text {【題目描述】}\)

銅企鵝是企鵝餐館的老闆,他正在計劃如何使得自己本年度收益增加。

共有 n 種食材,一份食材 i 需要花 ti 小時不間斷地進行播種,施肥,直至收穫。當然,一份食材 i 是可以直接賣掉得到 wi 塊錢的。

招牌菜共有 m 種,一份招牌菜 i 需要消耗一定的食材,花 Ti 小時不間斷地來烹飪,叫賣,並最終賣出得到 Wi 塊錢。

整個季度換算下來一共有 Tmax 小時可供你使用,銅企鵝需要在這期間賺到最多的錢,這樣他才有足夠多的錢來 steam 剁手,或者氪金手遊。

\(\color{blue} \text {【輸入】}\)

第一行一個整數 T,表示資料組數。

令 i 表示為當前資料內行數。

第一行三個整數 n,m,Tmax,含義如題所示。

第二行至第 n+1 行,每行兩個整數 ti−1, wi−1,含義如題所示。

第 n+2 行至第 n+m+1 行,每行兩個整數 Ti−n−1, Wi−n−2,含義如題所示。

第 n+m+2 行至第 n+2m+1 行,每行 n 個整數,第 j 個數 dj 表示招牌菜 i−n−m−1 需要 dj 個食材 j。

\(\color{blue} \text {【輸出】}\)

對於每組資料,輸出一行一個整數,表示你所能賺到的最多的錢。

\(\color{blue} \text {【輸入樣例】}\)

3
1 1 48
2 2000
9 21864 5
4 4 46
17 52
4 36
5 43
16 62
9 31659
1 20431
4 623
1 11961
4 5 3 5
5 4 3 4
3 3 3 3
4 4 5 5
10 0 48
10 41
18 48
2 14
22 65
12 77
7 48
4 85
2 61
24 85
8 34

\(\color{blue} \text {【輸出樣例】}\)

53728
410
1464

\(\color{blue} \text {【提示】}\)

資料範圍

對於 100% 的資料,保證 0<ti,Ti≤Tmax≤5000,0≤wi,Wi≤109,每份招牌菜使用的食材的個數總數不超過 105。

\(\color{blue} \text {【解題思路】}\)

這題認真分析一下就可以發現其實是一個完全揹包,然後你就照著完全揹包打就可以把這道題打出來了!可是注意卡常!一定要寫讀入優化!

AC code:

#include<bits/stdc++.h>
#define LL long long
#define N 2010
using namespace std;
LL n,m,T,lim;
LL tim1[N*3],w1[N*3],bag[N*3],tim2[N];
inline char gc() {
  static char buf[1 << 18], *fs, *ft;
  return (fs == ft && (ft = (fs = buf) + fread(buf, 1, 1 << 18, stdin)), fs == ft) ? EOF : *fs++;
}
inline void read(LL &aa) {
  register int k = 0, f = 1;
  register char c = gc();
  for (; !isdigit(c); c = gc()) if (c == '-') f = -1;
  for (; isdigit(c); c = gc()) k = (k << 3) + (k << 1) + (c - '0');
  aa=k * f;
}
void check1();
void init()
{
	memset(bag,0,sizeof(bag));
//	scanf("%lld%lld%lld",&n,&m,&lim);
	read(n);read(m);read(lim);
	for(int i=1;i<=n;++i) read(tim1[i]),read(w1[i]);//scanf("%lld%lld",&tim1[i],&w1[i]);
	if(m==0)
	{
		check1();
	}
	else
	{
		for(int i=1;i<=m;++i) read(tim2[i]),read(w1[i+n]);//scanf("%lld%lld",&tim2[i],&w1[i+n]);
		LL a;
		for(int i=1;i<=m;++i) 
		{
			tim1[i+n]=tim2[i];
			for(int j=1;j<=n;++j)
			read(a)/*scanf("%lld",&a)*/,tim1[i+n]+=a*tim1[j];
		}
		check1();
	}
}
int main()
{
	//freopen("restaurant.in","r",stdin);freopen("restaurant.out","w",stdout);
	read(T);
	for(int i=1;i<=T;++i) init();
	//fclose(stdin);fclose(stdout);
	return 0;
}
void check1()
{
	for(int i=1;i<=n+m;++i)
	{
		for(int j=0;j<=lim;++j)
		{
			if((j==0||bag[j])&&j+tim1[i]<=lim)
			bag[j+tim1[i]]=max(bag[j+tim1[i]],bag[j]+w1[i]);
		}
	}
	LL maxx=0;
	for(int i=1;i<=lim;++i) maxx=max(maxx,bag[i]);printf("%lld\n",maxx);
}
/* 
1
1 1 48
2 2000
9 21864
5
*/

\(\color{blue} \text {烯烴 (olefin)}\)

\(\color{blue} \text {【題目描述】}\)

銀企鵝非常擅長化學。有一天他在試圖命名一個巨大的單烯烴分子的時候,想到了一個問題。

給你一棵樹,一些邊有標記,對於每條有標記的邊,在樹中找到包含這條邊的一條最長鏈,並輸出長度。

\(\color{blue} \text {【輸入】}\)

第一行一個整數 id 表示測試點的編號。

多組資料,第二行一個整數 T 表示資料組數。

對於每組資料,第一行兩個整數 n, m 表示節點的個數,和被標記的邊的個數。

我們規定 1 是根,第二行 n−1 個整數給出 2 ∼ n 父親的編號,保證fai<i。

第三行 m 個整數範圍在 [2, n] 表示哪個點的父邊被標記過。

\(\color{blue} \text {【輸出】}\)

對於每組資料輸出一行 m 個整數,必須與輸入的邊順序一致,給出的是在這條邊必選的情況下樹中最長鏈的長度。

\(\color{blue} \text {【輸入樣例】}\)

0
1
10 3
1 2 3 1 4 6 7 3 8
10 7 9

\(\color{blue} \text {【輸出樣例】}\)

8 8 6

\(\color{blue} \text {【提示】}\)

1e5

\(\color{blue} \text {【解題思路】}\)

這道題一看就是一個非常明顯的一個樹上DP,只要我們能夠注意到一個細節就是,節點編號從小到大的順序就是從根節點到葉節點的順序就可以了,這樣可以節省一些程式碼,我們正序跑就是從根跑到葉節點,反過來就是從葉節點跑到根節點。

d1記錄從葉節點到當前節點的最長距離,d2記錄次長距離,u記錄可行的第二長的路徑,然後我們這樣思考完了以後這道題相應的也就做出來了。

為什麼我們要記錄可行的第二長路徑,是因為我們有可能最長路徑會和第二長路徑部分重合,然後求出來的最長路就不滿足要求!

AC code:

/*
    Name: olefin
    Copyright: lv
    Author:Mudrobot
    Date: 2018/10/10 17:03:44
    Description: tree dp
*/
#include<bits/stdc++.h>
#define gc() getchar()//caution!!!
#define N 100005
using namespace std;
/*inline char gc() {
  static char buf[1<<18],*fs,*ft;
  return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<18,stdin)),fs==ft)?EOF:*fs++;
}*/
inline void read(int &aa) {
  register int k=0,f=1;
  register char c=gc();
  for (;!isdigit(c);c=gc()) if(c=='-')f=-1;
  for (;isdigit(c);c=gc()) k=(k<<3)+(k<<1)+(c-'0');
  aa=k * f;
}
inline void out(int x){if(x>9)out(x/10);putchar(x%10+'0');}
int id,T,n,m,fa[N],d1[N],d2[N],u[N],x;
inline void clean()
{
	memset(fa,0,sizeof(fa));memset(d1,0,sizeof(d1));
	memset(d2,0,sizeof(d2));memset(u,0,sizeof(u));
}
int main()
{
    //freopen(".in", "r", stdin);freopen(".out", "w", stdout);
	read(id);read(T);
	while(T--)
	{
		clean();read(n);read(m);
		for(int i=2;i<=n;++i) read(fa[i]);
		for(int i=n;i>=1;--i) 
		if(d1[i]+1>=d1[fa[i]]) d2[fa[i]]=d1[fa[i]],d1[fa[i]]=d1[i]+1;
		else if(d1[i]+1>d2[fa[i]])d2[fa[i]]=d1[i]+1;
		for(int i=2;i<=n;++i)
		if(d1[fa[i]]==d1[i]+1) u[i]=max(u[fa[i]]+1,d2[fa[i]]+1);
		else u[i]=max(u[fa[i]]+1,d1[fa[i]]+1);
		for(int i=1;i<=m;++i) {read(x);out(d1[x]+u[x]);if(i!=m)printf(" ");}
		if(m!=0)printf("\n");
	}
	//fclose(stdin);fclose(stdout);
	return 0;
}
/*

*/

T3好像有一點超綱,所以這裡並沒有做。

Day 2

\(\color{blue} \text {鍛造}\)

\(\color{blue} \text {【題目描述】}\)

勇者雖然武力值很高,但在經歷了多次戰鬥後,發現怪物越來越難打,

於是開始思考是不是自己平時鍛鍊沒到位,於是苦練一個月後發現……自

己連一個史萊姆都打不過了。

勇者的精靈路由器告訴勇者其實是他自己的武器不好,並把他指引到

了鍛造廠。

“歡迎啊,老朋友。”

一陣寒暄過後,廠長帶他們參觀了廠子四周,並給他們講鍛造的流程。

“我們這裡的武器分成若干的等級,等級越高武器就越厲害,並且對每

一等級的武器都有兩種屬性值 b 和 c,但是我們初始只能花 a 個金幣來生

產 1 把 0 級劍……”

“所以你們廠子怎麼這麼垃圾啊,不能一下子就造出來 999 級的武器

嗎?”勇者不耐煩的打斷了廠長的話。

“彆著急,還沒開始講鍛造呢……那我們舉例你手中有一把 x 級武器和

一把 y 級武器 (y = max(x − 1, 0)),我們令鍛造附加值 k = min(cx, by),則

你有 k

cx 的概率將兩把武器融合成一把 x + 1 級的武器。”

“……但是,鍛造不是一帆風順的,你同樣有 1 −

k

cx 的概率將兩把武器

融合成一把 max(x − 1, 0) 級的武器……”

勇者聽完後暗暗思忖,他知道廠長一定又想借此機會坑騙他的零花錢,

於是求助這個村最聰明的智者——你,來告訴他,想要強化出一把 n 級的

武器,其期望花費為多少?

由於勇者不精通高精度小數,所以你只需要將答案對 998244353(7 ×

17 × 2

23 + 1,一個質數 ) 取模即可。

\(\color{blue} \text {【輸入】}\)

第一行兩個整數 n, a,含義如題所示。

為了避免輸入量過大,第二行五個整數 bx, by, cx, cy, p,按照下列程式碼

來生成 b 和 c 陣列。

b[0]=by+1;c[0]=cy+1;

for(int i=1;i

b[i]=((long long)b[i-1]*bx+by)%p+1;

c[i]=((long long)c[i-1]*cx+cy)%p+1;

}

\(\color{blue} \text {【輸出】}\)

輸出一行一個整數,表示期望花費。

\(\color{blue} \text {【輸入樣例】}\)

10 2
2 33 6 66 2333333

\(\color{blue} \text {【輸出樣例】}\)

976750710

\(\color{blue} \text {【提示】}\)

\(\color{blue} \text {【解題思路】}\)

摘自TTL

【解題思路】
不難得出這道題需要dp;
但怎麼避免後效性?我們發現對於一次失敗的操作,會產生一個\(i-1\)級的武器,是不是相當於\(i-1\)的武器有無數個,那麼期望花費則只與\(i\)有關,轉移方程就是\(f[i]=(\frac{1}{p})f[i-1]+f[i-2]\),p表示\(i-1\)製造成功的概率,以為有\(\frac{1}{p}\)所以我們需要求逆元,線性遞推逆元;

#include<bits/stdc++.h>
using namespace std;
int dp[10000010],ny[10000010];
int b[10000010],c[10000010];
const int mod=998244353;
int n,a,bx,by,cx,cy,p;
void init() { 
	ny[0]=ny[1]=1;
	for(register int i=2;i<=10000000;++i) { 
		ny[i]=(long long)ny[mod%i]*(mod-(mod/i))%mod;
	} 
	return;
} 

void make() { 
	b[0]=by+1;c[0]=cy+1;
	for(int i=1;i<n;i++) { 
		b[i]=((long long)b[i-1]*bx+by)%p+1;
		c[i]=((long long)c[i-1]*cx+cy)%p+1;
	} 
	return;
} 

int main() { 
	cin>>n>>a;
	cin>>bx>>by>>cx>>cy>>p;
	init();
	make();
	dp[0]=a;
	//dp[1]=1ll*(1ll*c[0]*ny[min(b[0],c[0])]%mod+1)*dp[0]%mod;
	register int k;
	for(int i=1;i<=n;++i) { 
		k=min(c[i-1],b[max(0,i-2)]);
		dp[i]=(1ll*(1ll*(1ll*c[i-1]*ny[k]%mod)*dp[i-1]%mod)+dp[max(i-2,0)])%mod;
	} 
	cout<<dp[n];
	return 0;
} 

\(\color{blue} \text {整除}\)

\(\color{blue} \text {【題目描述】}\)

整除符號為 |,d|n 在計算機語言中可被描述為 n%d == 0。

現有一算式 n|x

m − x,給定 n,m,求 [1, n] 以內 x 解的個數。

解可能很大,輸出取模 998244353。

\(\color{blue} \text {【輸入】}\)

其中 n 的給定方式是由 c 個不超過 t 的質數的乘積給出的,c 和 t 的

範圍會在資料範圍中給出。

第一行一個 id 表示這個資料點的標號。

多組資料,其中第二行一個整數 T 表示資料組數。

對於每一組資料:

第一行兩個整數 c 和 m。

第二行 c 個整數,這些整數都是質數,且兩兩不同,他們的乘積即為

n。

由於你可以通過輸入求出 t,輸入不再給出。

\(\color{blue} \text {【輸出】}\)

對於每組資料輸出一行,表示解的個數。

\(\color{blue} \text {【輸入樣例】}\)

0
1
2 3
2 3

\(\color{blue} \text {【輸出樣例】}\)

6

\(\color{blue} \text {【提示】}\)

\(\color{blue} \text {【解題思路】}\)

這道題要用到一些神奇的數學方法,主要是積性篩(實現方法和線性篩素數是一樣的,相信大家認真閱讀一下程式碼是可以看懂的,最後答案藉助CRT乘一下就好了!)

AC code:

/*
    Name: division 
    Copyright: ssy
    Author: Mudrobot
    Date: 2018/10/11 20:59:16
    Description: exgcd CRT
*/
#include<bits/stdc++.h>
#define gc() getchar()//caution!!!
#define LL long long
#define MOD 998244353
#define N 10004
using namespace std;
/*inline char gc() {
  static char buf[1<<18],*fs,*ft;
  return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<18,stdin)),fs==ft)?EOF:*fs++;
}*/
template<class T>
inline void read(T &aa) {
  register int k=0,f=1;
  register char c=gc();
  for (;!isdigit(c);c=gc()) if(c=='-')f=-1;
  for (;isdigit(c);c=gc()) k=(k<<3)+(k<<1)+(c-'0');
  aa=k*f;
}
template<class T>
inline void out(T x){if(x>9)out(x/10);putchar(x%10+'0');}
int id,T,c,m,ksm[N],zs[N];
bool mark[N];
int fpow(int x,int k,int p){int v=1;
    while(k){
        if(k&1)v=v*x%p;
        k>>=1,x=x*x%p;
    }return v;
}//BY Lys
int calc(int p)
{
	int ans=2,cnt=0;//因為一定有自身和1
	memset(mark,false,sizeof(mark));
	for(int i=2;i<=10000;++i)//注意題目要求範圍 
	{
		if(!mark[i])zs[++cnt]=i,ksm[i]=fpow(i,m,p);
		for(int j=1;j<=cnt&&i*zs[j]<=10000;++j)
		{
			mark[i*zs[j]]=true;
			ksm[i*zs[j]]=ksm[i]*ksm[zs[j]]%p;
			if(!i%zs[j])break;//防止重複! 
		}
		if(ksm[i]%p==i) ans++;
	}
	return ans;
}
int main()
{
    //freopen(".in", "r", stdin);freopen(".out", "w", stdout);
	read(id);read(T);
	for(int u=1;u<=T;++u) 
	{
		int ans=1;read(c);read(m);
		for(int i=1;i<=c;++i)
		{
			int a;read(a);
			ans=((LL)ans*calc(a))%MOD;
		}
		out(ans);putchar('\n');
	}
    //fclose(stdin);fclose(stdout);
    return 0;
}
/*
0
1
2 3
2 3
*/

本人太菜 T3出來!