涼心模擬總結
還是自己太菜了,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
*/