Nowcoder 牛客練習賽23
Preface
終於知道YKH他們為什麽那麽喜歡打牛客網了原來可以抽衣服
那天晚上有空就也去玩了下,刷了一波水TM的YKH就抽到了,我當然是沒有了
題目偏水,好像都是1A的。才打了一個半小時,回家就直接睡覺了
A 托米的賭球
送分題,考慮貪心的思想,由於要總數量最小,因此面額大的應該能選就選。
所以一路貪心下來即可。 CODE
#include<cstdio> #include<cctype> using namespace std; const int A[7]={100,50,20,10,5,2,1},B[6]={50,20,10,5,2,1}; int n,a,b,p; inline char tc(void) { static char fl[100000],*A=fl,*B=fl; return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++; } inline void read(int &x) { x=0; char ch; while (!isdigit(ch=tc())); while (x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc())); } inline void write(int x) { if (x>9) write(x/10); putchar(x%10+'0'); } int main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); register int i; read(n); while (n--) { read(a); read(b); p=0; for (i=0;i<7;++i) write(a/A[i]),putchar(' '),a%=A[i]; for (i=0;i<5;++i) write(b/B[i]),putchar(' '),b%=B[i]; write(b/B[5]); putchar('\n'); } return 0; }
B 托米的劃分
首先我們發現一個十分顯然的性質,每次劃分時的兩個數應當盡可能接近,這樣乘積才會最小。
可以結合長方形周長確定,兩邊越接近面積越小來理解當然用二次函數證明之也可
然後考慮直接遞歸計算這個過程,這樣貌似加上記憶化都是\(O(n)\)的。
隨即我們發現這個可以打表,但是同樣\(O(n)\)的時空復雜度難以接受。
然後我們就要發揮一下亂搞的技巧了,我們把\(10^6\)以內的數打表出來,然後剩下的直接遞歸處理即可。
這樣遞歸層數很小足以通過。 CODE
#include<cstdio> #include<cctype> using namespace std; const int N=1e6; int t,n; long long d[N+5]; inline char tc(void) { static char fl[100000],*A=fl,*B=fl; return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++; } inline void read(int &x) { x=0; char ch; while (!isdigit(ch=tc())); while (x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc())); } inline void write(long long x) { if (x>9) write(x/10); putchar(x%10+'0'); } inline void init(void) { for (register int i=1;i<=N;++i) d[i]=d[i+1>>1]+d[i>>1]+1LL*(i+1>>1)*(i>>1); } inline long long solve(int x) { if (x<=N) return d[x]; return solve(x+1>>1)+solve(x>>1)+1LL*(x+1>>1)*(x>>1); } int main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); register int i; read(t); init(); while (t--) { read(n); write(solve(n)); putchar('\n'); } return 0; }
C 托米的位運算
考慮轉化問題,當給價最大化是,就是當它們的共同二進制位最大時就是最大給價。
所以貪心地從大到小枚舉二進制位,滿足以下條件即可:
- \(a_i\)最大二進制位為\(1\)
- 滿足上一條的\(a_i\)的\(\operatorname{and}\)所得剩下的公有二進制位不小於目前的最高位
然後就輕松水過了。 CODE
#include<cstdio> #include<cctype> using namespace std; typedef long long LL; const LL N=1e5+5; LL t,n,a[N],b[N],cnt,tot; inline char tc(void) { static char fl[100000],*A=fl,*B=fl; return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++; } inline void read(LL &x) { x=0; char ch; while (!isdigit(ch=tc())); while (x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc())); } inline void write(LL x) { if (x>9) write(x/10); putchar(x%10+'0'); } int main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); register LL i,j; read(n); for (i=1;i<=n;++i) read(a[i]); for (cnt=0,tot=(1LL<<31)-1,j=30;j>=0;--j) { for (cnt=0,i=1;i<=n;++i) if (a[i]&(1LL<<j)) b[++cnt]=a[i],tot&=a[i]; if (tot%(1LL<<j)==0) { for (printf("%lld\n",cnt),i=1;i<cnt;++i) printf("%lld ",b[i]); printf("%lld",b[cnt]); break; } } return 0; }
D 托米的咒語
本來可能還想騙我們寫寫DFS增加下碼量的,然後我用next_premutation
水過去了
數據範圍這麽小,因此全排列來一發,考慮怎麽判斷
我們對於原來的字符串處理兩個數組。\(f_{i,j}\)表示第\(i\)位之後(不包括\(i\))最近的一個\(j\)字符的位置,\(fir_i\)表示字符\(i\)在串中最早出現的位置。
判斷時直接\(O(9)\)向後跳即可,CODE
#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=3005,P=15;
char s[N]; bool flag;
int f[N][P],num[P],fir[P],ans,n,now;
int main()
{
register int i; scanf("%s",s+1); n=strlen(s+1);
memset(f,63,sizeof(f));
for (i=n-1;i>=1;--i)
{
memcpy(f[i],f[i+1],sizeof(f[i])); f[i][s[i+1]-'a'+1]=i+1;
fir[s[i]-'a'+1]=i;
}
for (i=1;i<=9;++i) num[i]=i;
do
{
for (now=fir[num[1]],flag=1,i=2;i<=9;++i)
if (f[now][num[i]]<=n) now=f[now][num[i]]; else { flag=0; break; }
ans+=flag;
}while (next_permutation(num+1,num+10));
return printf("%d",ans),0;
}
E 托米的數學
貌似是不可食用的數學題不會留著以後填坑
F托米的遊戲
這題如果你光用腦子想可能會感到毫無頭緒,但是如果拿起筆寫一下式子就會發現這是個SB題。
考慮利用期望的線性性質來分析,總的期望輪數\(E\)等於各點的期望輪數\(e_i\)之和。
我們先DFS求出每個點的深度\(dep_i(dep_{rt}=1)\),那麽對於可能的情況只有兩種:
- 選中該點導致該點被砍去,那麽此時的概率為\(\frac{1}{dep_x}\)(因為刪去點的過程只與這個點的祖先個數(即深度)有關),期望貢獻為\(1\),則期望為\(\frac{1}{dep_x}\)
- 選到該點的祖先導致該點被砍去,此時概率都不用算,因為期望貢獻為\(0\),乘一下就是\(0\)
因此答案就是\(\sum_{i=1}^n \frac{1}{dep_i}\)
CODE
#include<cstdio>
#include<cctype>
#include<cstring>
using namespace std;
const int N=100005,mod=998244353;
struct edge
{
int to,next;
}e[N<<1];
int n,dep[N]={0,1},head[N],cnt,x,y,ans;
inline char tc(void)
{
static char fl[100000],*A=fl,*B=fl;
return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
}
inline void read(int &x)
{
x=0; char ch; while (!isdigit(ch=tc()));
while (x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc()));
}
inline void double_add(int x,int y)
{
e[++cnt].to=y; e[cnt].next=head[x]; head[x]=cnt;
e[++cnt].to=x; e[cnt].next=head[y]; head[y]=cnt;
}
inline void inc(int &x,int y)
{
if ((x+=y)>=mod) x-=mod;
}
inline int quick_pow(int x,int p)
{
int tot=1;
for (;p;x=1LL*x*x%mod,p>>=1) if (p&1) tot=1LL*tot*x%mod;
return tot;
}
inline void DFS(int now,int fa)
{
inc(ans,quick_pow(dep[now],mod-2));
for (register int i=head[now];~i;i=e[i].next)
if (e[i].to!=fa) dep[e[i].to]=dep[now]+1,DFS(e[i].to,now);
}
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
register int i; read(n); memset(head,-1,sizeof(head));
for (i=1;i<n;++i) read(x),read(y),double_add(x,y);
return DFS(1,-1),printf("%d",ans),0;
}
Nowcoder 牛客練習賽23