Noip模擬94 2021.11.11
T1 嗑瓜子
\(\color{red}{\huge{以後能線性求逆元就別用快速冪!!!}}\)
可得要長個教訓,千萬不要相信本機跑\(0.7\)能在\(TLEcoders\)上面\(A\)題!!!
一定要優化,不要浪!!!!
剩下的就沒啥了,應該都會切
算了還是說一下吧,設一個\(dp[i][j]\)表示堆裡面有\(i\)個瓜子\(j\)個瓜子皮時候的概率
那麼最後答案就是吃剩下了\(n\)個瓜子皮,不剩瓜子時候的概率乘上到這種狀態時候的需要的輪數
答案就是這樣統計的
for(int i=n*2;~i;i--)ans=(ans+f[0][i]*(n++)%mod)%mod;
那麼轉移的話直接就可以簡單的按照概率的計算方案計算出多少概率抽出瓜子多少概率抽出皮分別轉移到\(dp[i-1][j+2]\)
eat
#include<bits/stdc++.h> #define int long long using namespace std; const int NN=2005,mod=998244353; namespace AE86{ FILE *wsn; #define gc() p1==p2 and (p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?EOF:*p1++ char buf[1<<20],*p1=buf,*p2=buf; // #define gc getchar auto read=[](){ int x=0,f=1;char ch=gc();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=gc();} while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=gc();} return x*f; }; auto write=[](int x,char opt='\n'){ char ch[20];short len=0;if(x<0)x=~x+1,putchar('-'); do{ch[len++]=x%10+(1<<5)+(1<<4);x/=10;}while(x); for(short i=len-1;i>=0;--i){putchar(ch[i]);}putchar(opt); }; auto qmo=[](int a,int b,int ans=1){ for(;b;b>>=1,a=a*a%mod)if(b&1)ans=ans*a%mod; return ans; }; }using namespace AE86; int n,f[NN][NN<<1],ans,inv[NN<<1]; inline int frac(int x,int y){return x*inv[y]%mod;} namespace WSN{ inline short main(){ wsn=freopen("eat.in","r",stdin); wsn=freopen("eat.out","w",stdout); n=read(); f[n][0]=1; inv[0]=inv[1]=1; for(int i=2;i<=2*n;i++) inv[i]=(mod-mod/i)*inv[mod%i]%mod; for(int i=n-1;~i;i--){ for(int j=(n-i)*2;~j;j--){ f[i][j]+=f[i+1][j-2]*frac(i+1,i+j-1)%mod; if(i!=0) f[i][j]+=f[i][j+1]*frac(j+1,i+j+1)%mod; f[i][j]%=mod; } } for(int i=n*2;~i;i--)ans=(ans+f[0][i]*(n++)%mod)%mod; write(ans); return 0; } } signed main(){return WSN::main();}
T2 第k大查詢
應該背過主席樹板子的都能拿到\(60\),然後我呢,沒有用主席樹,用了個\(set\),邊界沒有卡對,只有\(20pts\)
改一下邊界就可以拿到\(60\),比較無奈的掛了\(40\)分。。。
TLE60
#include<bits/stdc++.h> #define int long long using namespace std; const int NN=5e5+5; namespace AE86{ FILE *wsn; #define gc() p1==p2 and (p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?EOF:*p1++ char buf[1<<20],*p1=buf,*p2=buf; auto read=[](){ int x=0,f=1;char ch=gc();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=gc();} while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=gc();} return x*f; }; auto write=[](int x,char opt='\n'){ char ch[20];short len=0;if(x<0)x=~x+1,putchar('-'); do{ch[len++]=x%10+(1<<5)+(1<<4);x/=10;}while(x); for(short i=len-1;i>=0;--i){putchar(ch[i]);}putchar(opt); }; auto checkmin=[](int &a,int b){a=a<b?a:b;}; auto checkmax=[](int &a,int b){a=a>b?a:b;}; }using namespace AE86; int n,a[NN],k; namespace carisian_tree{ int stk[NN],top,son[NN][2]; inline void build(int *a){ for(int i=1;i<=n;i++){ while(top&&a[stk[top]]<a[i])son[i][0]=stk[top--]; if(top) son[stk[top]][1]=i; stk[++top]=i; } } int ll[NN],rr[NN]; inline void dfs(int x=stk[1]){ ll[x]=rr[x]=x; if(son[x][0]) dfs(son[x][0]),checkmin(ll[x],ll[son[x][0]]); if(son[x][1]) dfs(son[x][1]),checkmax(rr[x],rr[son[x][1]]); } }using namespace carisian_tree; namespace Task{ auto task=[](){ build(a); dfs(); int ans=0; for(int i=1;i<=n;i++)ans+=a[i]*(i-ll[i]+1)*(rr[i]-i+1); write(ans); return; }; int srt[NN]; auto pian=[](int ans=0){ for(int i=1;i<=n-k+1;i++){ set<int> s; for(int j=i;j<=i+k-1;j++) s.insert(a[j]); ans+=*s.begin(); for(int j=k+i;j<=n;j++){ s.insert(a[j]); s.erase(s.begin()); ans+=*s.begin(); } } write(ans); return; }; } namespace WSN{ inline short main(){ wsn=freopen("kth.in","r",stdin); wsn=freopen("kth.out","w",stdout); n=read();k=read(); for(int i=1;i<=n;i++)a[i]=read(); if(k==1) return Task::task(),0; Task::pian(); return 0; } } signed main(){return WSN::main();}
正解比想\(set\)簡單吧,但是比較考驗碼力。。。依舊是調了很長時間
他們說這叫雙向連結串列,不理解,我覺得和連結串列沒有區別
我拿的\(set\)維護,這樣複雜度是\(O(nklogn)\)的,但是\(TLEcoders\)可以跑過就沒有什麼問題了
把排列的位置記錄下來,按照從大到小的順序依次插入\(set\),這樣保證了當前\(set\)中所有數都大於等於這個新插入的元素
找到離他最近的他前面的元素和他後面的元素,連上指標
然後每次只統計在他左邊和在他右邊的比他大的\(k-1\)個元素能夠框出多少合法區間即可
kth
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int NN=5e5+5;
namespace AE86{
FILE *wsn;
#define gc() p1==p2 and (p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?EOF:*p1++
char buf[1<<20],*p1=buf,*p2=buf;
auto read=[](){
int x=0,f=1;char ch=gc();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=gc();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=gc();} return x*f;
};
auto write=[](int x,char opt='\n'){
char ch[20];short len=0;if(x<0)x=~x+1,putchar('-');
do{ch[len++]=x%10+(1<<5)+(1<<4);x/=10;}while(x);
for(short i=len-1;i>=0;--i){putchar(ch[i]);}putchar(opt);
};
auto checkmin=[](int &a,int b){a=a<b?a:b;};
auto checkmax=[](int &a,int b){a=a>b?a:b;};
}using namespace AE86;
int n,a[NN],k;
namespace carisian_tree{
int stk[NN],top,son[NN][2];
inline void build(int *a){
for(int i=1;i<=n;i++){
while(top&&a[stk[top]]<a[i])son[i][0]=stk[top--];
if(top) son[stk[top]][1]=i;
stk[++top]=i;
}
}
int ll[NN],rr[NN];
inline void dfs(int x=stk[1]){ ll[x]=rr[x]=x;
if(son[x][0]) dfs(son[x][0]),checkmin(ll[x],ll[son[x][0]]);
if(son[x][1]) dfs(son[x][1]),checkmax(rr[x],rr[son[x][1]]);
}
}using namespace carisian_tree;
namespace Task{
auto task=[](){
build(a); dfs(); int ans=0;
for(int i=1;i<=n;i++)ans+=a[i]*(i-ll[i]+1)*(rr[i]-i+1);
write(ans); return;
};
}
set<int> s;
int pos[NN],ans,nxt[NN][2];
int len[51][2],l,r;
bool tl,tr;
auto insert=[](int x){
s.insert(x);
if(s.find(x)!=s.begin()){l=*(--s.find(x));nxt[x][0]=l;nxt[l][1]=x;}
if(s.find(x)!=--s.end()){r=*(++s.find(x));nxt[x][1]=r;nxt[r][0]=x;}
};
auto solve=[](int x){
insert(pos[x]); l=r=pos[x]; tl=tr=0;
memset(len,0,sizeof(len));
for(int i=0;i<=k;i++){
if(l!=0){
len[i][0]=l-nxt[l][0];
l=nxt[l][0];
}
if(r!=n+1){
len[i][1]=nxt[r][1]-r;
r=nxt[r][1];
}
if(!l&&r==n+1) break;
}
for(int i=0,j=k;i<=k;++i,--j) ans+=x*len[i][0]*len[j][1];
};
namespace WSN{
inline short main(){
wsn=freopen("kth.in","r",stdin);
wsn=freopen("kth.out","w",stdout);
n=read();k=read()-1;
for(int i=1;i<=n;i++)a[i]=read(),pos[a[i]]=i,nxt[i][0]=0,nxt[i][1]=n+1;
if(k==0) return Task::task(),0;
for(int i=n;i;i--)solve(i);
write(ans);
return 0;
}
}
signed main(){return WSN::main();}
T3 樹上路徑
其實是一道好題,各種陣列的定義在這裡再明確一下,感覺那個上面說的不是特別清楚
定義現在考慮的是斷開\((x,y)\)後的兩顆樹的直徑如何求
我們現在刪掉的是\(y\)的子樹,剩下的樹算出一個直徑記作\(up[y]\),刪掉的那個\(y\)裡面的直徑為\(down[y]\)
\(dp[x][0/1/2]\)表示以\(x\)為根的子樹中的最長、次長、次次長鏈的長度
\(dp[y][3]\)和前面的不是一個東西,可以理解為另一個數組,表示過\(y\)的最長的鏈的長度
\(len[x][0/1]\)表示不經過\(x\)的最長、次長鏈長度
tree
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int NN=500005;
namespace AE86{
FILE *wsn;
#define gc() p1==p2 and (p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?EOF:*p1++
char buf[1<<20],*p1=buf,*p2=buf;
auto read=[](){
int x=0,f=1;char ch=gc();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=gc();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=gc();} return x*f;
};
auto write=[](int x,char opt='\n'){
char ch[20];short len=0;if(x<0)x=~x+1,putchar('-');
do{ch[len++]=x%10+(1<<5)+(1<<4);x/=10;}while(x);
for(short i=len-1;i>=0;--i){putchar(ch[i]);}putchar(opt);
};
auto swap_=[](int&a,int&b){a^=b^=a^=b;};
auto checkmax=[](int&a,int b){a=a>b?a:b;};
}using namespace AE86;
int n,ans,dep[NN];
struct node{int u,v;}p[NN];
#define star(e,i,x) for(int i=e.head[x],y=e.to[i];i;i=e.next[i],y=e.to[i])
struct SNOW{
int to[NN<<1],next[NN<<1],head[NN],rp;
inline void add(int x,int y){
to[++rp]=y; next[rp]=head[x]; head[x]=rp;
to[++rp]=x; next[rp]=head[y]; head[y]=rp;
}
}e;
int dp[NN][4],dw[NN],up[NN],mx[NN],len[NN][2];
inline void dfs1(int f,int x,int deep){
dep[x]=deep;
star(e,i,x) if(y!=f){
dfs1(x,y,deep+1);
int tmp=dp[y][0]+1;
if(tmp>dp[x][0]) swap_(dp[x][0],tmp);
if(tmp>dp[x][1]) swap_(dp[x][1],tmp);
if(tmp>dp[x][2]) swap_(dp[x][2],tmp);
checkmax(dw[x],dw[y]);
} checkmax(dw[x],dp[x][0]+dp[x][1]);
}
inline void dfs2(int f,int x){
star(e,i,x) if(y!=f){
int tmp=dw[y];
if(tmp>len[x][0]) swap_(tmp,len[x][0]);
if(tmp>len[x][1]) swap_(tmp,len[x][1]);
}
star(e,i,x) if(y!=f){
if(dp[x][0]==dp[y][0]+1){
dp[y][3]=max(dp[x][3],dp[x][1])+1;
up[y]=max(dp[x][2],dp[x][3])+dp[x][1];
}else if(dp[x][1]==dp[y][0]+1){
dp[y][3]=max(dp[x][3],dp[x][0])+1;
up[y]=max(dp[x][2],dp[x][3])+dp[x][0];
}else{
dp[y][3]=max(dp[x][3],dp[x][0])+1;
up[y]=max(dp[x][1],dp[x][3])+dp[x][0];
}
if(len[x][0]==dw[y]) up[y]=max(up[y],len[x][1]);
else up[y]=max(up[y],len[x][0]);
dfs2(x,y);
}
}
namespace WSN{
inline short main(){
wsn=freopen("tree.in","r",stdin);
wsn=freopen("tree.out","w",stdout);
n=read();
for(int i=1,u,v;i<n;i++)
p[i].u=read(),p[i].v=read(),e.add(p[i].u,p[i].v);
dfs1(0,1,0); dfs2(0,1);
for(int i=1;i<n;i++){
int x=p[i].u,y=p[i].v;
if(dep[x]<dep[y])swap_(x,y);
mx[up[x]+1]=max(mx[up[x]+1],dw[x]+1);
mx[dw[x]+1]=max(mx[dw[x]+1],up[x]+1);
}
for(int i=n;i;i--)mx[i]=max(mx[i],mx[i+1]),ans+=mx[i];
write(ans);
return 0;
}
}
signed main(){return WSN::main();}
T4 糖果
還沒改出來,正在改
\(\color{white}{還是不理想,加上掛的分數僅僅能夠排到rk44,因為很多人都是切了兩道題的,180確實不算高,發現考場上覺得切掉一道題後效率就變得不是那麼高了,然而覺得最穩的一道題還被卡時限了。。。。。名次就更低了,確實有一些慌,但是並沒有對平日生活造成影響,只是覺得CSP過於之低,又加上之後的這些比賽沒有特別優秀的表現,僅僅是有幾次題目順手分還可以看。。。。比原來頹廢的時間少了,~~但是並沒有什麼提升,成績反而變低了(大霧)~~,就很不理解,不過記得當時CSP前有同學狀態不是很好,但是CSP上表現很好,可能這就是在沉默中爆發??不過我還是相信只要不頹廢,做題,思考,按步驟來就是可以的,考場上不能做出一道題效率就降低一定要努力思考到考試最後}\)