游記
12:00去開題,然后開了個錘子。
網站瘋狂卡崩。
直到12:50才進去。
為什么有的隊伍開局6min就切題了啊
看到1011很多人過了就去看。
然后發現並不會(
想了一下決定暴力硬剛,寫了個map+隊列維護每一層的垃圾玩意,交上去WA了。
然后轉眼一看這不直接記搜就好了嘛?我是小丑。
然后發現居然和cqy同時過了,他寫了1007
然后再看了一眼榜,發現1004蠻多人過了就去看,發現似乎就是隨便算平行直線就好了。寫了一發WA了發現沒判平行y軸,再改了一發交上去過了。
cqy覺得1009很可做因為隨機,然后一手翻譯成價格和寶石選一個,然后死都不知道數據隨機怎么用
看了一下榜發現1003過的人也很多,看了一眼感覺FFT匹配然后又不像,之后發現枚舉字符真的就是FFT匹配,NTT時模數寫成982244353也沒誰了。
漫無目的開了1010,發現這不就是wqs二分經典題嗎,然后寫了一發發現T了,發現可以爆枚所有斜率,然后交上去還是T了,發現大概要2e9次運算,就死命卡常沒過。
想不到什么\(O(nlogn+m)\)的MST
然后就4題11h排到了162,如果罰時少200min就可以到120,垃圾服務器毀我青春
題解
1011
容易發現對於一個給定的\(k\),\(n\)的答案是恆定的。
又因為線段樹的第\(i\)層只有\(i\)個節點。
所以直接記搜復雜度正確。
code:
using namespace std;
I void read(int &x){char s=Gc();x=0;while(s<'0'||s>'9') s=Gc();while(s>='0'&&s<='9') x=x*10+s-48,s=Gc();}
int T;ll n,k,Ans,ToT;map<ll,ll> G;
I ll dfs(ll n){
if(n<=k) return 1;if(G.count(n)) return G[n];return G[n]=dfs(n>>1)+dfs(n+1>>1)+1;
}
int main(){
freopen("1.in","r",stdin);
scanf("%d",&T);while(T--){
scanf("%lld%lld",&n,&k);G.clear();printf("%lld\n",dfs(n));
}
}
1004
容易發現如果我們先將所有的斜率都選一遍,再將剩下還有的斜率選一邊,這樣重復是最優的。
那么直接開個map記錄即可。注意特判斜率為無窮的情況。時間復雜度\(O(nlogn)\)
using namespace std;
I void read(int &x){char s=Gc();x=0;while(s<'0'||s>'9') s=Gc();while(s>='0'&&s<='9') x=x*10+s-48,s=Gc();}
int T,n,nx,mx,ny,my,cnt,F[N+5],Maxn,nows;map<db,int> G;pair<db,int> pus;
int main(){
freopen("1.in","r",stdin);
re int i,j;scanf("%d",&T);while(T--){
scanf("%d",&n);G.clear();cnt=nows=Maxn=0;for(i=1;i<=n;i++) scanf("%d%d%d%d",&nx,&ny,&mx,&my),mx==nx?(nows++):(G[(my-ny)*1.0/(mx-nx)]++);
Me(F,0);for(map<db,int>::it i=G.begin();i!=G.end();i++) pus=*i,F[pus.second]++,Maxn=max(Maxn,pus.second);F[nows]++;Maxn=max(Maxn,nows);
for(i=Maxn;i;i--) F[i]+=F[i+1];
for(i=1;i<=Maxn;i++){
while(F[i]--) cnt++,printf("%d\n",cnt-i);
}
}
}
1003
思路肯定是算出對於一個點以后的\(m\)個點有幾個匹配然后扔到答案數組上前綴和一下就好了。
我們順着這個思路下去。
帶通配符的匹配一般就是FFT的匹配。
我們考慮枚舉每個字符,算它匹配多少次然后加起來即可。
構造一下發現如果我們將所有通配符和當前字符都是\(1\),那么兩邊相乘就是匹配個數。
NTT加法卷積即可。時間復雜度\(O(wnlogn)\)
code:
using namespace std;
int T,n,m,tr[N+5],k,cnt,F[N+5],H[N+5];char s1[N+5],s2[N+5];ll A[N+5],B[N+5];
I ll mpow(ll x,int y=mod-2){ll ans=1;while(y) (y&1)&&(ans=ans*x%mod),x=x*x%mod,y>>=1;return ans;}
I void swap(ll &x,ll &y){x^=y^=x^=y;}const ll G=3,invG=mpow(G);
I void NTT(ll *A,int n,int flag){
int i,j,h;ll pus,now,key;for(i=0;i<n;i++) i<tr[i]&&(swap(A[i],A[tr[i]]),0);
for(i=2;i<=n;i<<=1){
for(key=mpow(flag?G:invG,(mod-1)/i),j=0;j<n;j+=i) {
for(now=1,h=j;h<j+i/2;h++) pus=A[h+i/2]*now%mod,A[h+i/2]=(A[h]-pus+mod)%mod,A[h]=(A[h]+pus)%mod,now=now*key%mod;
}
}if(flag)return;ll inv=mpow(n);for(i=0;i<n;i++) A[i]=A[i]*inv%mod;
}
int main(){
freopen("1.in","r",stdin);
re int i,j;scanf("%d",&T);while(T--){
scanf("%d%d",&n,&m);Me(H,0);Me(F,0);scanf("%s",s1);scanf("%s",s2);for(k=1;k<=n+m;k<<=1);for(i=0;i<k;i++) tr[i]=(tr[i>>1]>>1)|((i&1)?k/2:0);
for(i=cnt=0;i<m;i++) cnt+=(s2[i]=='*');for(i=0;i<=9;i++){
Me(A,0);Me(B,0);for(j=0;j<n;j++) A[j]=(s1[j]==48+i||s1[j]=='*');for(j=0;j<m;j++) B[m-j-1]=(s2[j]==48+i);
NTT(A,k,1);NTT(B,k,1);for(j=0;j<k;j++) A[j]=A[j]*B[j]%mod;NTT(A,k,0);for(j=m-1;j<n;j++) F[j-m+1]+=A[j];
}
for(i=0;i<=n-m;i++) H[m-cnt-F[i]]++;for(i=1;i<=m;i++)H[i]+=H[i-1];for(i=0;i<=m;i++) printf("%d\n",H[i]);
}
}
1010
我是sb
首先這個東西是個經典題目,因為這個函數是凸的,所以可以直接wqs二分,時間復雜度\(O(Tnmlogn)\)
然后發現斜率很小可以直接爆枚,然后就是\(O(Tnm)\)
但是還是不行,考慮怎么將邊數降為\(O(n)\)
容易發現我們跑折扣邊和非折扣邊兩顆MST出來,不在這上面的邊肯定不會出現。
所以復雜度變成\(O(T(m+n^2))\)
using namespace std;
I void read(int &x){char s=Gc();x=0;while(s<'0'||s>'9') s=Gc();while(s>='0'&&s<='9') x=x*10+s-48,s=Gc();}
int n,m,k,Maxn,fa[N+5],T,Ans[N+5],un,wn,Gh;struct ques{int w,a,b,x,y;}S[M+5],G[N+5<<1],now;vector<ques> F1[N+5<<1],F2[N+5<<1],F3[N+5<<1];
I bool cmp1(ques x,ques y){return x.a<y.a;}I bool cmp2(ques x,ques y){return x.b<y.b;}
I int Getfa(int x){return x==fa[x]?x:fa[x]=Getfa(fa[x]);}
I void GetAns(int mid){
re int i,j,cntmax=0,cntmin=0,ans=0,ToT=0,tots=0;for(i=1;i<=n;i++) fa[i]=i;for(i=1;i<=Maxn*2;i++) F1[i].clear(),F2[i].clear(),F3[i].clear();
for(i=1;i<=Gh;i++){
G[i].w=min(G[i].b,G[i].a+mid);ToT=max(ToT,G[i].w);
if(G[i].a+mid==G[i].b)F1[G[i].w].push_back(G[i]);
else if(G[i].a+mid<G[i].b) F2[G[i].w].push_back(G[i]);
else F3[G[i].w].push_back(G[i]);
}
for(i=1;i<=ToT;i++){
int s1=F1[i].size(),s2=F2[i].size(),s3=F3[i].size();
for(j=0;j<s1;j++) {
now=F1[i][j];un=Getfa(now.x);wn=Getfa(now.y);un^wn&&(ans+=now.w,fa[un]=wn);
}
for(j=0;j<s3;j++) {
now=F3[i][j];un=Getfa(now.x);wn=Getfa(now.y);un^wn&&(ans+=now.w,fa[un]=wn);
}
for(j=0;j<s2;j++) {
now=F2[i][j];un=Getfa(now.x);wn=Getfa(now.y);un^wn&&(ans+=now.w,cntmin++,fa[un]=wn);
}
}
for(i=1;i<=n;i++) fa[i]=i;tots=ans=0;
for(i=1;i<=ToT;i++){
int s1=F1[i].size(),s2=F2[i].size(),s3=F3[i].size();
for(j=0;j<s1;j++) {
now=F1[i][j];un=Getfa(now.x);wn=Getfa(now.y);un^wn&&(ans+=now.w,cntmax++,fa[un]=wn);if(tots==n-1) break;
}
for(j=0;j<s2;j++) {
now=F2[i][j];un=Getfa(now.x);wn=Getfa(now.y);un^wn&&(ans+=now.w,cntmax++,fa[un]=wn);if(tots==n-1) break;
}
for(j=0;j<s3;j++) {
now=F3[i][j];un=Getfa(now.x);wn=Getfa(now.y);un^wn&&(ans+=now.w,fa[un]=wn);if(tots==n-1) break;
}
}
for(i=cntmax;i>=cntmin;i--) Ans[i]=min(Ans[i],ans-i*mid);
}
int main(){
freopen("1.in","r",stdin);freopen("1.out","w",stdout);
re int i;scanf("%d",&T);while(T--){
scanf("%d%d",&n,&m);Maxn=Gh=0;Me(Ans,0x3f);for(i=1;i<=m;i++) read(S[i].x),read(S[i].y),read(S[i].b),read(S[i].a),Maxn=max(Maxn,S[i].b);
for(i=1;i<=n;i++) fa[i]=i;for(sort(S+1,S+m+1,cmp1),i=1;i<=m;i++){
un=Getfa(S[i].x);wn=Getfa(S[i].y);if(un==wn) continue;fa[un]=wn;G[++Gh]=S[i];
}
for(i=1;i<=n;i++) fa[i]=i;for(sort(S+1,S+m+1,cmp2),i=1;i<=m;i++){
un=Getfa(S[i].x);wn=Getfa(S[i].y);if(un==wn) continue;fa[un]=wn;G[++Gh]=S[i];
}for(i=0;i<=Maxn;i++) GetAns(i);for(i=0;i<n;i++) printf("%d\n",Ans[i]);
}
}
1009
cqy的英語水平真的不錯。
因為數據隨機所以可以考慮亂搞。
可以發現如果對於一個點的兩個狀態\((x1,y1)\)和\((x2,y2)\)如果存在二維偏序那么小的那個一定可以不要。
盲猜這樣維護單調棧以后不會很多,事實上是對的。時間復雜度\(O(能過)\)
code:
using namespace std;
int T,n,m,A[N+5][N+5],B[N+5][N+5],l,r,Eh,Ch;ll Ans;struct ques{int a,b;}now,E[M+5],C[M+5];vector<ques> F[N+5][N+5];I bool cmp(ques x,ques y){return (x.a==y.a)?x.b<y.b:x.a<y.a;}
I void Solve(){
re int i,j,h;scanf("%d",&n);for(i=1;i<=n;i++) for(j=1;j<=n;j++) scanf("%d",&A[i][j]);for(i=1;i<=n;i++) for(j=1;j<=n;j++) scanf("%d",&B[i][j]),F[i][j].clear();
F[1][1].push_back((ques){A[1][1],B[1][1]});for(i=1;i<=n;i++){
for(j=1;j<=n;j++){if(i==1&&j==1) continue;l=r=Eh=Ch=0;
while(l<F[i][j-1].size()||r<F[i-1][j].size())E[++Eh]=(r>=F[i-1][j].size()||(l<F[i][j-1].size()&&cmp(F[i][j-1][l],F[i-1][j][r])))?F[i][j-1][l++]:F[i-1][j][r++];
for(h=Eh;h;h--) (!Ch||E[h].b>C[Ch].b)&&(C[++Ch]=E[h],0);for(h=Ch;h;h--) C[h].a+=A[i][j],C[h].b+=B[i][j],F[i][j].push_back(C[h]);
}
}for(i=Ans=0;i<F[n][n].size();i++) Ans=max(Ans,1ll*F[n][n][i].a*F[n][n][i].b);printf("%lld\n",Ans);
}
int main(){
freopen("1.in","r",stdin);
scanf("%d",&T);while(T--)Solve();
}
先咕着等NOI完了再補。