sb博主又犯sb錯誤了!
他覺得以往模擬賽因為犯sb錯誤扔的分足足有1k分了!
於是他想記錄一下自己犯的sb錯誤看看自己到底有多sb!
嗯就從今天開始吧
2018.9.28
1. 二分邊界寫錯。騷什么啊卡那么緊的上界是要干啥啊開1e9是能死啊?
2. 如果差分完要求前綴和的話記得掃兩遍啊掃兩遍,只掃一遍求的是原數組的值記住了
3. 真心立flag-->空間再開炸就剁手!!
4. 一個數 x 想求它遍歷模 n 剩余系的話應該是 (n-1)/gcd(x,n),不是(n-1)/x 注意了
5. 啊雙模數哈希的時候一定一定要特別小心敲錯變量名,比如說把mod2敲成mod1,base1敲成base2之類的。。。也許可以考慮起名的時候起兩個相差很遠的名字?
6. 我他媽!再打錯文件!他媽女裝!
7.千萬!千萬!別用三目運算符!能用if就不要用三目運算符!90->50!
8. 忽略上面那條 md自己點分治打錯了還怪三目運算符 三目運算符:這鍋我不背
以下原文:
寫在前面
由於我天天被虐,模擬考還老是考的極差,感覺有必要開個博客記錄一下每場考試
由於懶得加美元符所以這篇博客不用 latex
由於要弄代碼折疊我還特意換掉了 Markdown 編輯器
由於我沒話說了,前言就這么多吧
2018.7.13
開場看 T1 發現不會頓時賊虛,思路明明馬上就是正解了但是死活沒想出來。把點權放到邊上求出來最大生成樹之后立馬敲了個樹剖(我只是生來碼農) 然后跟暴力一樣 n^2 求的兩點邊權最小值
看 T2 也是發現性質可以開兩個樹狀數組做但是死活沒想出來答案從哪找,結果發現莫隊一臉可做碼了個帶修莫隊還把塊大小設成根號 n 了。(我果然菜到連前一天剛講過的板子都不會敲了
結果 T2 被卡但是還是比暴力多 20 分
T3 更不可做 直接30分爆搜走人
總分 50+50+30=130 本部 rank3/16
T1 這東西應該能發現可以從大到小加邊合並集合啊這不跟走廊潑水節一樣么

#include<cstdio> #include<cctype> #include<algorithm> #define N 100005 #define int long long #define min(A,B) ((A)<(B)?(A):(B)) #define max(A,B) ((A)>(B)?(A):(B)) #define swap(A,B) ((A)^=(B)^=(A)^=(B)) int val[N]; int father[N]; int d[N],fa[N]; int n,m,cnt,tot; int cme[N],fs[N]; int sze[N],son[N]; int dfn[N],top[N]; int head[N],mn[N<<2]; struct Edge{ int to,nxt,dis; }edge[N<<1]; void add(int x,int y,int z){ edge[++cnt].to=y; edge[cnt].nxt=head[x]; edge[cnt].dis=z; head[x]=cnt; } struct Node{ int x,y,dis; friend bool operator<(Node a,Node b){ return a.dis>b.dis; } }node[N*10]; int getint(){ int x=0,f=0;char ch=getchar(); while(!isdigit(ch)) f|=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return f?-x:x; } int find(int x){ if(father[x]==x) return x; return father[x]=find(father[x]); } void file(){ freopen("zoo.in","r",stdin); freopen("zoo.out","w",stdout); } signed main(){ file(); n=getint(),m=getint(); for(int i=1;i<=n;i++){ father[i]=i; sze[i]=1; val[i]=getint(); } for(int i=1;i<=m;i++){ node[i].x=getint(); node[i].y=getint(); node[i].dis=min(val[node[i].x],val[node[i].y]); } int now=0; std::sort(node+1,node+1+m); for(int i=1;i<=m;i++){ int x=find(node[i].x); int y=find(node[i].y); if(x==y) continue; father[x]=y; now+=sze[x]*sze[y]*node[i].dis; sze[y]+=sze[x]; } printf("%lld\n",now<<1); return 0; }
T2 可以開兩個樹狀數組分別存每個點之前線段左、右端點各出現了多少次。
然后查詢就是用右端點之前出現了多少次減去左端點減一之前出現了多少次

#include<cstdio> #include<cctype> #include<cstring> #include<algorithm> #define N 200005 #define min(A,B) ((A)<(B)?(A):(B)) #define max(A,B) ((A)>(B)?(A):(B)) #define swap(A,B) ((A)^=(B)^=(A)^=(B)) int tot,cnt; int g[N<<1]; int l[N],r[N]; int T,n,cas,m; int ques[N][5]; int xgl[N],xgr[N]; struct BIT{ int f[N<<1]; void clear(){ memset(f,0,sizeof f); } int query(int x){ int ans=0; for(;x>=1;x-=x&-x) ans+=f[x]; return ans; } void add(int x,int y){ for(;x<=m;x+=x&-x) f[x]+=y; } }a,b; int getint(){ int x=0,f=0;char ch=getchar(); while(!isdigit(ch)) f|=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return f?-x:x; } signed main(){ freopen("segment.in","r",stdin); freopen("segment.out","w",stdout); T=getint(); while(T--){ a.clear();b.clear(); cnt=tot=0; n=getint(); for(int i=1;i<=n;i++){ ques[i][1]=getint(); ques[i][2]=getint(); if(ques[i][1]){ xgl[i]=l[ques[i][2]]; xgr[i]=r[ques[i][2]]; } else{ cnt++; ques[i][3]=ques[i][2]+cnt; l[cnt]=ques[i][2]; r[cnt]=ques[i][3]; g[++tot]=ques[i][2]; g[++tot]=ques[i][3]; } } std::sort(g+1,g+1+tot); m=std::unique(g+1,g+1+tot)-g-1; printf("Case #%d:\n",++cas); for(int i=1;i<=n;i++){ if(ques[i][1]==0){ ques[i][2]=std::lower_bound(g+1,g+1+m,ques[i][2])-g; ques[i][3]=std::lower_bound(g+1,g+1+m,ques[i][3])-g; printf("%d\n",b.query(ques[i][3])-a.query(ques[i][2]-1)); a.add(ques[i][2],1); b.add(ques[i][3],1); } else{ xgl[i]=std::lower_bound(g+1,g+1+m,xgl[i])-g; xgr[i]=std::lower_bound(g+1,g+1+m,xgr[i])-g; a.add(xgl[i],-1); b.add(xgr[i],-1); } } } return 0; }
T3 什么鬼啊喂 2^((n-1)*(m-1)-k) 是啥啊 noip 壓軸題靠打表的啊啊啊?
證明:不會(×)

#include<cstdio> #include<cctype> #include<cstring> #define int long long #define min(A,B) ((A)<(B)?(A):(B)) #define max(A,B) ((A)>(B)?(A):(B)) #define swap(A,B) ((A)^=(B)^=(A)^=(B)) int k; int n,m,p; int ksm(int a,int b){ int ans=1; if(b<0) return ans; while(b){ if(b&1) ans=ans*a%p; a=a*a%p; b>>=1; } return ans; } int getint(){ int x=0,f=0;char ch=getchar(); while(!isdigit(ch)) f|=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return f?-x:x; } signed main(){ freopen("number.in","r",stdin); freopen("number.out","w",stdout); n=getint();m=getint();k=getint(); for(int i=1;i<=k;i++) int a=getint(),b=getint(),c=getint(); p=getint(); if((n+m)%2==1) return printf("0"),0; printf("%lld\n",ksm(2,(n-1)*(m-1)-k)); return 0; }
2018.7.15
辣雞選手T1寫掛T2寫萎還不看T3
考試過程大概就是寫了50分鍾T1感覺有100分了然后去寫T2樹形背包查錯三小時無果交30分暴力結果暴力寫挫變成10分T1也只有20
rank...沒敢看 大約全場倒數前十吧?
T1 考試思路是對的就是用擴歐求出特解然后求通解。但是實際上不用像考場上那么復雜分16種情況。。考慮擴歐最后得出的解 x,y,讓x取最小正整數解,那么y就相應取到了最大正整數解(其實這樣說不是很准確,實際上是“有限制”的最大正整數解),然后大概可以判斷如果此時a*b<0的時候顯然有無窮多解。否則再讓y對 a/gcd(a,b) 取模得出y的最小正整數解,然后作差再除以 a/gcd(a,b) 就是y能取到的正整數解數量。至於為什么這時x也是正數,試着證一下:因為此時a,b都是同號了。假設ab同正,那么y變小的同時x是加上一個正數,不斷變大,恆正。假設ab同負,那么y變小是加上一個負數,相應的x應該減去一個負數,還是恆正。證畢。所以x是滿足要求的,只需要考慮y就行了。
代碼懶得寫了要考慮太多情況。。
T2 好題 考場上狀態定義有誤導致這東西的dp有后效性然而最后20分鍾才查出來當時正慌的一批想不出來正解了。以下正解:
定義f[i][j]表示以i為根子樹中選j個黑點所有的邊對答案的最大貢獻 其實這種考慮貢獻的題見了好多回了可每次就是想不到。有了dp方程就很好轉移了,枚舉x的當前子樹中選j個,之前的子樹中選k個,式子就能通過枚舉當前子樹中的黑點乘上外邊的黑點加上當前子樹的白點乘上外邊的白點再乘上邊權轉移了

#include<cstdio> #include<cctype> #define N 2005 #define int long long #define min(A,B) ((A)<(B)?(A):(B)) #define max(A,B) ((A)>(B)?(A):(B)) #define swap(A,B) ((A)^=(B)^=(A)^=(B)) int n,m,cnt; int sze[N],g[N][N]; int head[N],f[N][N]; struct Edge{ int to,nxt,dis; }edge[N<<1]; void add(int x,int y,int z){ edge[++cnt].to=y; edge[cnt].nxt=head[x]; edge[cnt].dis=z; head[x]=cnt; } int getint(){ int x=0,f=0;char ch=getchar(); while(!isdigit(ch)) f|=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return x; } void dp(int now){ sze[now]=1; for(int i=head[now];i;i=edge[i].nxt){ int to=edge[i].to; if(sze[to]) continue; dp(to); sze[now]+=sze[to]; for(int j=0;j<=m;j++) g[now][j]=f[now][j]; for(int j=0;j<=sze[to] and j<=m;j++){ for(int k=0;k<=sze[now]-sze[to] and k<=m;k++){ if(j+k>m) break; f[now][j+k]=max(f[now][j+k],f[to][j]+g[now][k]+j*(m-j)*edge[i].dis+(sze[to]-j)*(n-sze[to]-m+j)*edge[i].dis); } } } } signed main(){ n=getint(),m=getint(); for(int i=1;i<n;i++){ int a=getint(),b=getint(),c=getint(); add(a,b,c);add(b,a,c); } dp(1); printf("%lld\n",f[1][m]); return 0; }
T3 什么切比雪夫距離不會(×) 然而我發現網上一大半講切比雪夫距離的博客都拿邊長為1的正方形舉例子然而那實際上應該是根號二略略略
2018.7.19
開場發現三道題全被早晨zyz押中了“lyd不就考dp圖論么”%%%
T1 感覺水水dp但是死活想不出來咋做,后來有了一個n三方的做法枚舉前i個選j組上一個是k,然后發現了一個奇怪的性質選j組可以不枚舉然后就n方水過了這題

#include<cstdio> #include<cctype> #define N 5005 #define int long long #define min(A,B) ((A)<(B)?(A):(B)) #define max(A,B) ((A)>(B)?(A):(B)) #define swap(A,B) ((A)^=(B)^=(A)^=(B)) int n; int dp[N][3],val[N]; int getint(){ int x=0,f=0;char ch=getchar(); while(!isdigit(ch)) f|=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return x; } void file(){ freopen("tower.in","r",stdin); freopen("tower.out","w",stdout); } signed main(){ file(); n=getint(); for(int i=1;i<=n;i++) val[i]=val[i-1]+getint(); for(int i=1;i<=n;i++){ for(int j=0;j<i;j++){ if(val[i]-val[j]>=val[j]-val[dp[j][0]]){ if(dp[j][1]+1>dp[i][1]){ dp[i][1]=dp[j][1]+1; dp[i][0]=j; } else if(dp[j][1]+1==dp[i][1]){ dp[i][0]=max(dp[i][0],j); } } } } printf("%lld\n",n-dp[n][1]); return 0; }
T2 看完一分鍾就有思路十分鍾敲完代碼結果沒考慮這天啥也不干的情況直接干成0分我大概是個傻逼好學生天天光想着學習從不摸魚

#include<cstdio> #include<cctype> #include<cstring> #include<algorithm> #define M 105 #define N 10005 #define min(A,B) ((A)<(B)?(A):(B)) #define max(A,B) ((A)>(B)?(A):(B)) #define swap(A,B) ((A)^=(B)^=(A)^=(B)) int t,n,m; int ycl[M]; int dp[N][M]; struct W{ int c,d; friend bool operator<(W a,W b){ return a.c<b.c; } }w[N]; struct C{ int m,l,a; friend bool operator<(C x,C y){ return x.m<y.m; } }c[M]; int getint(){ int x=0,f=0;char ch=getchar(); while(!isdigit(ch)) f|=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return x; } signed main(){ freopen("wrk.in","r",stdin); freopen("wrk.out","w",stdout); t=getint(),m=getint(),n=getint(); for(int i=1;i<=m;i++){ c[i].m=getint(); c[i].l=getint(); c[i].a=getint(); } for(int i=1;i<=n;i++){ w[i].c=getint(); w[i].d=getint(); } std::sort(c+1,c+1+m); std::sort(w+1,w+1+n); memset(ycl,0x3f,sizeof ycl); for(int i=1;i<=n;i++) ycl[w[i].c]=min(ycl[w[i].c],w[i].d); for(int i=1;i<=100;i++) ycl[i]=min(ycl[i],ycl[i-1]); int now=1; memset(dp,0xcf,sizeof dp); dp[0][1]=0; for(int i=0;i<=t;i++){ for(int j=1;j<=100;j++){ dp[i+ycl[j]][j]=max(dp[i+ycl[j]][j],dp[i][j]+1); while(now<=m and c[now].m==i){ dp[i+c[now].l][c[now].a]=max(dp[i+c[now].l][c[now].a],dp[i][j]); now++; } } } int ans=0; for(int i=1;i<=100;i++) ans=max(ans,dp[t][i]); printf("%d\n",ans); return 0; }
T3 顯然二分枚舉選哪個點能包含中間那個根然后用鏈長公式什么的搞一下就行

#include<queue> #include<cstdio> #include<cctype> #include<cstring> #define N 2005 #define min(A,B) ((A)<(B)?(A):(B)) #define max(A,B) ((A)>(B)?(A):(B)) #define swap(A,B) ((A)^=(B)^=(A)^=(B)) int tot; int deg[N]; int vis[N]; int d[N],ro; int head[N]; int n,m,k,cnt; struct Edge{ int to,nxt; }edge[N<<1]; void add(int x,int y){ edge[++cnt].to=y; edge[cnt].nxt=head[x]; head[x]=cnt; } int getint(){ int x=0,f=0;char ch=getchar(); while(!isdigit(ch)) f|=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return x; } void dfs(int now){ vis[now]=1;tot++; for(int i=head[now];i;i=edge[i].nxt){ int to=edge[i].to; if(vis[to]) continue; dfs(to); } } bool check(int x){ for(int i=1;i<=n;i++){ memset(d,0,sizeof d); memset(vis,0,sizeof vis); std::queue<int> q; q.push(i); vis[i]=1; while(q.size()){ int u=q.front();q.pop(); if(d[u]==x) continue; for(int i=head[u];i;i=edge[i].nxt){ int to=edge[i].to; if(vis[to]) continue; vis[to]=1; d[to]=d[u]+1; q.push(to); } } if(!vis[ro]) continue; int now=1; for(int j=1;j<=n;j++){ if(vis[j]) continue; tot=0; dfs(j); now+=(tot-1)/(x*2+1)+1; } if(now<=k){ //printf("x=%d,i=%d,now=%d\n",x,i,now); return 1; } } return 0; } signed main(){ freopen("holes.in","r",stdin); freopen("holes.out","w",stdout); n=getint(),m=getint(),k=getint(); if(k>=n) return printf("0\n"),0; int maxn=0,minn=1e9; for(int i=1;i<=m;i++){ int a=getint(),b=getint(); add(a,b);add(b,a); deg[a]++,deg[b]++; int p=max(deg[a],deg[b]); maxn=max(maxn,p); } for(int i=1;i<=n;i++) minn=min(minn,deg[i]); if(maxn<=2){ int l=1,r=n,ans=0; while(l<=r){ int mid=l+r>>1; if((n-1)/(mid*2+1)+1<=k) ans=mid,r=mid-1; else l=mid+1; } return printf("%d\n",ans),0; } for(int i=1;i<=n;i++){ if(deg[i]>2) ro=i; } int l=1,r=n,ans=0; while(l<=r){ int mid=l+r>>1; if(check(mid)) r=mid-1,ans=mid; else l=mid+1; } printf("%d\n",ans); return 0; }
總分 100+0+100=200 本部rank3 md明明能AK的
2018.7.20
水題歡樂賽
拿到題發現都知道做法,只是T3的掃描線從沒寫過,心里問自己,我是不是要AK了(flag)
先看T1,是個線段樹維護最大子段和,還是單點修改,這不隨便做,噼里啪啦敲完順便過了拍
再看T2,裸矩乘,沒啥意義的題,全場切 (orz GXZ legend)
此時離考試結束還有兩個小時零二十分鍾,開始剛T3
原來沒寫過掃描線,但是還是聽說過怎么做的,貌似是按橫坐標排序然后縱坐標離散化線段樹維護當前縱坐標覆蓋的長度?
yy了一陣感覺很靠譜,媽媽我會掃描線了!
然后碼碼碼,碼完測一下樣例。咦,怎么wa了? 哦原來這里要加個標記。不慫,調!
碼碼碼,碼完測一下樣例。咦,怎么又wa了?哦這個標記不能這么用。調!
終於過了樣例,隨手出了組數據,成功hack掉自己的程序。原來是讀優沒判負數
就這樣調調調,突然意識到一個事情,線段樹上維護的應該是區間不應該是點,那有點麻煩啊,但是還是靠着自己強大的yy能力yy出了一個新算法
碼完過了樣例和手造數據
敲完暴力開始對拍,結果一拍一wa
此時離考試結束還有三十分鍾。
意識有點模糊了,手也有點發抖了,趕緊把自己寫的暴力加上文件扔進了考試文件夾。想着要是調不出來我就認了。
還有十五分鍾。
突然發現有個地方沒有pushdown,不動聲色的加上了pushdown,Ctrl+B編譯,測樣例,過了,測手造數據,過了。對拍,拍上了。(安排上了)
長舒一口氣,癱在了椅子上。再次證明了自己強無敵的yy能力。
考試結束發現T3莫名RE一個點 (flag)
發現數據出鍋了 造數據的來你來告訴我一條線段它tm的算不算矩形
然而最后也沒改成績
總分100+100+90=290 本部rank6 mmp
T1 線段樹支持單點修改維護最大子段和

#include<cstdio> #include<cctype> #define N 500005 #define min(A,B) ((A)<(B)?(A):(B)) #define max(A,B) ((A)>(B)?(A):(B)) #define swap(A,B) ((A)^=(B)^=(A)^=(B)) int n,m; int val[N],sum[N<<2]; int mx[N<<2],lmx[N<<2],rmx[N<<2]; struct Node{ int sum,mx,lmx,rmx; }; int getint(){ int x=0,f=0;char ch=getchar(); while(!isdigit(ch)) f|=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return f?-x:x; } void pushup(int cur){ sum[cur]=sum[cur<<1]+sum[cur<<1|1]; lmx[cur]=max(lmx[cur<<1],sum[cur<<1]+lmx[cur<<1|1]); rmx[cur]=max(rmx[cur<<1|1],sum[cur<<1|1]+rmx[cur<<1]); mx[cur]=max(sum[cur],max(lmx[cur<<1|1]+rmx[cur<<1],max(mx[cur<<1],mx[cur<<1|1]))); } void build(int cur,int l,int r){ if(l==r){ sum[cur]=mx[cur]=lmx[cur]=rmx[cur]=val[l]; return; } int mid=l+r>>1; build(cur<<1,l,mid); build(cur<<1|1,mid+1,r); pushup(cur); } Node query(int cur,int l,int r,int ql,int qr){ if(ql<=l and r<=qr) return (Node){sum[cur],mx[cur],lmx[cur],rmx[cur]}; int mid=l+r>>1; if(ql>mid) return query(cur<<1|1,mid+1,r,ql,qr); if(qr<=mid) return query(cur<<1,l,mid,ql,qr); Node a=query(cur<<1,l,mid,ql,qr); Node b=query(cur<<1|1,mid+1,r,ql,qr); Node c;c.sum=c.mx=c.lmx=c.rmx=0; c.sum=a.sum+b.sum; c.lmx=max(a.lmx,a.sum+b.lmx); c.rmx=max(b.rmx,b.sum+a.rmx); c.mx=max(c.sum,max(a.rmx+b.lmx,max(a.mx,b.mx))); return c; } void modify(int cur,int l,int r,int ql,int qr,int c){ if(ql<=l and r<=qr){ sum[cur]=lmx[cur]=rmx[cur]=mx[cur]=c; return; } int mid=l+r>>1; if(ql<=mid) modify(cur<<1,l,mid,ql,qr,c); else modify(cur<<1|1,mid+1,r,ql,qr,c); pushup(cur); } signed main(){ freopen("BRS.in","r",stdin); freopen("BRS.out","w",stdout); n=getint(),m=getint(); for(int i=1;i<=n;i++) val[i]=getint(); build(1,1,n); while(m--){ if(getint()==1){ int x=getint(),y=getint(); printf("%d\n",query(1,1,n,x,y).mx); } else{ int x=getint(),y=getint(); modify(1,1,n,x,x,y); } } return 0; }
T2 裸矩乘

#include<cstdio> #include<cctype> #include<cstring> #define N 12 #define int long long const int mod=7777777; #define min(A,B) ((A)<(B)?(A):(B)) #define max(A,B) ((A)>(B)?(A):(B)) #define swap(A,B) ((A)^=(B)^=(A)^=(B)) int n,m; int x[N]; struct Mat{ int a[N][N]; void clear(){ memset(a,0,sizeof a); } void init(){ clear(); for(int i=1;i<=m;i++) a[i][i]=1; } friend Mat operator*(Mat x,Mat y){ Mat z;z.clear(); for(int i=1;i<=m;i++){ for(int k=1;k<=m;k++){ for(int j=1;j<=m;j++) z.a[i][j]=(z.a[i][j]+x.a[i][k]*y.a[k][j]%mod)%mod; } } return z; } }cs,f; int getint(){ int x=0,f=0;char ch=getchar(); while(!isdigit(ch)) f|=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return f?-x:x; } Mat ksm(Mat x,int y){ Mat ans;ans.init(); while(y){ if(y&1) ans=ans*x; x=x*x; y>>=1; } return ans; } signed main(){ freopen("fyfy.in","r",stdin); freopen("fyfy.out","w",stdout); m=getint(),n=getint(); cs.clear();f.clear(); f.a[1][1]=1; int sum=1; for(int i=2;i<=m;i++){ f.a[1][i]=sum; sum+=f.a[1][i]; } if(n<=m-1){ printf("%lld\n",f.a[1][n+1]); return 0; } for(int i=1;i<m;i++) cs.a[i+1][i]=1; for(int i=1;i<=m;i++) cs.a[i][m]=1; f.a[1][1]=1; cs=ksm(cs,n-m+1); f=f*cs; printf("%lld\n",f.a[1][m]); return 0; }
T3 掃描線,說起來還是第一個完全靠自己學會的算法
考場代碼:

#include<cstdio> #include<cctype> #include<algorithm> #define N 505 #define int long long void pushdown(int,int,int); #define min(A,B) ((A)<(B)?(A):(B)) #define max(A,B) ((A)>(B)?(A):(B)) #define swap(A,B) ((A)^=(B)^=(A)^=(B)) int g[N]; int n,tot,pos; int now[N<<2],d[N<<2]; int sum[N<<2],flag[N<<2]; int lazy[N<<2],real[N<<2]; struct Node{ int x,y1,y2; int type; friend bool operator<(Node a,Node b){ return a.x<b.x; } }node[N<<1]; int getint(){ int x=0,f=0;char ch=getchar(); while(!isdigit(ch)) f|=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return f?-x:x; } void build(int cur,int l,int r){ real[cur]=g[r]-g[l]; if(r-l==1){ d[cur]=1; return; } int mid=l+r>>1; build(cur<<1,l,mid); build(cur<<1|1,mid,r); d[cur]=d[cur<<1]+1; } void pushup(int cur,int l,int r){ //sum[cur]=sum[cur<<1]+sum[cur<<1|1]; now[cur]=now[cur<<1]+now[cur<<1|1]; flag[cur]=min(flag[cur<<1],flag[cur<<1|1]); //printf("cur=%lld,l=%lld,r=%lld,d=%lld\n",cur,l,r,d[cur]); /*if(d[cur]!=2){ now[cur]=now[cur<<1]+now[cur<<1|1]; //printf("now1=%lld,now2=%lld,now=%lld\n",now[cur<<1],now[cur<<1|1],now[cur]); return; } //printf("flag1=%lld,flag2=%lld\n",flag[cur<<1],flag[cur<<1|1]); now[cur]=0; int mid=l+r>>1; if(flag[cur<<1] and flag[cur<<1|1]) now[cur]=real[cur]; else if(flag[cur<<1]) now[cur]=g[mid]-g[l]; else if(flag[cur<<1|1]) now[cur]=g[r]-g[mid]; else now[cur]=0;*/ } void dfs(int cur,int l,int r){ //printf("cur=%lld,l=%lld,r=%lld\n",cur,l,r); if(r-l==1){ if(flag[cur]) now[cur]=real[cur]; else now[cur]=0; return; } pushdown(cur,l,r); int mid=l+r>>1; dfs(cur<<1,l,mid); dfs(cur<<1|1,mid,r); pushup(cur,l,r); } void pushdown(int cur,int l,int r){ if(!lazy[cur]) return; int mid=l+r>>1;/* sum[cur<<1]+=(mid-l)*lazy[cur]; sum[cur<<1|1]+=(r-mid)*lazy[cur]; if(sum[cur<<1]>=mid-l) flag[cur<<1]=1,now[cur<<1]=real[cur<<1]; if(sum[cur<<1|1]>=r-mid) flag[cur<<1|1]=1,now[cur<<1|1]=real[cur<<1|1]; //if(sum[cur<<1]<mid-l) dfs(cur<<1,l,mid); //if(sum[cur<<1|1]<r-mid) dfs(cur<<1|1,mid,r);*/ flag[cur<<1]+=lazy[cur]; flag[cur<<1|1]+=lazy[cur]; lazy[cur<<1]+=lazy[cur]; lazy[cur<<1|1]+=lazy[cur]; lazy[cur]=0; } void modify(int cur,int l,int r,int ql,int qr,int c){ //printf("in::cur=%lld,l=%lld,r=%lld,ql=%lld,qr=%lld,now=%lld\n",cur,l,r,ql,qr,now[cur]); if(ql<=l and r<=qr){ if(c==1){ //sum[cur]+=r-l; lazy[cur]++; flag[cur]++; dfs(cur,l,r); //now[cur]=real[cur]; } else{ //sum[cur]-=r-l; flag[cur]--; lazy[cur]--; //if(sum[cur]<r-l) dfs(cur,l,r); } return; } int mid=l+r>>1; pushdown(cur,l,r); if(ql==mid){ modify(cur<<1|1,mid,r,ql,qr,c); goto s; } if(qr==mid){ modify(cur<<1,l,mid,ql,qr,c); goto s; } if(ql<mid) modify(cur<<1,l,mid,ql,qr,c); if(mid<qr) modify(cur<<1|1,mid,r,ql,qr,c); s:; pushup(cur,l,r); //printf("out::cur=%lld,l=%lld,r=%lld,ql=%lld,qr=%lld,now=%lld\n",cur,l,r,ql,qr,now[cur]); } int query(int cur,int l,int r,int ql,int qr){ if(ql<=l and r<=qr) return now[cur]; } signed main(){ freopen("olddriver.in","r",stdin); freopen("olddriver.out","w",stdout); n=getint(); for(int i=1;i<=n;i++){ int a=getint(),b=getint(),c=getint(),d=getint(); node[++tot].x=a;node[tot].y1=d;node[tot].y2=b;node[tot].type=1; node[++tot].x=c;node[tot].y1=d;node[tot].y2=b;node[tot].type=2; g[++pos]=b;g[++pos]=d; } std::sort(node+1,node+1+tot); std::sort(g+1,g+1+pos); int m=std::unique(g+1,g+1+pos)-g-1; for(int i=1;i<=tot;i++){ node[i].y1=std::lower_bound(g+1,g+1+m,node[i].y1)-g; node[i].y2=std::lower_bound(g+1,g+1+m,node[i].y2)-g; } build(1,1,m); int ans=0; for(int i=1;i<=tot;i++){ ans+=query(1,1,m,1,m)*(node[i].x-node[i-1].x); //printf("i=%lld,x=%lld,y2=%lld,y1=%lld\n",i,node[i].x,node[i].y2,node[i].y1); //printf("query=%lld,ans=%lld\n",query(1,1,m,1,m),ans); modify(1,1,m,node[i].y2,node[i].y1,node[i].type); } printf("%lld\n",ans); return 0; }
2018.7.23
話說上次寫的時候沒剎住車直接飆成游記了啊。。。
為了節省時間以后就考場上的考試過程就少寫點啊
拿到題看T1,一眼性質題,楞推了二十分鍾大概有個O(nm)的做法,本機測一發極限數據發現要4.3s,感覺要涼,再一看時間限制6s,瞬間穩得不行。去看T2,發現是牛客比賽原題,當時zyz打了這個比賽還跟我炫耀做出來這題來着。。。知道了結論直接敲了個n三方上去。此時剛過了五十分鍾,開始剛T3。過程略。
考試結束,發現自己得的所有分是考試開始前一個小時得的mmp。
總分100+100+0=200,本部 rank2
T1 性質題,前綴和優化一下就行了

#include<ctime> #include<cstdio> #include<cctype> #define int long long const int N=2005; #define min(A,B) ((A)<(B)?(A):(B)) #define max(A,B) ((A)>(B)?(A):(B)) #define swap(A,B) ((A)^=(B)^=(A)^=(B)) int n,m; int a[N][N],b[N][N]; int qzha[N][N],qzhb[N][N]; int getint(){ int x=0,f=0;char ch=getchar(); while(!isdigit(ch)) f|=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return f?-x:x; } void file(){ freopen("matrix.in","r",stdin); freopen("matrix.out","w",stdout); } signed main(){ file(); n=getint(),m=getint(); for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ a[i][j]=getint(); qzha[i][j]=qzha[i-1][j]+a[i][j]; } } for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ b[i][j]=getint(); qzhb[i][j]=qzhb[i][j-1]+b[i][j]; } } while(m--){ int c=getint(),d=getint(),x=getint(),y=getint(),ans=0; int cc=min(c,x),xx=max(c,x),dd=min(d,y),yy=max(d,y); for(int i=1;i<=n;i++) ans+=(qzha[xx][i]-qzha[cc-1][i])*(qzhb[i][yy]-qzhb[i][dd-1]); printf("%lld\n",ans); } return 0; }
T2 性質題,集合點只可能在所有的橫坐標和縱坐標中。

#include<cstdio> #include<cctype> #include<cstring> #include<algorithm> #define N 55 #define min(A,B) ((A)<(B)?(A):(B)) #define max(A,B) ((A)>(B)?(A):(B)) #define swap(A,B) ((A)^=(B)^=(A)^=(B)) inline int abs(int x){return x<0?-x:x;} int n; int dis[N]; int ans[N]; int x[N],y[N]; int lenx,leny; int nx[N],ny[N]; int xx[N*N],yy[N*N]; int getint(){ int x=0,f=0;char ch=getchar(); while(!isdigit(ch)) f|=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return x; } void file(){ freopen("tower.in","r",stdin); freopen("tower.out","w",stdout); } signed main(){ file(); n=getint(); for(int i=1;i<=n;i++){ x[i]=getint(),y[i]=getint(); nx[i]=x[i];ny[i]=y[i]; } std::sort(nx+1,nx+1+n); std::sort(ny+1,ny+1+n); lenx=std::unique(nx+1,nx+1+n)-nx-1; leny=std::unique(ny+1,ny+1+n)-ny-1; memset(ans,0x3f,sizeof ans); for(int i=1;i<=lenx;i++){ for(int j=1;j<=leny;j++){ for(int p=1;p<=n;p++) dis[p]=abs(nx[i]-x[p])+abs(ny[j]-y[p]); std::sort(dis+1,dis+1+n); int sum=0; for(int p=1;p<=n;p++){ sum+=dis[p]; ans[p]=min(ans[p],sum); } } } for(int i=1;i<=n;i++) printf("%d\n",ans[i]); return 0; }
T3 最大匹配的話直接網絡流二分圖什么的都能求,但是方案就不行了,又因為給出的是一棵樹,我們考慮樹形DP。
定義 f[i][0/1],g[i][0/1]分別表示以點i為根的子樹中,選了/不選點i的最大匹配數與其方案。如果不選i的話,就需要在它的所有兒子里求和,即 $\sum \max(f[to][0],f[to][1])$ 咦不是不用Latex么
別的也是一樣更新就好
考場上的主要障礙是一直局限在樹形DP的老套路里,就是D完一個兒子之后立馬更新答案,其實可以把所有兒子都DP完之后再統一更新。再寫個高精啥的就行了。

#include<cstdio> #include<cctype> #include<cstring> #define N 1005 #define int long long #define min(A,B) ((A)<(B)?(A):(B)) #define max(A,B) ((A)>(B)?(A):(B)) #define swap(A,B) ((A)^=(B)^=(A)^=(B)) int stk[N],top; int n,cnt,d[N]; int f[N][5],tot; int qzh[N],hzh[N]; int head[N],sze[N]; struct BigInteger{ int a[205]; void clear(){ memset(a,0,sizeof a); } friend BigInteger operator+(BigInteger x,BigInteger y){ BigInteger z; z.clear(); int jw=0; for(int i=1;i<=100;i++){ z.a[i]=x.a[i]+y.a[i]+jw; jw=z.a[i]/10; z.a[i]%=10; } return z; } friend BigInteger operator*(BigInteger x,BigInteger y){ BigInteger z; z.clear(); for(int i=1;i<=100;i++){ int jw=0; for(int j=1;j<=100;j++){ z.a[i+j-1]+=x.a[i]*y.a[j]+jw; jw=z.a[i+j-1]/10; z.a[i+j-1]%=10; } z.a[i+100]=jw; } return z; } void print(){ int flag=0; for(int i=100;i;i--){ if(flag or a[i]) printf("%d",a[i]),flag=1; } if(!flag) printf("0"); } }g[N][5]; struct Edge{ int to,nxt; }edge[N<<1]; void add(int x,int y){ edge[++cnt].to=y; edge[cnt].nxt=head[x]; head[x]=cnt; } int getint(){ int x=0,f=0;char ch=getchar(); while(!isdigit(ch)) f|=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return x; } void dfs(int now){ sze[now]=1; for(int i=head[now];i;i=edge[i].nxt) dfs(edge[i].to),sze[now]+=sze[edge[i].to]; for(int i=head[now];i;i=edge[i].nxt){ int to=edge[i].to; if(max(f[to][0],f[to][1])==0) continue; f[now][0]+=max(f[to][0],f[to][1]); if(f[to][0]==f[to][1]) g[now][0]=g[now][0]*(g[to][0]+g[to][1]); else if(f[to][0]>f[to][1]) g[now][0]=g[now][0]*g[to][0]; else g[now][0]=g[now][0]*g[to][1]; } for(int i=head[now];i;i=edge[i].nxt){ int to=edge[i].to; int nnow=f[to][0]+1; BigInteger ans=g[to][0]; for(int j=head[now];j;j=edge[j].nxt){ int too=edge[j].to; if(to==too) continue; if(max(f[too][0],f[too][1])==0) continue; nnow+=max(f[too][0],f[too][1]); if(f[too][0]==f[too][1]) ans=ans*(g[too][0]+g[too][1]); else if(f[too][0]>f[too][1]) ans=ans*g[too][0]; else ans=ans*g[too][1]; } if(nnow>f[now][1]) f[now][1]=nnow,g[now][1]=ans; else if(nnow==f[now][1]) g[now][1]=g[now][1]+ans; } } signed main(){ n=getint(); for(int i=1;i<=n;i++){ int x=getint(),y=getint(); g[x][0].clear();g[x][1].clear(); g[x][0].a[1]=1; g[x][1].a[1]=1; while(y--){ int z=getint(); add(x,z); d[z]++; } } int root; for(int i=1;i<=n;i++){ if(!d[i]) root=i; } dfs(root); printf("%lld\n",max(f[root][0],f[root][1])); BigInteger ans;ans.clear(); if(f[root][0]==f[root][1]) ans=g[root][0]+g[root][1]; else if(f[root][0]>f[root][1]) ans=g[root][0]; else ans=g[root][1]; ans.print(); return 0; }
2018.8.6
開場看T1發現是道輸入極其惡心的模擬題,眼殘把題目中保證沒有兩個水管在同一高度看成了不保證。。於是題目變得十分復雜起來,硬推了一個小時把如何將這種很復雜的情況考慮進去想清楚了,然后敲+調了一個小時。花了兩個小時做了一道半個小時就能寫完的題。然后看T2發現是一道區間DP,但是數組開不下,光是狀態數就有1e9。想盡各種辦法優化都無能為力,突然看到部分分是跟m有關,於是想到離散化,那么就可做了。然后調調調最后一分鍾才調出來也是緊張+刺激。
但是因為姿勢原因TLE了一個點,實際上不用離散化的。 T3略,只是穩拿20分變成10分有點傷心。
總分 100+100+10=210 本部rank1,全場rank8
T1 直接放考場代碼了,支持更加復雜的情況

#include<cstdio> #include<cctype> #include<vector> #include<cstring> #include<iostream> #include<algorithm> #define N 1005 #define M 120000 int a[M]; int n,m,tot; void dfs(int); int zbx[M][3]; // 1->up 2->down int zby[M][3]; // 1->left 2->right char ch[N][N]; int cme[M],dwn[M]; int sze[M],str[M]; int qzh1[M],qzh2[M]; struct Node{ int son,h; }b[M]; std::vector<Node> v[M]; bool cmp(Node x,Node y){ return x.h>y.h; } bool cmp2(Node x,Node y){ return x.son<y.son; } int getint(){ int x=0,f=0;char ch=getchar(); while(!isdigit(ch)) f|=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return f?-x:x; } int getnum(int x,int y){ int ans=0; for(int i=y;i;i++){ if(!isdigit(ch[x][i])) return ans; ans=ans*10+ch[x][i]-'0'; } } void file(){ freopen("clickbait.in","r",stdin); freopen("clickbait.out","w",stdout); } //dfs2->find the index of one's son //fx: 1->left 2->right 3->down int dfs2(int x,int y,int fx){ // printf("x=%d,y=%d,fx=%d\n",x,y,fx); if(ch[x][y]=='+'){ if(fx==1 or fx==2) return dfs2(x+1,y,3); else{ if(ch[x][y+1]=='-') return dfs2(x,y+1,2); else return dfs2(x,y-1,1); } } int a,b,c,d; if(fx==3 and ch[x+1][y]=='-'){ for(int i=y;i;i--){ if(ch[x+1][i]=='+'){ a=i; break; } } for(int i=y;i;i++){ if(ch[x+1][i]=='+'){ b=i; break; } } for(int i=x+2;i;i++){ if(ch[i][a]=='+'){ c=i; break; } } int num=0; for(int i=x+1;i<=c;i++){ for(int j=a;j<=b;j++){ if(isdigit(ch[i][j])){ num=getnum(i,j); break; } } if(num) break; } zbx[num][1]=x+1;zbx[num][2]=c; zby[num][1]=a;zby[num][2]=b; tot++; dfs(num); return num; } if(fx==1) return dfs2(x,y-1,fx); if(fx==2) return dfs2(x,y+1,fx); if(fx==3) return dfs2(x+1,y,fx); } void dfs(int now){ sze[now]=(zbx[now][2]-zbx[now][1])*(zby[now][2]-zby[now][1]); for(int i=zbx[now][1];i<=zbx[now][2];i++){ if(ch[i][zby[now][1]-1]=='-'){ int to=dfs2(i,zby[now][1]-1,1); v[now].push_back((Node){to,i}); str[now]+=str[to]+zbx[to][1]-i; sze[now]+=sze[to]; } } for(int i=zbx[now][1];i<=zbx[now][2];i++){ if(ch[i][zby[now][2]+1]=='-'){ int to=dfs2(i,zby[now][2]+1,2); v[now].push_back((Node){to,i}); str[now]+=str[to]+zbx[to][1]-i; sze[now]+=sze[to]; } } std::sort(v[now].begin(),v[now].end(),cmp); qzh1[0]=qzh2[0]=0; for(int i=0;i<v[now].size();i++){ int to=v[now][i].son; //printf("to=%d,sze=%d,str=%d\n",to,sze[to],str[to]); if(i==0) qzh1[0]=sze[to],qzh2[0]=str[to]; else{ if(v[now][i].h==v[now][i-1].h){ a[to]+=qzh1[i-2]+qzh2[i-2]; qzh1[i]=qzh1[i-1]+sze[to]; qzh2[i]=qzh2[i-1]+str[to]; } else{ a[to]+=qzh1[i-1]+qzh2[i-1]; qzh1[i]=qzh1[i-1]+sze[to]; qzh2[i]=qzh2[i-1]+str[to]; } } b[to].son=sze[to]+str[to]; b[to].h=to; } } void print(int x){ printf("x=%d\n",x); printf("zbx1=%d,zbx2=%d,zby1=%d,zby2=%d\n",zbx[x][1],zbx[x][2],zby[x][1],zby[x][2]); } void dfs3(int now){ for(int i=0;i<v[now].size();i++){ int to=v[now][i].son; a[to]+=a[now]; b[to].son+=a[to]; dfs3(to); } } signed main(){ file(); scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%s",ch[i]+1); int starx=0,stary=0; for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ if(ch[i][j]=='+'){ starx=i; stary=j; break; } } if(starx) break; } zbx[1][1]=starx;zby[1][1]=stary; for(int i=stary+1;i<=m;i++){ if(ch[starx][i]=='+'){ zby[1][2]=i; break; } } for(int i=starx+1;i<=n;i++){ if(ch[i][stary]=='+'){ zbx[1][2]=i; break; } } tot=1; dfs(1); dfs3(1); std::sort(b+1,b+1+tot,cmp2); //for(int i=1;i<=tot;i++) // printf("i=%d,a=%d,b=%d\n",i,a[i],b[i].son); for(int i=2;i<=tot;i++) printf("%d\n",b[i].h); printf("1"); return 0; }
T2 放這個垃圾版本的區間DP,實際上不用離散化,把精靈當做狀態而不是把房子當做狀態更加優秀。

%:pragma GCC optimize(2) #include<cstdio> #include<cctype> #include<vector> #include<cstring> #include<iostream> #include<algorithm> #define M 105 #define re register #define in inline int g[M],tot; int fz[M][M]; int qzh[M][M]; int vis[M][4005]; int n,m,k,maxn,len; int f[2005][M][M][2]; struct Node{ int a,b,t; friend bool operator<(Node x,Node y){ return x.t<y.t; } }node[M]; std::vector<Node> v[M]; in int getint(){ int x=0,f=0;char ch=getchar(); while(!isdigit(ch)) f|=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return f?-x:x; } in void file(){ freopen("go.in","r",stdin); freopen("go.out","w",stdout); } in int bsearch(int d,int l,int r,int x){ int ans=r; while(l<=r){ int mid=l+r>>1; if(fz[d][mid]>=x) ans=mid-1,r=mid-1; else l=mid+1; } //printf("l=%d,r=%d,d=%d,x=%d,ans=%d\n",l,r,d,x,ans); return ans; } in int calc(int x,int y){ if(~vis[x][y]) return vis[x][y]; if(!fz[x][0]) return vis[x][y]=0; int it=bsearch(x,1,fz[x][0],y); return vis[x][y]=qzh[x][fz[x][0]]-qzh[x][it]; } signed main(){ file(); n=getint(),k=getint(),m=getint(); for(re int i=1;i<=m;i++){ node[i].a=getint(),node[i].b=getint(),node[i].t=getint(); g[++tot]=node[i].a; maxn=std::max(maxn,node[i].t); } g[++tot]=k; std::sort(g+1,g+1+tot); len=std::unique(g+1,g+1+tot)-g-1; k=std::lower_bound(g+1,g+1+tot,k)-g; for(re int i=1;i<=m;i++){ node[i].a=std::lower_bound(g+1,g+1+len,node[i].a)-g; v[node[i].a].push_back((Node){node[i].a,node[i].b,node[i].t}); fz[node[i].a][++fz[node[i].a][0]]=node[i].t; } for(re int i=1;i<=len;i++) std::sort(fz[i]+1,fz[i]+1+fz[i][0]),std::sort(v[i].begin(),v[i].end()); for(re int i=1;i<=len;i++){ if(!fz[i][0]) continue; for(re int j=1;j<=fz[i][0];j++) qzh[i][j]=qzh[i][j-1]+v[i][j-1].b; } memset(vis,-1,sizeof vis); memset(f,0xcf,sizeof f); int ans=0; f[1][k][k][0]=calc(k,1); for(re int i=1;i<=maxn;i++){ for(re int j=1;j<=len;j++){ for(re int p=j;p<=len;p++){ //f[i][j][p][0/1]->f[i+1][j-1][p][0] or f[i+1][j][p+1][1] f[i+1][j][p][0]=std::max(f[i+1][j][p][0],f[i][j][p][0]); f[i+1][j][p][1]=std::max(f[i+1][j][p][1],f[i][j][p][1]); ans=std::max(ans,std::max(f[i+1][j][p][0],f[i+1][j][p][1])); for(int at=0;at<=1;at++){ int now=at?p:j; if(j-1>=1){ if(i+g[now]-g[j-1]<=maxn) f[i+g[now]-g[j-1]][j-1][p][0]=std::max(f[i+g[now]-g[j-1]][j-1][p][0],f[i][j][p][at]+calc(j-1,i+g[now]-g[j-1])); } if(p+1<=len){ if(i+g[p+1]-g[now]<=maxn) f[i+g[p+1]-g[now]][j][p+1][1]=std::max(f[i+g[p+1]-g[now]][j][p+1][1],f[i][j][p][at]+calc(p+1,i+g[p+1]-g[now])); } } } } } printf("%d\n",ans); return 0; }
然后是B組題解。
T1 觀察到有用的點只有k+2個,於是在求完這些點兩兩之間最短路之后,狀壓一下就行了。注意k=0的情況要特判一下。

#include<queue> #include<cstdio> #include<cctype> #include<cstring> #include<iostream> #define N 50005 #define int long long int is[12]; int f[12][1<<12]; int dis[N],vis[N]; int n,m,k,s,t,cnt; int head[N],d[15][15]; struct Edge{ int to,nxt,dis; }edge[N<<1]; struct Node{ int now,dis; friend bool operator<(Node x,Node y){ return x.dis>y.dis; } }; void add(int x,int y,int z){ edge[++cnt].to=y; edge[cnt].nxt=head[x]; edge[cnt].dis=z; head[x]=cnt; } int getint(){ int x=0,f=0;char ch=getchar(); while(!isdigit(ch)) f|=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return f?-x:x; } bool flag=0; void dijkstra(int fr,int id){ memset(vis,0,sizeof vis); for(int i=1;i<=n;i++) dis[i]=2e18;dis[fr]=0; std::priority_queue<Node> pq;pq.push((Node){fr,0}); while(pq.size()){ int u=pq.top().now;pq.pop(); if(vis[u]) continue; vis[u]=1; for(int i=head[u];i;i=edge[i].nxt){ int to=edge[i].to; if(dis[to]>dis[u]+edge[i].dis){ dis[to]=dis[u]+edge[i].dis; pq.push((Node){to,dis[to]}); } } } for(int i=1;i<=k;i++) d[id][i]=dis[is[i]]; d[id][k+2]=dis[t]; if(fr==s and dis[t]>=2e18) printf("-1"),flag=1; return; } signed main(){ //freopen("in.txt","r",stdin); n=getint(),m=getint(),k=getint(),s=getint(),t=getint(); for(int i=1;i<=m;i++){ int x=getint(),y=getint(),z=getint(); add(x,y,z); } for(int i=1;i<=k;i++) is[i]=getint(); for(int i=1;i<=k;i++) dijkstra(is[i],i); dijkstra(s,k+1); if(flag) return 0; if(!k){ printf("%lld\n",dis[t]); return 0; } int maxn=1<<k; for(int i=1;i<=k;i++){ for(int j=1;j<maxn;j++) f[i][j]=2e18; } for(int i=1;i<=k;i++) f[i][1<<i-1]=d[k+1][i]; for(int i=1;i<maxn;i++){ for(int j=1;j<=k;j++){ if(i&(1<<j-1)){ for(int p=1;p<=k;p++){ if(!(i&(1<<p-1))) f[p][i|(1<<p-1)]=std::min(f[p][i|(1<<p-1)],f[j][i]+d[j][p]); } } } } int ans=2e18; for(int i=1;i<=k;i++) f[i][maxn-1]+=d[i][k+2]; for(int i=1;i<=k;i++) ans=std::min(ans,f[i][maxn-1]); printf("%lld\n",ans); return 0; }
T2 對於C操作,貪心的都先選上,然后放進一個小根堆里目的是為了支持撤銷,如果遇到E操作就從小根堆里撤銷即可。

#include<queue> #include<cstdio> #include<cctype> #include<cstring> #define N 200005 int n,ans; std::priority_queue<int,std::vector<int>,std::greater<int> > pq; int getint(){ int x=0,f=0;char ch=getchar(); while(!isdigit(ch)) f|=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return f?-x:x; } signed main(){ n=getint(); for(int i=1;i<=n;i++){ char ch[10];scanf("%s",ch); int x=getint(); if(ch[0]=='c'){ ans+=x;pq.push(x); } else if(i!=n){ while(pq.size()>=x){ int t=pq.top();pq.pop(); ans-=t; } } else{ if(pq.size()>=x) printf("%d\n",ans); else printf("-1"); } } return 0; }
T3 先用組合數求出三角形的情況,然后再減去三點共線的所有情況就好。如何求三點共線的情況呢?對於每個點,求出其它點與它的斜率,對於有同一斜率的m個點,那么關於這個點重復計算的個數就是C(m,2)。
但是這樣會減重,只需要內層循環從1到i即可。

#include<cstdio> #include<cctype> #include<cstring> #include<algorithm> #define N 3005 #define db double #define ll long long ll ans; db fz[N]; db x[N],y[N]; db h[N],z[N]; int n,tot,aa,bb; int getint(){ int x=0,f=0;char ch=getchar(); while(!isdigit(ch)) f|=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return f?-x:x; } void work(db *a,int m){ db last=-1e9;int now=0; for(int i=1;i<=m;i++){ if(a[i]==last) now++; else{ if(now<2){ last=a[i]; now=1; } else{ ans-=(ll)now*(now-1)/2; last=a[i]; now=1; } } } if(now>=2) ans-=(ll)now*(now-1)/2; } signed main(){ freopen("triangle.in","r",stdin); freopen("triangle.out","w",stdout); n=getint(); for(int i=1;i<=n;i++) scanf("%lf%lf",&x[i],&y[i]); ans=(ll)n*(n-1)*(n-2)/6; for(int i=1;i<=n;i++){ tot=0; aa=0; bb=0; for(int j=1;j<i;j++){ if(x[i]!=x[j]){ if(y[i]==y[j]) h[++aa]=y[i]; else{ db a=(y[i]-y[j])/(x[i]-x[j]); fz[++tot]=a; } } else z[++bb]=x[i]; } std::sort(h+1,h+1+aa); std::sort(z+1,z+1+bb); std::sort(fz+1,fz+1+tot); work(h,aa);work(z,bb);work(fz,tot); } printf("%lld\n",ans); return 0; }
2018.8.8
開場看T1T2發現都不會,感覺T3是最可做的,就去剛T3。想了一個前綴和+並查集的方法,結果發現這題喪心病狂的卡空間。突然沒了思路,於是轉過頭去看T1,發現是道博弈,但是沒想到DP的做法,於是寫了個記搜。然后看T2,是道巨惡心的模擬+爆搜,不想做,於是去敲T3的暴力。想了個分塊+並查集+二分答案的做法,復雜度1e8,很穩,敲完調完還剩四十分鍾。急忙轉過頭去寫T2。本來以為T1和T3能拿很多分的,想着T2調不出來我也就認了,沒想到寫完之后看一會就找到了一個錯,再看一會又找到了一個錯。沒多久就過了所有樣例。
出成績 80+50+20=150,本部rank2,全場有點慘,rank16.
T1 這玩意可以DP做,然后后綴和優化一下就行了

#include<cstdio> #include<cctype> #include<cstring> #include<iostream> #define N 5005 using std::max; using std::min; int n,m,k; int ans[N]; int val[N<<1]; int dp[2][N]; int qzh[2][N]; int getint(){ int x=0,f=0;char ch=getchar(); while(!isdigit(ch)) f|=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return f?-x:x; } signed main(){ freopen("vode.in","r",stdin);freopen("vode.out","w",stdout); n=getint(),m=getint(),k=getint(); for(int i=1;i<=n;i++) val[i]=getint(); for(int i=n+1;i<=n+m;i++) val[i]=val[i-n]; int cur=0; for(int i=n+m;i;i--){ for(int j=m-1;j;j--){ int r=min(j+k,m-1); if(val[i]==val[i+1]){ if(qzh[cur^1][j+1]-qzh[cur^1][r+1]>0) dp[cur][j]=1; else dp[cur][j]=0; } else{ if(qzh[cur^1][j+1]-qzh[cur^1][r+1]>0) dp[cur][j]=0; else dp[cur][j]=1; } qzh[cur][j]=qzh[cur][j+1]+dp[cur][j]; } cur^=1; memset(qzh[cur],0,sizeof qzh[cur]); if(i<=n){ for(int j=1;j<=k;j++) ans[i]|=dp[cur^1][j]; } } for(int i=1;i<=n;i++){ printf("%d ",ans[i]?val[i]:val[i]^1); } return 0; }
T2 正解是最短路,正常走路操作向四個方向連邊權為1的邊,向四個方向最近的牆連邊權為到最近的牆的距離的邊,然后跑最短路即可。

#include<queue> #include<cstdio> #include<cctype> #include<cstring> #include<iostream> #define N 505 using std::min; using std::max; int n,m,s,t; int dis[N*N]; int vis[N*N]; char ch[N][N]; int head[N*N],cnt; int upx[5][N][N],upy[5][N][N]; int dx[]={-1,0,1,0},dy[]={0,-1,0,1}; inline int abs(int x){return x<0?-x:x;} inline int idx(int x,int y){return (x-1)*n+y;} struct Edge{ int to,nxt,dis; }edge[N*N*10]; struct Node{ int now,val; friend bool operator<(Node x,Node y){ return x.val>y.val; } }; std::priority_queue<Node> pq; void dijkstra(){ pq.push((Node){s,0});dis[s]=0; while(pq.size()){ int u=pq.top().now;pq.pop(); if(vis[u]) continue; for(int i=head[u];i;i=edge[i].nxt){ int to=edge[i].to; if(dis[to]>dis[u]+edge[i].dis){ dis[to]=dis[u]+edge[i].dis; pq.push((Node){to,dis[to]}); } } } } void add(int x,int y,int z){ edge[++cnt].to=y; edge[cnt].nxt=head[x]; edge[cnt].dis=z; head[x]=cnt; } int getint(){ int x=0,f=0;char ch=getchar(); while(!isdigit(ch)) f|=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return f?-x:x; } signed main(){ freopen("portal.in","r",stdin);freopen("portal.out","w",stdout); n=getint(),m=getint(); memset(dis,0x3f,sizeof dis); for(int i=1;i<=n;i++){ scanf("%s",ch[i]+1); for(int j=1;j<=m;j++){ if(ch[i][j]=='C') s=idx(i,j); if(ch[i][j]=='F') t=idx(i,j); if(i-1>0 and ch[i-1][j]!='#') upx[0][i][j]=upx[0][i-1][j],upy[0][i][j]=upy[0][i-1][j]; else upx[0][i][j]=i-1,upy[0][i][j]=j; if(j-1>0 and ch[i][j-1]!='#') upx[1][i][j]=upx[1][i][j-1],upy[1][i][j]=upy[1][i][j-1]; else upx[1][i][j]=i,upy[1][i][j]=j-1; } } for(int i=n;i;i--){ for(int j=m;j;j--){ if(i+1<=n and ch[i+1][j]!='#') upx[2][i][j]=upx[2][i+1][j],upy[2][i][j]=upy[2][i+1][j]; else upx[2][i][j]=i+1,upy[2][i][j]=j; if(j+1<=m and ch[i][j+1]!='#') upx[3][i][j]=upx[3][i][j+1],upy[3][i][j]=upy[3][i][j+1]; else upx[3][i][j]=i,upy[3][i][j]=j+1; } } for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ if(ch[i][j]=='#') continue; for(int k=0;k<4;k++){ int nx=i+dx[k]; int ny=j+dy[k]; if(nx>0 and nx<=n and ny>0 and ny<=m and ch[nx][ny]!='#') add(idx(i,j),idx(nx,ny),1); } int ans=1e9; for(int k=0;k<4;k++) ans=min(ans,abs(upx[k][i][j]-i)+abs(upy[k][i][j]-j)); for(int k=0;k<4;k++){ int aaa; if(k==0 or k==2) aaa=2-k; if(k==1 or k==3) aaa=4-k; int nx=upx[k][i][j]+dx[aaa]; int ny=upy[k][i][j]+dy[aaa]; add(idx(i,j),idx(nx,ny),ans); } } } dijkstra(); printf(dis[t]>=0x3f3f3f3f?"nemoguce":"%d\n",dis[t]); return 0; }
T3 按秩合並的並查集,觀察到一個性質就是按秩合並的並查集樹高是 $O(\log n)$ 級別的,所以可以建出一棵帶權並查集樹,邊權為這兩個集合相連的最早時間。對於每個詢問,找出它們路徑上的最大值即可。

#include<cstdio> #include<cctype> #include<cstring> #include<iostream> #define N 100005 using std::max; using std::swap; int n,m,q,dis[N],h[N]; int head[N],cnt,f[N][22]; int father[N],d[N],mx[N][22]; struct Edge{ int to,nxt,dis; }edge[N]; void add(int x,int y,int z){ edge[++cnt].to=y; edge[cnt].nxt=head[x]; edge[cnt].dis=z; head[x]=cnt; } int find(int x){ if(father[x]==x) return x; return find(father[x]); } int getint(){ int x=0,f=0;char ch=getchar(); while(!isdigit(ch)) f|=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return f?-x:x; } void dfs(int now){ for(int i=head[now];i;i=edge[i].nxt){ int to=edge[i].to; f[to][0]=now; d[to]=d[now]+1; mx[to][0]=edge[i].dis; for(int i=1;i<=20;i++){ f[to][i]=f[ f[to][i-1] ][i-1]; mx[to][i]=max(mx[to][i-1],mx[f[to][i-1]][i-1]); } dfs(to); } } int query(int x,int y){ if(d[x]<d[y]) swap(x,y); int ans=0; for(int i=20;~i;i--){ if(d[f[x][i]]>=d[y]) ans=max(ans,mx[x][i]),x=f[x][i]; } if(x==y) return ans; for(int i=20;~i;i--){ if(f[x][i]!=f[y][i]){ ans=max(ans,max(mx[x][i],mx[y][i])); x=f[x][i],y=f[y][i]; } } return max(ans,max(mx[x][0],mx[y][0])); } signed main(){ freopen("pictionary.in","r",stdin); freopen("pictionary.out","w",stdout); n=getint(),m=getint(),q=getint(); for(int i=1;i<=n;i++) father[i]=i,h[i]=1; for(int i=1;i<=m;i++){ int x=m-i+1; for(int j=2;j;j++){ if(j*x>n) break; int r1=find(x); int r2=find(j*x); if(r1==r2) continue; if(h[r1]<h[r2]) swap(r1,r2); h[r1]=max(h[r1],h[r2]+1); father[r2]=r1; add(r1,r2,i); } } int x=find(1); dfs(x); while(q--){ int x=getint(),y=getint(); printf("%d\n",query(x,y)); } return 0; }
B組
T1 一眼最大生成樹,然后再跑一個最小生成樹就行了。

#include<cstdio> #include<cctype> #include<cstring> #include<algorithm> #define N 50005 #define db double db xx[N]; int fr[N]; int n,m,s,t; int father[N]; struct Edge{ int x,y; db dis; friend bool operator<(Edge a,Edge b){ return a.dis<b.dis; } }node[100005]; int getint(){ int x=0,f=0;char ch=getchar(); while(!isdigit(ch)) f|=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return f?-x:x; } int find(int x){ if(father[x]==x) return x; return father[x]=find(father[x]); } signed main(){ freopen("trip.in","r",stdin);freopen("trip.out","w",stdout); n=getint(),m=getint(); for(int i=1;i<=m;i++){ node[i].x=getint(); node[i].y=getint(); scanf("%lf",&node[i].dis); } int tot=0; for(int i=1;i<=n;i++){ int p=getint();tot+=p; while(p--) fr[getint()]=i; } for(int i=1;i<=n;i++) scanf("%lf",&xx[i]); for(int i=1;i<=m;i++){ if(fr[node[i].x]==fr[node[i].y]) node[i].dis=node[i].dis*xx[fr[node[i].x]]/100.0; else node[i].dis=node[i].dis*(xx[fr[node[i].x]]+xx[fr[node[i].y]])/200.0; } s=getint(),t=getint(); std::sort(node+1,node+1+m); for(int i=1;i<=tot;i++) father[i]=i; // for(int i=1;i<=m;i++) // printf("i=%d,x=%d,y=%d,dis=%.3lf\n",i,node[i].x,node[i].y,node[i].dis); int l,r; for(int i=m;i;i--){ int r1=find(node[i].x); int r2=find(node[i].y); if(r1==r2) continue; father[r1]=r2; if(find(s)==find(t)){ l=node[i].dis; for(int j=1;j<=tot;j++) father[j]=j; int idx; for(int j=i;j;j--){ if(node[j].dis<l) break; idx=j; } //printf("idx=%d\n",idx); int r1=find(node[i].x),r2=find(node[i].y); father[r1]=r2; if(find(s)==find(t)){ int q=node[i].dis; if(q-node[i].dis==0) r=l; else r=l+1; printf("%d %d\n",l,r); return 0; } for(int j=idx;j<=m;j++){ int r1=find(node[j].x); int r2=find(node[j].y); if(r1==r2) continue; father[r1]=r2; if(find(s)==find(t)){ int q=node[j].dis; if(q-node[j].dis==0) r=node[j].dis; else r=node[j].dis+1; printf("%d %d\n",l,r); return 0; } } } } }
T2
咕咕咕
T3 數學題,用van老師和特沈本教的方法推一波公式即可。注意開long long

#include<cstdio> #include<cctype> #include<cstring> #include<algorithm> const int N=1e7; #define ll long long #define int long long ll n; ll c[N]; int getint(){ int x=0,f=0;char ch=getchar(); while(!isdigit(ch)) f|=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return f?-x:x; } signed main(){ freopen("math.in","r",stdin);freopen("math.out","w",stdout); n=getint(); if(n==0 or n==1 or n==2){puts("0");return 0;} for(int i=1;i*i<=n;i++){ if(n%i) continue; if(n/i&1) c[++c[0]]=n-i; if(i*i!=n and i!=1 and i&1) c[++c[0]]=n-n/i; } std::sort(c+1,c+1+c[0]); printf("%lld ",c[0]); for(int i=1;i<=c[0];i++) printf("%lld ",c[i]); return 0; }
2018.8.10
開場看T1發現要求一條路徑上線段的交的最大值,想了20min寫了個自認為是正解的爆搜。然后看T2,想了會寫了個Trie樹,過了大樣例及1w組對拍。此時剛剛過去一個半小時,T3顯然不可做題,於是開始頹。
一個小時之后,想着測一下T1的極限數據,發現要跑12s,突然慌得一批。之后的一個小時因為想不出T1正解就一直在卡常,感覺只能拿30分,沒想到直接得了90分,數據有點水(后來好像改數據T成70分了)
總分70+100+0=170,本部rank2,全場rank16(一堆省選組的神仙來虐場%%%)
T1 這東西枚舉答案的左端點然后二分右端點bfs判斷即可。

#include<queue> #include<cstdio> #include<cctype> #include<cstring> #include<algorithm> #define N 1005 #define M 3005 int n,m,cnt; int g[M<<1],tot; int head[N],vis[N]; struct Edge{ int to,nxt,l,r; }edge[M<<1]; void add(int x,int y,int a,int b){ edge[++cnt].to=y; edge[cnt].nxt=head[x]; edge[cnt].l=a; edge[cnt].r=b; head[x]=cnt; } int getint(){ int x=0,f=0;char ch=getchar(); while(!isdigit(ch)) f|=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return f?-x:x; } bool check(int l,int r){ std::queue<int> q; q.push(1);memset(vis,0,sizeof vis);vis[1]=1; //printf("l=%d,r=%d\n",l,r); while(q.size()){ int u=q.front();q.pop();//printf("u=%d\n",u); for(int i=head[u];i;i=edge[i].nxt){ int to=edge[i].to; if(vis[to]) continue; if(edge[i].l>l or edge[i].r<r) continue; q.push(to);vis[to]=1;//printf("to=%d\n",to); } } return vis[n]; } void write(int x){ if(x>9) write(x/10); putchar(x%10+'0'); } signed main(){ freopen("travel.in","r",stdin);freopen("travel.out","w",stdout); n=getint(),m=getint(); for(int i=1;i<=m;i++){ int a=getint(),b=getint(),c=getint(),d=getint(); add(a,b,c,d);add(b,a,c,d); g[++tot]=c;g[++tot]=d; } g[++tot]=1; std::sort(g+1,g+1+tot); int len=std::unique(g+1,g+1+tot)-g-1; int now=0,nowl=0,nowr=0; for(int i=1;i<=len;i++){ int l=i,r=len,ans=0; while(l<=r){ if(g[r]-g[i]+1<now) break; int mid=l+r>>1; if(g[mid]-g[i]+1<now){ l=mid+1; continue; } if(check(g[i],g[mid])) ans=mid,l=mid+1; else r=mid-1; } if(!ans) continue; if(g[ans]-g[i]+1>now) now=g[ans]-g[i]+1,nowl=g[i],nowr=g[ans]; else if(g[ans]-g[i]+1==now and nowl>g[i]) nowl=g[i],nowr=g[ans]; } printf("%d\n",now); if(now) for(int i=nowl;i<=nowr;i++) write(i),putchar(' '); return 0; }
T2 這玩意兒還是挺水的trie樹上維護信息就好了也比較好想,就是卡空間有點惡心

%:pragma GCC optimize(3) #include<cstdio> #include<cctype> #include<cstring> #include<iostream> #define re register const int inf=3e6+1; using std::max; int n,m,tot=1; int ch[inf][3]; char a[5000005]; int mx[inf],las[inf]; void ins(int x){ int len=strlen(a); int now=1; for(re int i=0;i<len;i++){ if(!ch[now][a[i]-'a']) ch[now][a[i]-'a']=++tot; now=ch[now][a[i]-'a']; mx[now]=max(mx[now],x-las[now]-1); las[now]=x; } } void query(){ int len=strlen(a); int now=1; for(re int i=0;i<len;i++){ if(!ch[now][a[i]-'a']){ printf("%d\n",n); return; } now=ch[now][a[i]-'a']; } int p=max(mx[now],n-las[now]); printf("%d\n",p); } signed main(){ freopen("word.in","r",stdin);freopen("word.out","w",stdout); scanf("%d%d",&n,&m); for(re int i=1;i<=n;i++){ scanf("%s",a); ins(i); } while(m--){ scanf("%s",a); query(); } return 0; }
T3
咕咕咕
B組
T1 分解質因數,二分一個答案mid,再求出1~mid中所有質數的指數,合法的條件當且僅當之前處理的每個質數的指數大於1~mid中質數的指數。

#include<cstdio> #include<cctype> #include<cstring> #define N 100005 int n,a[N]; int vis[N*10],pcnt; int p[N*10],c[N*10]; int getint(){ int x=0,f=0;char ch=getchar(); while(!isdigit(ch)) f|=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return f?-x:x; } void init(){ for(int i=2;i<=1000000;i++){ if(!vis[i]) p[++pcnt]=i; for(int j=1;j<=pcnt;j++){ if(p[j]*i>1000000) break; vis[p[j]*i]=1; if(i%p[j]==0) break; } } } void fj(int x){ for(int i=1;i<=pcnt and x>=p[i];i++){ if(x%p[i]==0){ while(x%p[i]==0) c[i]++,x/=p[i]; } } } bool check(int x){ for(int i=1;i<=pcnt;i++){ int ans=0,now=1; for(int j=1;j;j++){ now=now*p[i]; if(now>x or now<0) break; ans=ans+x/now; } if(ans<c[i]) return 0; } return 1; } signed main(){ freopen("factorial.in","r",stdin); freopen("factorial.out","w",stdout); init(); n=getint(); for(int i=1;i<=n;i++) a[i]=getint(),fj(a[i]); int l=1,r=N*100,ans; while(l<=r){ int mid=l+r>>1; if(check(mid)) ans=mid,r=mid-1; else l=mid+1; } printf("%d\n",ans); return 0; }
T2 辣雞BFS,更辣雞的我調了一個小時

%:pragma GCC optimize(2) #include<queue> #include<cstdio> #include<cctype> #include<cstring> #include<iostream> #define N 505 #define re register using std::min; int n,m; int mp[105]; char ch[N][N]; int dis[N][N][4]; int vis[N][N][4]; int dx[]={-1,0,1,0},dy[]={0,-1,0,1}; struct Node{ int x,y,dir,dis; friend bool operator<(Node a,Node b){ return a.dis>b.dis; } }; std::priority_queue<Node> q; int getint(){ int x=0,f=0;char ch=getchar(); while(!isdigit(ch)) f|=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return f?-x:x; } signed main(){ freopen("run.in","r",stdin); freopen("run.out","w",stdout); mp['U']=0;mp['D']=2;mp['L']=1;mp['R']=3; n=getint(),m=getint(); for(re int i=1;i<=n;i++) scanf("%s",ch[i]+1); q.push((Node){1,1,-1,0}); memset(dis,0x3f,sizeof dis); while(q.size()){ Node u=q.top();q.pop(); int x=u.x,y=u.y,dir=u.dir; if(x==n and y==m){ printf("%d\n",dis[x][y][dir]); return 0; } if(dir!=-1 and vis[x][y][dir]) continue; if(dir!=-1) vis[x][y][dir]=1; if(ch[x][y]=='S') continue; if(dir==-1){ for(re int i=0;i<4;i++){ if(i==mp[ch[x][y]]) continue; int nx=x+dx[i]; int ny=y+dy[i]; if(nx<1 or ny<1 or nx>n or ny>m) continue; dis[nx][ny][i]=0; q.push((Node){nx,ny,i,0}); } continue; } for(re int i=0;i<4;i++){ if(i==mp[ch[x][y]]) continue; int nx=x+dx[i]; int ny=y+dy[i]; if(nx==1 and ny==1) continue; if(nx<1 or ny<1 or nx>n or ny>m) continue; if(dis[nx][ny][i]<=dis[x][y][dir]+(i!=dir)) continue; dis[nx][ny][i]=dis[x][y][dir]+(i!=dir); q.push((Node){nx,ny,i,dis[nx][ny][i]}); } } printf("No Solution"); return 0; }
T3
咕咕咕
2018.8.24
T1 看到邊權是1應該直接BFS的求個毛線的最短路啊。。

%:pragma GCC optimize(2) #include<queue> #include<cstdio> #include<cctype> #include<cstring> #include<iostream> #define K 105 #define N 100005 using std::max; using std::min; using std::swap; int head[N],dis[N]; int n,m,k,cnt,in[N]; int ok[K],ans[N],vis[N]; struct Edge{ int to,nxt; }edge[N<<1]; void add(int x,int y){ edge[++cnt].to=y; edge[cnt].nxt=head[x]; head[x]=cnt; } int getint(){ int x=0,f=0;char ch=getchar(); while(!isdigit(ch)) f|=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return f?-x:x; } void dij(int s){ std::queue<int> q;q.push(s); memset(dis,0x3f,sizeof dis);dis[s]=0; while(q.size()){ int u=q.front();q.pop();in[u]=0; ans[u]=max(ans[u],dis[u]); for(int i=head[u];i;i=edge[i].nxt){ int to=edge[i].to; if(dis[to]>dis[u]+1){ dis[to]=dis[u]+1; if(!in[to]) in[to]=1,q.push(to); } } } } signed main(){ freopen("oasis.in","r",stdin);freopen("oasis.out","w",stdout); n=getint(),m=getint(),k=getint(); for(int i=1;i<=k;i++) ok[i]=getint(); for(int i=1;i<=m;i++){ int a=getint(),b=getint(); add(a,b);add(b,a); } for(int i=1;i<=k;i++) dij(ok[i]); for(int i=1;i<=n;i++) printf("%d ",ans[i]); return 0; }
T2 考試的時候一直以為這是道數學題
定義 f[i][j] 表示有 i 個一類點,j 個二類點且都連滿的方案數
為了防止重復,規定要先連一類點再連二類點,也就是說 $f[i+1][j]+=f[i-1][j]*i$ 要在 $i>0\;\& \&\;j==0$ 的情況下
另外枚舉放一個二類點和哪些點連邊,dp轉移即可

#include<cstdio> #include<cctype> #include<cstring> #include<iostream> #define N 2005 #define int long long const int mod=998244353; using std::max; using std::min; using std::swap; int f[N][N]; int n,cnt1,cnt2; int getint(){ int x=0,f=0;char ch=getchar(); while(!isdigit(ch)) f|=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return f?-x:x; } signed main(){ freopen("map.in","r",stdin);freopen("map.out","w",stdout); n=getint(); for(int i=1;i<=n;i++){ int x=getint(); if(x==1) cnt1++;else cnt2++; } f[0][0]=1; for(int tot=0;tot<=n;tot++){ for(int i=n;~i;i--){ int j=tot-i; if(j<0) continue; if(i and j==0) (f[i+1][j]+=f[i-1][j]*i%mod)%=mod; if(i>1) (f[i][j+1]+=f[i-2][j]*(i*(i-1)/2)%mod)%=mod; if(i and j) (f[i][j+1]+=f[i][j-1]*i%mod*j%mod)%=mod; if(j>1) (f[i][j+1]+=f[i+2][j-2]*(j*(j-1)/2)%mod)%=mod; } } printf("%lld\n",f[cnt1][cnt2]); return 0; }
T3 這東西挺簡單的考場上式子就差一步就推過來了啊。。
定義sum[i..j]為i到j的區間和。
假設當前考慮了第1—i個數,答案不在其中。那么下一個可能是答案的數一定大於等於sum[1..i],設第一個大於等於sum[1..i]的數在第x個(可以使用線段樹二分找到)。判斷第x個數是否是答案,如果是就退出,否則把未找到答案的范圍擴展到第1—x個數,此時值域至少乘2,所以只要進行log次就找到答案或返回無解。

#include<cstdio> #include<cctype> #include<cstring> #include<iostream> using std::max; using std::min; using std::swap; #define N 200005 #define ls cur<<1,l,mid,ql,qr #define rs cur<<1|1,mid+1,r,ql,qr int n,m; int p[N],tag[N<<2]; int mx[N<<2],sum[N<<2]; int getint(){ int x=0,f=0;char ch=getchar(); while(!isdigit(ch)) f|=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return f?-x:x; } void pushup(int cur){ sum[cur]=sum[cur<<1]+sum[cur<<1|1]; mx[cur]=max(mx[cur<<1],mx[cur<<1|1]); } void build(int cur,int l,int r){ if(l==r){ sum[cur]=mx[cur]=p[l]; return; } int mid=l+r>>1; build(cur<<1,l,mid);build(cur<<1|1,mid+1,r); pushup(cur); } int querysum(int cur,int l,int r,int ql,int qr){ if(ql<=l and r<=qr) return sum[cur]; int mid=l+r>>1,ans=0; if(ql<=mid) ans+=querysum(ls); if(mid<qr) ans+=querysum(rs); return ans; } void modify(int cur,int l,int r,int ql,int qr,int x){ if(ql<=l and r<=qr){ sum[cur]=mx[cur]=x; return; } int mid=l+r>>1; if(ql<=mid) modify(ls,x); else modify(rs,x); pushup(cur); } int query(int cur,int l,int r,int ql,int qr,int x){ if(mx[cur]<x) return -1; if(l==r){ if(mx[cur]>=x) return l; else return -1; } int mid=l+r>>1,ans=-1; if(ql<=mid) ans=query(ls,x); if(ans!=-1) return ans; if(mid<qr) ans=query(rs,x); return ans; } signed main(){ freopen("challenge.in","r",stdin);freopen("challenge.out","w",stdout); n=getint(),m=getint(); for(int i=1;i<=n;i++) p[i]=getint(); build(1,1,n); while(m--){ int a=getint(),b=getint(); modify(1,1,n,a,a,b);p[a]=b; if(!p[1]){ printf("1\n"); continue; } int now=p[1],at=2; while(at<=n){ int pp=query(1,1,n,at,n,now); // printf("pp=%d\n",pp); if(pp==-1){ printf("-1\n"); break; } else{ int q=querysum(1,1,n,1,pp-1); if(q==p[pp]){ printf("%d\n",pp); break; } at=pp+1;now=q+p[pp]; } } } return 0; }
2018.8.27
T1 一個性質是聯通塊數=點數-你的邊數。然而我連這個性質都沒意識到=。= 。然后推一波式子之后發現這題跟阿狸和桃子的游戲一樣是個貪心,交替選度數最小的點即可。
代碼丟了就不貼了
T2 挺裸的矩乘啊 然而只有我tblztb切了
代碼找不到了就不貼了
T3 不會滾
2018.9.10~2018.9.11
靠最近老是考這么差,再這樣下去noip要省二滾粗了啊。。
Day1
T1
把式子寫出來是 $c_i=\sum \limits_{j=1}^i a_{\lfloor{\frac{i}{j}}\rfloor}b_{i-\lfloor{\frac{i}{j}}\rfloor\times j}$
發現a的下標可以數論分塊,b的下標可以前綴和搞一下,定義qzh[i][j]表示從點i往前每j個元素統計一次的前綴和,那么后面那個b就是i-kj,i-(k+1)j,i-(k+2)j...i-rj,可以用前綴和弄成 qzh[i-kj][k]-qzh[i-(r+1)*k][k]。
空間不夠?分塊思想
k大於根號n的暴力求,小於根號n的再用前綴和
這個思想還是很重要的,考場上前綴和啥的都想到了就差這一步了啊啊啊GG

#include<cmath> #include<cstdio> #include<cctype> #include<cstring> #include<iostream> #include<algorithm> #define N 100005 using std::min; using std::max; using std::swap; #define ll long long const int mod=123456789; int n,a[N],b[N]; int qzh[N][400]; int getint(){ int x=0,f=0;char ch=getchar(); while(!isdigit(ch)) f|=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return f?-x:x; } signed main(){ n=getint();int block=sqrt(n); for(int i=1;i<=n;i++) a[i]=getint()%mod; for(int i=1;i<=n;i++) b[i-1]=getint()%mod; for(int j=1;j<=block;j++){ for(int i=0;i<n;i++) qzh[i][j]=((i>=j?qzh[i-j][j]:0)+b[i])%mod; } for(int i=1;i<=n;i++){ ll ans=0; for(int j=1,r;j<=i;j=r+1){ int p=i/j; r=i/p; if(p>block){ for(int k=j;k<=r;k++) ans+=(ll)a[p]*b[i%k]%mod; } else ans+=(ll)a[p]*(qzh[i-p*j][p]-(i>=(r+1)*p?qzh[i-(r+1)*p][p]:0)+mod)%mod; } printf("%lld\n",ans%mod); } return 0; }
Day2
T1
這么水的題考場愣是沒想出來
對於一個坐標為d,高為h的仙人掌,一定要在d-h之前跳,同時一定會在d+h之后落地。那么一個個仙人掌就轉化成了一段段區間,因為不能二段跳,所以求一段最長區間並就行了。
考場上過於想知道恐龍在某個點的狀態會是什么,忽略了一些小性質。

#include<cstdio> #include<cctype> #include<cstring> #include<iostream> #include<algorithm> #define N 300005 using std::min; using std::max; using std::swap; int n; struct Node{ int l,r; friend bool operator<(Node x,Node y){ return x.l<y.l or x.l==y.l and x.r>y.r; } }node[N],node2[N]; int getint(){ int x=0,f=0;char ch=getchar(); while(!isdigit(ch)) f|=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return f?-x:x; } signed main(){ n=getint(); for(int i=1;i<=n;i++){ int a=getint(),b=getint(); if(a<b) return printf("-1"),0; node2[i].l=a-b;node2[i].r=a+b; } std::sort(node2+1,node2+1+n); int now=0; for(int i=1;i<=n;i++){ if(now and node2[i].l==node[now].l) continue; node[++now]=node2[i]; } n=now; int maxn=0,l=node[1].l,r=node[1].r; for(int i=2;i<=n;i++){ if(node[i].l<r) r=max(r,node[i].r); else maxn=max(maxn,r-l),l=node[i].l,r=node[i].r; } maxn=max(maxn,r-l); printf("%.1lf\n",(double)maxn/2.0); return 0; }
T2
水
T3
直接按聯通塊的size從小到大dfs,如果兩個聯通塊一樣大就先dfs因數個數少的那個。第二條剪枝極為有用。

#include<cstdio> #include<cctype> #include<vector> #include<cstring> #include<iostream> #include<algorithm> #define N 10 using std::min; using std::max; using std::swap; #define re register struct Node{ int x,y; }; std::vector<int> v[100000]; std::vector<Node> ltk[N*N]; int dx[]={-1,0,1,0},dy[]={0,1,0,-1}; int n,want[N][N],mp[N][N],tot,idx[N][N],cnt; int ans[N][N],hang[N],lie[N],now[N*N],vis[N][N]; bool cmpp(std::vector<Node> a,std::vector<Node> b){ return a.size()<b.size() or a.size()==b.size() and v[want[a[0].x][a[0].y]].size()<v[want[b[0].x][b[0].y]].size(); } int getint(){ int x=0,f=0;char ch=getchar(); while(!isdigit(ch)) f|=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return f?-x:x; } void dfs(int x,int y,int z){ ltk[z].push_back((Node){x,y}); idx[x][y]=z;vis[x][y]=1; for(int k=0;k<4;k++){ int nx=x+dx[k],ny=y+dy[k]; if(nx<1 or ny<1 or nx>n or ny>n or want[nx][ny]!=want[x][y] or vis[nx][ny]) continue; dfs(nx,ny,z); } } void cmp(){ for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ if(ans[i][j]!=mp[i][j]){ if(ans[i][j]<mp[i][j])return; else{ for(int k=1;k<=n;k++){ for(int p=1;p<=n;p++) ans[k][p]=mp[k][p]; } return; } } } } } void print(){ for(int i=1;i<=n;i++,puts("")){ for(int j=1;j<=n;j++) printf("%d ",mp[i][j]); } } void sdfs(int x,int xx){ // printf("x=%d,xx=%d\n",x,xx); // print();puts(""); if(x>cnt){ tot++;cmp(); return; } if(xx>=ltk[x].size()){ if(now[x]!=1) return; sdfs(x+1,0); return; } Node y=ltk[x][xx]; re int p=now[x],pp=hang[y.x]|lie[y.y]; for(int k=0;k<v[p].size();k++){ int z=v[p][k]; re int q=1<<z-1; if(pp&q) continue; mp[y.x][y.y]=z;hang[y.x]=hang[y.x]|q;lie[y.y]=lie[y.y]|q;now[x]=now[x]/z; sdfs(x,xx+1); mp[y.x][y.y]=0;hang[y.x]=hang[y.x]^q;lie[y.y]=lie[y.y]^q;now[x]=now[x]*z; } } signed main(){ for(int i=1;i<=9;i++){ for(int k=1;;k++){ if(i*k>100000) break; v[i*k].push_back(i); } } n=getint(); for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++) want[i][j]=getint(); } for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ if(!vis[i][j]) dfs(i,j,++cnt); } } std::sort(ltk+1,ltk+1+cnt,cmpp); for(int i=1;i<=cnt;i++) now[i]=want[ltk[i][0].x][ltk[i][0].y]; memset(ans,0x3f,sizeof ans); sdfs(1,0); printf("%d\n",tot); for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++) printf("%d ",ans[i][j]); puts(""); } return 0; }
2018.9.20
IOI賽制就很刺激。開場思考T3無果,發現一堆人A了T1,感覺是傻逼題。轉去想T1更無果。此時1.5h過去,得分還是0分,心態非常爆炸。
然后去想T2,突然想起正難則反。然后有了點思路,A了T2。又寫了T3的暴力,又想了1hT1,。發現確實是傻逼題,又切了T1。
總分100+100+40=240.本部rank3 (狗逼特神犇最后2min交了個暴力把我卡下去了mmp)。
T1
可以取一個>1e9的大質數,然后用大質數減去得出的結果肯定是P的倍數。然后用這個減去1詢問一下就是P-1了。再加上1就是P了。

#include "test.h" int Solve(){ int x=1e9+7,y=Query(x); int z=Query(x-y-1); return z+1; }
T2
正難則反。我們考慮求出一個序列,讓它們的GCD不等於1的方案數。然后用m^n減去這個方案就行了。然后發現,這是一個遞歸的子問題,就可以遞歸求了。復雜度O(logn*sqrt(n))
老器說我這是杜教篩?可是我從來沒寫過啊原理也不知道。。我又xjbYY出了一個算法??

#include<map> #include<cctype> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using std::min; using std::max; using std::swap; #define pii std::pair<ull,ull> typedef unsigned long long ull; std::map<pii,ull> mp; #define mp(A,B) std::make_pair(A,B) int getint(){ int X=0,w=0;char ch=0; while(!isdigit(ch))w|=ch=='-',ch=getchar(); while( isdigit(ch))X=X*10+ch-48,ch=getchar(); return w?-X:X; } ull ksm(ull a,ull b){ ull ans=1; while(b){ if(b&1) ans=ans*a; a=a*a;b>>=1; } return ans; } ull solve(ull x,ull y){ pii qqq=mp(x,y); if(mp[qqq]) return mp[qqq]; // std::cout<<x<<" "<<y<<std::endl; if(y==1) return 1ULL; ull p=ksm(y,x),ans=0; for(ull i=2,r;i<=y;i=r+1){ ull j=y/i;r=y/j; ans+=solve(x,j)*(r-i+1); } // for(ull i=2;i<=y;i++) // ans+=solve(x,y/i); return mp[qqq]=p-ans; } signed main(){ ull n,m;std::cin>>n>>m; std::cout<<solve(n,m); return 0; }
T3
咕咕咕
2018.9.22
這場比賽全程心態比較好。最開始他們噼里啪啦開始敲之后也沒有很着急,也是自己在那一點一點推。然后先切了T1,再切了T3。然后也沒有太滿足感覺大眾分都是這個分就又去好好想了想T2。一眼看出是性質題,在紙上寫下“推性質”,想了大概一個半小時突然有了一個想法,然后寫了一個非常優秀的暴力。
總分100+80+100=280 rank1 今天心態比較好吧沒做出來的題沒有着急做出來后也沒有放松。再加上他們四個沒發揮出正常水平就rank1了。
T1
第一個性質是跳的樓房的高度肯定是單調的不然可以通過交換變得更優。然后暴力O(n^3)dp就行了

#include<cctype> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> const int N=55; using std::min; using std::max; using std::swap; typedef double db; typedef long long ll; #define pb(A) push_back(A) #define pii std::pair<int,int> #define mp(A,B) std::make_pair(A,B) int n,m,g[N],len; ll f[N][N][N];//f[i][j][k]->前i個 選了j個 最后一個選的是k 最小多少 struct Node{ int c,h; friend bool operator<(Node x,Node y){ return x.h<y.h; } }node[N]; int getint(){ int X=0,w=0;char ch=0; while(!isdigit(ch))w|=ch=='-',ch=getchar(); while( isdigit(ch))X=X*10+ch-48,ch=getchar(); return w?-X:X; } signed main(){ n=getint(); for(int i=1;i<=n;i++) node[i].c=getint(); for(int i=1;i<=n;i++) node[i].h=getint(),g[i]=node[i].h; m=getint(); std::sort(node+1,node+1+n); std::sort(g+1,g+1+n);len=std::unique(g+1,g+1+n)-g-1; for(int i=1;i<=n;i++) node[i].h=std::lower_bound(g+1,g+1+len,node[i].h)-g; for(int i=0;i<=n;i++){ for(int j=0;j<=n;j++){ for(int k=0;k<=n;k++) f[i][j][k]=2e18; } } f[0][0][0]=0; for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ for(int k=0;k<i;k++){ //f[i-1][j-1][k] f[i][j][k]=min(f[i-1][j][k],f[i][j][k]); if(j!=1) f[i][j][i]=min(f[i][j][i],f[i-1][j-1][k]+(ll)node[i].c+g[node[i].h]-g[node[k].h]); else f[i][j][i]=min(f[i][j][i],(ll)node[i].c); } // printf("i=%d,j=%d,f=%lld\n",i,j,f[i][j][i]); } } int ans=0; for(int j=1;j<=n;j++){ for(int k=1;k<=n;k++){ if(f[n][j][k]<=m) ans=max(ans,j); } } printf("%d\n",ans); return 0; }
T2
吼性質題啊!
考場上腦抽一直覺得除了a2+a3還要考慮諸如a2+a4等等的情況覺得很不可做所以只枚舉了a1
實際上知道了a2+a3之后就知道了a1,進而就知道了a4~an
用multiset存一下就行了

#include<set> #include<vector> #include<cctype> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using std::min; using std::max; using std::swap; const int N=305; typedef double db; typedef long long ll; #define pb(A) push_back(A) #define pii std::pair<int,int> #define mp(A,B) std::make_pair(A,B) std::multiset<int> s; int n,b[N*N],a[N],ans; std::vector<int> v[100005]; std::multiset<int>::iterator it; bool cmp(std::vector<int> a,std::vector<int> b){ for(int i=0;i<n;i++){ if(a[i]!=b[i]) return a[i]>b[i]; } return 1; } int getint(){ int X=0,w=0;char ch=0; while(!isdigit(ch))w|=ch=='-',ch=getchar(); while( isdigit(ch))X=X*10+ch-48,ch=getchar(); return w?-X:X; } void dfs(int now){ if(now>n){ ans++; for(int i=1;i<=n;i++) v[ans].pb(a[i]); return; } a[now]=(*s.begin())-a[1]; for(int i=1;i<now;i++){ it=s.lower_bound(a[i]+a[now]); if(it==s.end() or (*it)!=a[i]+a[now]){ for(int j=1;j<i;j++) s.insert(a[j]+a[now]); return; } s.erase(it); } dfs(now+1); for(int i=1;i<now;i++) s.insert(a[i]+a[now]); } signed main(){ n=getint(); for(int i=1;i<=n*(n-1)/2;i++) b[i]=getint(),s.insert(b[i]); std::sort(b+1,b+1+n*(n-1)/2); for(int i=3;i<=n;i++){ //a[2]+a[3]=b[i] a[1]=b[1]+b[2]-b[i]>>1; a[2]=b[1]-a[1];a[3]=b[2]-a[1]; it=s.lower_bound(b[1]);s.erase(it); it=s.lower_bound(b[2]);s.erase(it); it=s.lower_bound(b[i]);s.erase(it); dfs(4); s.insert(b[1]);s.insert(b[2]);s.insert(b[i]); } std::sort(v+1,v+1+ans,cmp); ans=std::unique(v+1,v+1+ans)-v-1; printf("%d\n",ans); for(int i=1;i<=ans;i++){ for(int j=0;j<n;j++) printf("%d ",v[i][j]); puts(""); } return 0; }
T3
值域只有10000,可以按值域分塊做。
就是都想到離線排序了>100的還去用主席樹做我大概是個zz
那個>100的也可以弄成前綴相減的形式啊好有道理啊

#include<cctype> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using std::min; using std::max; using std::swap; typedef double db; const int N=100005; typedef long long ll; #define ls ch[cur][0] #define rs ch[cur][1] #define pb(A) push_back(A) #define pii std::pair<int,int> #define mp(A,B) std::make_pair(A,B) int qzh[105]; int ans[N],tot,cnt; int n,m,maxn,val[N]; int root[N],sum[N*30],ch[N*30][2]; struct Ques{ int l,r,p,v,idx,type; friend bool operator<(Ques x,Ques y){ if(x.p!=y.p) return x.p<y.p; return x.l<y.l; } }ques[N<<1]; int getint(){ int X=0,w=0;char ch=0; while(!isdigit(ch))w|=ch=='-',ch=getchar(); while( isdigit(ch))X=X*10+ch-48,ch=getchar(); return w?-X:X; } void build(int &cur,int l,int r){ if(!cur) cur=++cnt; if(l==r) return; int mid=l+r>>1; build(ls,l,mid);build(rs,mid+1,r); } int modify(int pre,int l,int r,int ql,int qr){ int cur=++cnt;ch[cur][0]=ch[pre][0];ch[cur][1]=ch[pre][1];sum[cur]=sum[pre]+1; if(l==r) return cur; int mid=l+r>>1; if(ql<=mid) ch[cur][0]=modify(ch[pre][0],l,mid,ql,qr); else ch[cur][1]=modify(ch[pre][1],mid+1,r,ql,qr); return cur; } int query(int pre,int now,int l,int r,int ql){ if(l==r) return sum[now]-sum[pre]; int mid=l+r>>1; if(ql<=mid) return query(ch[pre][0],ch[now][0],l,mid,ql); else return query(ch[pre][1],ch[now][1],mid+1,r,ql); } signed main(){ // freopen("in.txt","r",stdin); n=getint(),m=getint(); for(int i=1;i<=n;i++) val[i]=getint(),maxn=max(maxn,val[i]); for(int i=1;i<=m;i++){ int l=getint(),r=getint(),p=getint(),v=getint(); if(p<=100){ ques[++tot].l=l-1;ques[tot].r=l-1;ques[tot].p=p;ques[tot].v=v;ques[tot].idx=i;ques[tot].type=-1; ques[++tot].l=r;ques[tot].r=r;ques[tot].p=p;ques[tot].v=v;ques[tot].idx=i;ques[tot].type=1; } else{ ques[++tot].l=l;ques[tot].r=r;ques[tot].p=p;ques[tot].v=v;ques[tot].idx=i;ques[tot].type=2; } } std::sort(ques+1,ques+1+tot); build(root[0],0,maxn); for(int i=1;i<=n;i++) root[i]=modify(root[i-1],0,maxn,val[i],val[i]); for(int i=1;i<=tot;){ if(ques[i].p<=100){ if(i==1 or ques[i].p!=ques[i-1].p){ memset(qzh,0,sizeof qzh); int now=i; while(now<=tot and ques[now].l==0 and ques[now].p==ques[i].p) now++; if(ques[now].p!=ques[i].p){ i=now; continue; } for(int j=1;j<=n;j++){ qzh[val[j]%ques[i].p]++; while(now<=tot and ques[now].l==j and ques[now].p==ques[i].p){ ans[ques[now].idx]+=ques[now].type*qzh[ques[now].v]; now++; } } i=now;continue; } } else{ // printf("ques.p=%d\n",ques[i].p); int now=ques[i].v; while(1){ if(now>maxn) break; ans[ques[i].idx]+=query(root[ques[i].l-1],root[ques[i].r],0,maxn,now); // printf("now=%d,ans=%d\n",now,ans[ques[i].idx]); now+=ques[i].p; } i++; } } for(int i=1;i<=m;i++) printf("%d\n",ans[i]); return 0; }
2018.9.26
md三道傻逼題全場切然而T3調試完多刪了一句話導致沒有輸出我就是個zz
總分100+100+0=200 rank10
傻逼死了我
T2
題面保證相同的數字小於等於10個肯定是個突破口
然后因為兩段相同的話那么相對的數字的位置肯定是一樣的
就可以暴力存一下兩兩相同字符之間的距離,然后枚舉匹配的長度,哈希判一下就行了。用了雙模數,比較穩。

#include<set> #include<map> #include<queue> #include<cctype> #include<cstdio> #include<vector> #include<cstring> #include<iostream> #include<algorithm> using std::min; using std::max; using std::swap; typedef double db; const int N=100005; typedef long long ll; const ll mod1=1e9+7; const ll mod2=1e9+9; const ll base=100007; #define pb(A) push_back(A) #define pii std::pair<int,int> #define mp(A,B) std::make_pair(A,B) std::vector<int> v[N]; int n,val[N],g[N],len,las[N][12]; ll hsh1[N],hsh2[N],len1[N],len2[N]; int getint(){ int X=0,w=0;char ch=0; while(!isdigit(ch))w|=ch=='-',ch=getchar(); while( isdigit(ch))X=X*10+ch-48,ch=getchar(); return w?-X:X; } bool ok(int s,int t,int len){ ll a=(hsh1[t-1]-hsh1[s-1]*len1[len]%mod1+mod1)%mod1; ll b=(hsh1[t+len-1]-hsh1[t-1]*len1[len]%mod1+mod1)%mod1; if(a!=b) return 0; a=(hsh2[t-1]-hsh2[s-1]*len2[len]%mod2+mod2)%mod2; b=(hsh2[t+len-1]-hsh2[t-1]*len2[len]%mod2+mod2)%mod2; if(a!=b) return 0; return 1; } signed main(){ freopen("dor.in","r",stdin);freopen("dor.out","w",stdout); n=getint();len1[0]=len2[0]=1; for(int i=1;i<=n;i++){ g[i]=val[i]=getint(); len1[i]=len1[i-1]*base%mod1; len2[i]=len2[i-1]*base%mod2; } std::sort(g+1,g+1+n);len=std::unique(g+1,g+1+n)-g-1; for(int i=1;i<=n;i++){ val[i]=std::lower_bound(g+1,g+1+len,val[i])-g; hsh1[i]=(hsh1[i-1]*base%mod1+val[i])%mod1; hsh2[i]=(hsh2[i-1]*base%mod2+val[i])%mod2; las[val[i]][++las[val[i]][0]]=i; for(int j=1;j<las[val[i]][0];j++) v[i-las[val[i]][j]].pb(i); } int l=1; for(int len=1;len<=n/2;len++){ for(int i=0;i<v[len].size();i++){ if(v[len][i]-len<l) continue; if(v[len][i]+len-1>n) continue; // printf("len=%d,v=%d\n",len,v[len][i]); if(ok(v[len][i]-len,v[len][i],len)) l=v[len][i]; } } printf("%d\n",n-l+1); for(int i=l;i<=n;i++) printf("%d ",g[val[i]]); return 0; }
2018.9.29
啊昨天的不想寫了好煩啊
寫今天的吧。考試過程啥的也不想寫了。md二分邊界寫錯少了50pts擱誰都不想寫啊
T1 %%%yjc15min急速A題 狗題居然卡空間卡了我半個小時才卡過去
假設|s|<|t|。觀察一下s每個位置都會在t的哪里出現。發現間隔是gcd(|s|,|t|)。然后前綴和搞一下就行了。
~~說着比較簡單然而做了我1.5h還卡了0.5h空間~~

#include<set> #include<map> #include<queue> #include<cctype> #include<vector> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using std::min; using std::max; using std::swap; typedef double db; const int N=1000005; typedef long long ll; #define pb(A) push_back(A) #define pii std::pair<int,int> #define mp(A,B) std::make_pair(A,B) ll n,m; ll lena,lenb; ll qzh[N][27]; char s[N],t[N]; int getint(){ int X=0,w=0;char ch=0; while(!isdigit(ch))w|=ch=='-',ch=getchar(); while( isdigit(ch))X=X*10+ch-48,ch=getchar(); return w?-X:X; } ll gcd(ll a,ll b){ return b?gcd(b,a%b):a; } signed main(){ // freopen("a.txt","r",stdin); freopen("string.in","r",stdin);freopen("string.out","w",stdout); n=getint(),m=getint(); scanf("%s%s",s+1,t+1); lena=strlen(s+1);lenb=strlen(t+1); if(lena>lenb) swap(lena,lenb),swap(s,t),swap(n,m); ll g=gcd(lena,lenb); ll d=lena*lenb/g; for(int i=1;i<=lenb;i++){ for(int j=1;j<=26;j++){ if(i>g) qzh[i][j]=qzh[i-g][j]+(t[i]-'a'+1==j); else qzh[i][j]=(t[i]-'a'+1==j); } } ll ans=0,now=(lenb-1)/g*g+1; for(int i=1;i<=lena;i++){ if(i+now-1<=lenb){ ans+=qzh[i+now-1][s[i]-'a'+1]-(i>g?qzh[i-g][s[i]-'a'+1]:0); } else{ ans+=qzh[(i+now-1)%lenb][s[i]-'a'+1]+qzh[i+now-1-(i+now-1-lenb+g-1)/g*g][s[i]-'a'+1]-(i>g?qzh[i-g][s[i]-'a'+1]:0); } } printf("%lld\n",n*lena/d*ans); return 0; }
T2 md就是這題二分邊界寫錯少了50pts無緣rank2
首先觀察到可以二分。然后枚舉哪個是最高的積木。觀察到這個最高的兩邊呈等差數列,然后在左邊找個最大的右邊找個最小的大於x的值就行了。
這玩意兒可以拿單調隊列做?考場上時間比較緊張直接上的線段樹,然而因為一些奇怪的原因並沒有過大樣例。
下來調了調發現是線段樹直接區間查會有鍋,改了一下感覺復雜度不對但是過了?

#include<set> #include<map> #include<queue> #include<cctype> #include<vector> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using std::min; using std::max; using std::swap; typedef double db; #define int long long const int N=100005; typedef long long ll; #define pb(A) push_back(A) #define pii std::pair<int,int> #define mp(A,B) std::make_pair(A,B) #define ls cur<<1 #define rs cur<<1|1 #define lss ls,l,mid,ql,qr #define rss rs,mid+1,r,ql,qr int n,m,val[N]; ll sum[N],sum2[N],f[N]; ll mx[N<<2][2],idx[N<<2][2]; int getint(){ int X=0,w=0;char ch=0; while(!isdigit(ch))w|=ch=='-',ch=getchar(); while( isdigit(ch))X=X*10+ch-48,ch=getchar(); return w?-X:X; } const int inf=2e16; int query(int cur,int l,int r,int ql,int qr,int c,int pd){ if(mx[cur][pd]<c) return inf+5; if(l==r) return l; int mid=l+r>>1; if(qr<=mid) return query(lss,c,pd); if(ql>mid) return query(rss,c,pd); if(!pd){ int a=query(rss,c,pd); if(a!=inf+5) return a; return query(lss,c,pd); } else{ int a=query(lss,c,pd); if(a!=inf+5) return a; return query(rss,c,pd); } } bool check(ll mid){ for(int i=1;i<=n;i++){ if(val[i]>=mid) return 1; //find l r ll l=query(1,1,n,1,i-1,mid-i,0)+1,r=query(1,1,n,i+1,n,mid+i,1)-1; if(l>=inf or r>=inf) continue; ll used1=0,used2=0; used1=(mid+i)*(r-i+1)-sum[r]+sum[i-1]; used2=(mid-i)*(i-l+1)+sum2[i]-sum2[l-1]; if(used1+used2-(mid-val[i])<=m) return 1; } return 0; } void modify(int cur,int l,int r,int ql,int qr,ll c,ll d){ if(l==r){ mx[cur][0]=d; mx[cur][1]=c; idx[cur][0]=idx[cur][1]=l; return; } int mid=l+r>>1; if(ql<=mid) modify(lss,c,d); else modify(rss,c,d); mx[cur][0]=max(mx[ls][0],mx[rs][0]); if(mx[cur][0]==mx[rs][0]) idx[cur][0]=idx[rs][0]; else idx[cur][0]=idx[ls][0]; mx[cur][1]=max(mx[ls][1],mx[rs][1]); if(mx[cur][1]==mx[ls][1]) idx[cur][1]=idx[ls][1]; else idx[cur][1]=idx[rs][1]; } signed main(){ // freopen("in.txt","r",stdin); // freopen("block.in","r",stdin);freopen("block.out","w",stdout); n=getint();m=getint(); for(int i=1;i<=n;i++){ val[i]=getint(); modify(1,1,n,i,i,val[i]+i,val[i]-i); sum[i]=sum[i-1]+val[i]+i; sum2[i]=sum2[i-1]+i-val[i]; } ll l=0,r=2e18,ans=0; while(l<=r){ ll mid=l+r>>1; if(check(mid)) ans=mid,l=mid+1; else r=mid-1; } printf("%lld\n",ans); return 0; }
2018.9.30
hin良心的一套題
做了一個半小時發現C題是一道防AK的題然而暴力有70於是懶得去想正解了。。
總分100+100+70=270 rank1
T3
考場上就感覺跟之前CF的一道bubble cup的題很像,然而並沒有往下細想。
其實很水,我們維護一下當前棧里的元素的哈希值,然后在每個位置找一下相同的哈希值之前出現過了幾次,加上這個答案就行。因為在之前的某個位置和現在是相同的字符的話,那中間的這段一定是消掉的,直接加這段區間就好了。然而這題卡常幸虧沒寫正解不然跑成跟暴力一個分怕是要氣死

#include<set> #include<map> #include<queue> #include<cctype> #include<vector> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using std::min; using std::max; using std::swap; typedef double db; const int N=1000005; typedef long long ll; const int base=127; const int mod1=1e9+7; const int mod2=1e9+9; #define pb(A) push_back(A) #define pii std::pair<ll,ll> #define mp(A,B) std::make_pair(A,B) int n; char ch[N]; int stk[N],top; struct Node{ int x,y; ll get(){ return 1ll*x*mod2+y; } friend bool operator<(Node a,Node b){ return a.x<b.x or a.x==b.x and a.y<b.y; } }H[N]; std::map<ll,int> mp; int getint(){ int X=0,w=0;char ch=0; while(!isdigit(ch))w|=ch=='-',ch=getchar(); while( isdigit(ch))X=X*10+ch-48,ch=getchar(); return w?-X:X; } signed main(){ scanf("%s",ch+1);n=strlen(ch+1); ll ans=0;H[0].x=0;H[0].y=0;mp[H[0].get()]++; for(int i=1;i<=n;i++){ if(top and stk[top]==(int)ch[i]) top--; else{ stk[++top]=ch[i]; H[top]=H[top-1]; H[top].x=(1ll*H[top].x*base+ch[i])%mod1; H[top].y=(1ll*H[top].y*base+ch[i])%mod2; } ll x=H[top].get(); ans+=mp[x];mp[x]++; } printf("%lld\n",ans); return 0; }
2018.10.9
從頭被吊打到尾
開場旁邊的yqy直接驚呼“這不原題么”然后噼里啪啦一頓亂敲,頓時心態非常爆炸。知道大概是個貪心也知道是個原題貌似也寫過然而就是想不起來怎么做,無奈老老實實自己想。想了個貪心之后忘了有大樣例直接敲了個對拍。然后拍一組wa一組,最后修修補補的總算把貪心給寫對了。然后去看T2,冥思一小時后想到了正解感覺可以拿莫隊做,因為n,m加減都是可以O(1)算出來的,無奈考場犯傻逼算錯復雜度覺得莫隊復雜度是O(nm sqrt(nm))=O(n^3)不如暴力,於是寫了80分部分分還寫掛了10分去看T3。T3想了半天大概有正解的思路了就是各種vecotr二分亂搞然而覺得太復雜了就沒寫,然后去寫部分分,然而這次也是寫了80分部分分然而寫掛了40分。差分之后求前綴和就掃一遍也真是沒誰了
總分100+70+40=210=GG
靠靠靠暴力不打錯就rank4了暴力打滿就rank2了啊啊啊血虧
T2 真是沒救了能否定想出來的正解我甚至開始佩服自己的傻逼了

#include<set> #include<map> #include<queue> #include<cctype> #include<vector> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using std::min; using std::max; using std::swap; typedef double db; const int N=100005; const int M=100000; const int mod=1e9+7; typedef long long ll; #define pb(A) push_back(A) #define pii std::pair<int,int> #define mp(A,B) std::make_pair(A,B) int fac[N],ifac[N],block,ans[N]; struct Ques{ int l,r,idx; friend bool operator<(Ques a,Ques b){ return a.l/block<b.l/block or a.l/block==b.l/block and a.r<b.r; } }ques[N]; int C(int n,int m){ return (ll)fac[n]*ifac[n-m]%mod*ifac[m]%mod; } int getint(){ int X=0,w=0;char ch=0; while(!isdigit(ch))w|=ch=='-',ch=getchar(); while( isdigit(ch))X=X*10+ch-48,ch=getchar(); if(w) return -X;return X; } int ksm(int a,int b){ int ans=1; while(b){ if(b&1) ans=(ll)ans*a%mod; a=(ll)a*a%mod;b>>=1; } return ans; } signed main(){ fac[0]=ifac[0]=1;block=100; for(int i=1;i<=M;i++) fac[i]=(ll)fac[i-1]*i%mod; ifac[M]=ksm(fac[M],mod-2); for(int i=M-1;i;i--) ifac[i]=(ll)ifac[i+1]*(i+1)%mod; getint();int q=getint(); for(int i=1;i<=q;i++){ ques[i].l=getint();ques[i].r=getint();ques[i].idx=i; } std::sort(ques+1,ques+q+1); int l=0,r=0,nowans=1,inv=ksm(2,mod-2); for(int i=1;i<=q;i++){ // printf("q=%d,l=%d,r=%d,ans=%d\n",q,l,r,nowans); if(r<ques[i].r){ while(l<ques[i].l){l++;nowans=((ll)nowans*2%mod-C(l-1,r)+mod)%mod;} while(l>ques[i].l){l--;nowans=((ll)nowans+C(l,r))%mod*inv%mod;} while(r<ques[i].r){r++;nowans=((ll)nowans+C(l,r))%mod;} } else if(r>ques[i].r){ while(r>ques[i].r){nowans=((ll)nowans-C(l,r)+mod)%mod;r--;} while(l<ques[i].l){l++;nowans=((ll)nowans*2%mod-C(l-1,r)+mod)%mod;} while(l>ques[i].l){l--;nowans=((ll)nowans+C(l,r))%mod*inv%mod;} } else{ while(l<ques[i].l){l++;nowans=((ll)nowans*2%mod-C(l-1,r)+mod)%mod;} while(l>ques[i].l){l--;nowans=((ll)nowans+C(l,r))%mod*inv%mod;} } ans[ques[i].idx]=nowans; } for(int i=1;i<=q;i++) printf("%d\n",ans[i]); return 0; }
T3 改不動告辭
2018.10.10
這里是空間開大選手yzh
總分100+0+30=130 啊啊啊好虧啊T2要開log的空間腦抽怕RE多摁了兩個0...
T1 貪心的想一定是刪去最小的a的一段和最小的b的一段,雙指針掃一下就行了 考場上沒清空數組調了我二十分鍾

#include<set> #include<map> #include<cctype> #include<cstdio> #include<vector> #include<cstring> #include<iostream> #include<algorithm> using std::min; using std::max; using std::swap; typedef double db; const int N=100005; typedef long long ll; int n,m; int cx[N]; struct Node{ int val,idx; friend bool operator<(Node x,Node y){ return x.val<y.val; } }a[N],b[N]; int getint(){ int x=0,f=0;char ch=getchar(); while(!isdigit(ch)) f|=ch=='-',ch=getchar(); while(isdigit(ch)) x=x*10+ch-'0',ch=getchar(); if(f) return -x;return x; } signed main(){ freopen("d.in","r",stdin);freopen("d.out","w",stdout); int T=getint(); while(T--){ n=getint();m=getint(); for(int i=1;i<=n;i++){ a[i].val=getint(); b[i].val=getint(); a[i].idx=b[i].idx=i; } std::sort(a+1,a+1+n);std::sort(b+1,b+1+n); ll ans=(ll)a[1].val*b[1].val; for(int i=1;i<=m;i++) cx[a[i].idx]++; int nowa=m+1,nowb=1; while(nowb<=n and cx[b[nowb].idx]){ cx[b[nowb].idx]++; nowb++; } ans=max(ans,(ll)a[m+1].val*b[nowb].val); int las=a[m+1].val; for(int i=m;i;i--){ cx[a[i].idx]--; if(!cx[a[i].idx]){ cx[b[nowb].idx]++;nowb++; while(nowb<=n and cx[b[nowb].idx]){ cx[b[nowb].idx]++; nowb++; } las=a[i].val; } ans=max(ans,(ll)las*b[nowb].val); } printf("%lld\n",ans); memset(cx,0,sizeof cx); } return 0; }
T2 這個最小的聯通塊肯定是每個點到lca組成的鏈的集合 主席樹暴搞求一個值的前驅后繼就好了
然而考場沒想出來一個log的做法就外層套了個二分。。
然后因為這個二分的做法還要額外記錄一下dfs時候的臨時變量,然后這個需要logn的空間
but我開成了根號級別

#include<set> #include<map> #include<cctype> #include<cstdio> #include<vector> #include<cstring> #include<iostream> #include<algorithm> using std::min; using std::max; using std::swap; #define re register typedef double db; const int N=100005; typedef long long ll; int n,q,type,cnt,tot; int sum[N*40],ch[N*40][2]; int f[N][19],len,d[N],g[N]; int val[N],head[N],root[N]; struct Edge{ int to,nxt; }edge[N<<1]; inline void add(int x,int y){ edge[++cnt].to=y; edge[cnt].nxt=head[x]; head[x]=cnt; } inline int getint(){ int x=0,f=0;char ch=getchar(); while(!isdigit(ch)) f|=ch=='-',ch=getchar(); while(isdigit(ch)) x=x*10+ch-'0',ch=getchar(); if(f) return -x;return x; } int modify(int cur,int l,int r,int ql){ int now=++tot; sum[now]=sum[cur]+1;ch[now][0]=ch[cur][0];ch[now][1]=ch[cur][1]; if(l==r) return now; int mid=l+r>>1; if(ql<=mid) ch[now][0]=modify(ch[cur][0],l,mid,ql); else ch[now][1]=modify(ch[cur][1],mid+1,r,ql); return now; } void dfs(int now,int fa){ val[now]=std::lower_bound(g+1,g+1+len,val[now])-g; root[now]=modify(root[fa],1,len,val[now]); for(re int i=head[now];i;i=edge[i].nxt){ int to=edge[i].to; if(to==fa) continue; d[to]=d[now]+1;f[to][0]=now; for(int j=1;j<=18;j++) f[to][j]=f[f[to][j-1]][j-1]; // printf("now=%d,to=%d\n",now,to); // for(int j=0;j<=18;j++) printf("j=%d,f=%d\n",j,f[to][j]); dfs(to,now); } } inline int lca(int x,int y){ if(d[x]<d[y]) swap(x,y); // printf("x=%d,y=%d\n",x,y); for(re int i=18;~i;i--) if(d[f[x][i]]>=d[y]) x=f[x][i]; // printf("x=%d,y=%d\n",x,y); if(x==y) return x; for(re int i=18;~i;i--) if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i]; return f[x][0]; } int cnts; int stkk[30][N*5]; int dp[N*5],dc; inline int newnode(){ return dc?dp[dc--]:++cnts; } int query(int pre,int pre2,int del,int l,int r,int ql,int qr,int pd){ // printf("l=%d,r=%d,ql=%d,qr=%d\n",l,r,ql,qr); if(ql<=l and r<=qr){ int now=0; for(re int i=1;i<=del+1;i++) now+=sum[stkk[pd][i]]; now=now-sum[pre]*del-sum[pre2]; // printf("now=%d\n",now); if(now>0) return 1; return 0; } int mid=l+r>>1; if(ql>mid){ for(re int i=1;i<=del+1;i++) stkk[pd][i]=ch[stkk[pd][i]][1]; return query(ch[pre][1],ch[pre2][1],del,mid+1,r,ql,qr,pd); } if(qr<=mid){ for(re int i=1;i<=del+1;i++) stkk[pd][i]=ch[stkk[pd][i]][0]; return query(ch[pre][0],ch[pre2][0],del,l,mid,ql,qr,pd); } int lef=newnode(); for(re int i=1;i<=del+1;i++) stkk[lef][i]=ch[stkk[pd][i]][0]; int kkk=query(ch[pre][0],ch[pre2][0],del,l,mid,ql,qr,lef); if(kkk){ dp[++dc]=lef; return 1; } for(re int i=1;i<=del+1;i++) stkk[lef][i]=ch[stkk[pd][i]][1]; kkk=query(ch[pre][1],ch[pre2][1],del,mid+1,r,ql,qr,lef); dp[++dc]=lef; return kkk; } void change(int &cur,int l,int r){ cur=++tot; if(l==r) return; int mid=l+r>>1; change(ch[cur][0],l,mid);change(ch[cur][1],mid+1,r); } inline int abs(int x){ if(x<0) return -x; return x; } int ques[N*5]; signed main(){ freopen("e.in","r",stdin);freopen("e.out","w",stdout); n=getint(),q=getint(),type=getint(); for(re int i=1;i<=n;i++) g[i]=val[i]=getint(); std::sort(g+1,g+1+n);len=std::unique(g+1,g+1+n)-g-1; for(re int i=1;i<n;i++){ int x=getint(),y=getint(); add(x,y);add(y,x); } change(root[0],1,len);d[1]=1;dfs(1,0); int lasans=0; while(q--){ int rr=getint(),k=getint(),now=(getint()-1+type*lasans)%n+1;ques[1]=root[now]; for(re int i=2;i<=k;i++){ int x=(getint()-1+type*lasans)%n+1; now=lca(now,x);ques[i]=root[x]; } if(k==1){ printf("%d\n",lasans=abs(g[val[now]]-rr)); continue; } int lala=newnode(); int p=std::lower_bound(g+1,g+1+len,rr)-g; int l=p,r=len,ans=0; while(l<=r){ // printf("l=%d,r=%d\n",l,r); for(re int i=1;i<=k;i++) stkk[lala][i]=ques[i]; int mid=l+r>>1; if(query(root[now],root[f[now][0]],k-1,1,len,p,mid,lala)) ans=mid,r=mid-1; else l=mid+1; } int ppp=0x3f3f3f3f; // printf("ans=%d\n",ans); if(ans) ppp=abs(g[ans]-rr); l=1,r=p-1,ans=0; while(l<=r){ // for(int i=0;i<k;i++) b[i]=a[i]; for(re int i=1;i<=k;i++) stkk[lala][i]=ques[i]; int mid=l+r>>1; if(query(root[now],root[f[now][0]],k-1,1,len,mid,p-1,lala)) ans=mid,l=mid+1; else r=mid-1; } if(ans) ppp=min(abs(g[ans]-rr),ppp); printf("%d\n",lasans=ppp);dp[++dc]=lala; } return 0; }
誒我發現我代碼風格越來越難看了啊。。。
看來相比於強迫症,我還是更懶一些=。=
T3 可以先確定每一個二進制位對於答案逆序對的貢獻,就是一段前綴相等然后這一位不相等的值,這個可以trie樹預處理出來
然后就知道了[0,2^k)每個數的貢獻 然而這樣是O(2^k)的
考慮把一個二進制數拆成前一半和后一半
這樣就顯得很可做了 每一問二分一遍就好了
由於二分姿勢錯誤調了好久 由於忘開long long調到懷疑人生

#include<set> #include<map> #include<cmath> #include<queue> #include<cctype> #include<vector> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using std::min; using std::max; using std::swap; using std::vector; typedef double db; const int P=32; const int N=500005; const int M=N*30; typedef long long ll; #define pb(A) push_back(A) #define pii std::pair<ll,int> #define mp(A,B) std::make_pair(A,B) int n,k,p,tot=1; ll val[P][2],k1,k2; pii a[1<<16],b[1<<16]; int ch[M][2];ll sze[M]; int getint(){ int X=0,w=0;char ch=0; while(!isdigit(ch))w|=ch=='-',ch=getchar(); while( isdigit(ch))X=X*10+ch-48,ch=getchar(); if(w) return -X;return X; } void insert(int x){ int now=1; for(int i=k-1;~i;i--){ if(x>>i&1) val[i][1]+=sze[ch[now][0]]; else val[i][0]+=sze[ch[now][1]]; if(!ch[now][x>>i&1]) ch[now][x>>i&1]=++tot; now=ch[now][x>>i&1]; sze[now]++; } } bool check(ll x){ int now=(1<<k2)-1;ll sum=0; for(int i=0;i<(1<<k1);i++){ while(now>=0 and a[i].first+b[now].first>x) now--; sum+=now+1; } return sum<p; } bool check2(ll x,ll y){ int now=(1<<k2)-1;ll sum=0; for(int i=0;i<(1<<k1);i++){ while(now>=0 and ((a[i].first+b[now].first>x) or ((a[i].first+b[now].first==x) and ((a[i].second|b[now].second)>y)))) now--; sum+=now+1; } return sum<p; } signed main(){ n=getint(),k=getint(),p=getint(); for(int i=1;i<=n;i++){ int x=getint(); insert(x); } k1=k>>1,k2=k-k1; for(int i=0;i<(1<<k1);i++){ for(int j=0;j<k1;j++){ if(i>>j&1) a[i].first+=val[j+k2][1]; else a[i].first+=val[j+k2][0]; } a[i].second=i<<k2; } std::sort(a,a+(1<<k1)); for(int i=0;i<(1<<k2);i++){ for(int j=0;j<k2;j++){ if(i>>j&1) b[i].first+=val[j][1]; else b[i].first+=val[j][0]; } b[i].second=i; } std::sort(b,b+(1<<k2)); // for(int i=0;i<(1<<k1);i++) printf("i=%d,a=%d,b=%d\n",i,a[i].first,a[i].second); // for(int i=0;i<(1<<k2);i++) printf("i=%d,a=%d,b=%d\n",i,b[i].first,b[i].second); ll l=0,r=(ll)n*(n-1)/2,ans=-1; while(l<=r){ ll mid=l+r>>1; if(check(mid)) ans=mid,l=mid+1; else r=mid-1; } printf("%lld ",ans+1); l=0,r=(1<<k)-1;ll ans2=-1; while(l<=r){ ll mid=l+r>>1; if(check2(ans+1,mid)) ans2=mid,l=mid+1; else r=mid-1; } printf("%lld",ans2+1); return 0; }
2018.10.13
自己菜沒啥好說的
T1 前綴后綴搞一下就行了

#include<set> #include<map> #include<cmath> #include<queue> #include<cctype> #include<vector> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using std::min; using std::max; using std::swap; using std::vector; typedef double db; const int N=100005; typedef long long ll; #define pb(A) push_back(A) #define pii std::pair<int,int> #define mp(A,B) std::make_pair(A,B) int n,x; ll val[N],qzh[N],hzh[N]; ll getint(){ ll X=0,w=0;char ch=0; while(!isdigit(ch))w|=ch=='-',ch=getchar(); while( isdigit(ch))X=X*10+ch-48,ch=getchar(); if(w) return -X;return X; } signed main(){ freopen("poker.in","r",stdin);freopen("poker.out","w",stdout); n=getint(),x=getint(); for(int i=1;i<=n;i++) val[i]=getint(),qzh[i]=qzh[i-1]|val[i]; std::sort(val+1,val+1+n); for(int i=n;i;i--) hzh[i]=hzh[i+1]|val[i]; ll ans=0; for(int i=1;i<=n;i++) ans=max(ans,(val[i]*x)|qzh[i-1]|hzh[i+1]); printf("%lld\n",ans); return 0; }
T2 一個顯然的容斥想法是像幸運數字那樣做一波,復雜度不會算,但是覺得沒有2^50那么多,因為1e9以內一個數最多只有9個不同的質因子,感覺加上這個剪枝可以隨便過。事實是確實可以隨便過,但是考場上不知道怎么想的覺得這個 gcd(val[i],n)!=1 等價於 val[i] 是 n 的約數,然后就涼涼。。。 加上一句 val[i]=gcd(val[i],n) 就能 A 了

#include<set> #include<map> #include<cmath> #include<queue> #include<cctype> #include<vector> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using std::min; using std::max; using std::swap; using std::vector; typedef double db; typedef long long ll; #define pb(A) push_back(A) #define pii std::pair<int,int> #define mp(A,B) std::make_pair(A,B) ll n,m; int cnts; ll val[55],ans,b[55]; ll gcd(ll a,ll b){ return b?gcd(b,a%b):a; } void dfs(int now,int used,ll lcm){ if(lcm>n) return; if(now>cnts){ if(used&1) ans+=(n-1)/lcm; else if(used) ans-=(n-1)/lcm; return; } dfs(now+1,used+1,lcm*b[now]/gcd(lcm,b[now])); dfs(now+1,used,lcm); } ll getint(){ ll X=0,w=0;char ch=0; while(!isdigit(ch))w|=ch=='-',ch=getchar(); while( isdigit(ch))X=X*10+ch-48,ch=getchar(); if(w) return -X;return X; } signed main(){ freopen("running.in","r",stdin);freopen("running.out","w",stdout); n=getint(),m=getint(); for(int i=1;i<=m;i++){ val[i]=getint(); if(gcd(val[i],n)==1) return printf("0"),0; val[i]=gcd(val[i],n); } std::sort(val+1,val+1+m);m=std::unique(val+1,val+1+m)-val-1; for(int i=1;i<=m;i++){ int flag=0; for(int j=1;j<i;j++){ if(val[i]%val[j]==0) flag=1; } if(flag)continue; b[++cnts]=val[i]; } std::reverse(b+1,b+1+cnts); dfs(1,0,1); printf("%lld\n",n-ans-1); return 0; }
然而正解不是這樣的 白瞎了我證明半天復雜度可以艹過去
正解是對於 n 的每個約數 d ,如果有某個 i 滿足 gcd(val_i,n)|d,那么所有 gcd(x,n)=d 的 x 都是可以被經過的,答案加上 phi(n/d) 就好了
woc 感覺這樣好玄學而且好難想

#include<set> #include<map> #include<cmath> #include<queue> #include<cctype> #include<vector> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> const int N=55; using std::min; using std::max; using std::swap; using std::vector; typedef double db; typedef long long ll; #define pb(A) push_back(A) #define pii std::pair<int,int> #define mp(A,B) std::make_pair(A,B) ll n,m; vector<ll> v; ll val[N],ans; ll gcd(ll a,ll b){ return b?gcd(b,a%b):a; } ll getint(){ ll X=0,w=0;char ch=0; while(!isdigit(ch))w|=ch=='-',ch=getchar(); while( isdigit(ch))X=X*10+ch-48,ch=getchar(); if(w) return -X;return X; } ll phi(ll x){ ll now=x,sq=sqrt(x); for(int i=2;i<=sq;i++){ if(x%i==0){ now=now/i*(i-1); while(x%i==0) x/=i; } } if(x>1) now=now/x*(x-1);return now; } signed main(){ n=getint(),m=getint(); for(int i=1;i<=m;i++){ val[i]=getint(); ll g=gcd(val[i],n); if(g==1) return printf("0"),0; val[i]=g; } ll sq=sqrt(n); for(ll i=1;i<=sq;i++){ if(n%i==0){ v.pb(i); if(i*i!=n) v.pb(n/i); } } for(int i=0;i<v.size();i++){ for(int j=1;j<=m;j++){ if(v[i]%val[j]==0){ ans+=phi(n/v[i]); break; } } } printf("%lld\n",n-ans); return 0; }
有沒有老哥資助個鍵盤啊
noip考完就還 考退役還 考AK也還
2018.10.15
考完看確實三道傻逼題啊... 然而考場上沒想出來T1傻逼遞推也沒推出來T3柿子 然而還是rank3?
T1 哇這遞推還想不出來noip還咋考啊 顯然有遞推性質啊不知道考場上怎么想的

#include<set> #include<map> #include<cmath> #include<queue> #include<cctype> #include<vector> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using std::min; using std::max; using std::swap; using std::vector; typedef double db; typedef long long ll; const int mod=99824353; #define pb(A) push_back(A) #define pii std::pair<int,int> #define mp(A,B) std::make_pair(A,B) int val[1<<25]; int n,k,Seed,ans; int getint(){ int X=0,w=0;char ch=0; while(!isdigit(ch))w|=ch=='-',ch=getchar(); while( isdigit(ch))X=X*10+ch-48,ch=getchar(); if(w) return -X;return X; } signed main(){ n=getint(),k=getint(),Seed=getint(); int maxn=1<<k; for(int i=1;i<maxn;i++) val[i]=(val[i>>1]>>1)|((i&1)<<k-1); for(int i=1;i<=n;i++){ Seed=(214013LL*Seed+2531011)&((1<<k)-1); ans=((ll)ans*233+val[Seed])%mod; } printf("%d\n",ans); return 0; }
T2 哈希隨便做做就行了
T3 據說一堆打表怪??可以把式子推成 C(n,i)*i^2 的結構,然后ztb講的組合意義比較好理解,每一項就相當於求包含 i 個元素的集合平方之后子集的個數 然后考慮貢獻就做完了 %ztb

#include<set> #include<map> #include<cmath> #include<queue> #include<cctype> #include<vector> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using std::min; using std::max; using std::swap; using std::vector; typedef double db; typedef long long ll; const int mod=1e9+7; #define pb(A) push_back(A) #define pii std::pair<int,int> #define mp(A,B) std::make_pair(A,B) int ksm(int a,int b){ int ans=1; while(b){ if(b&1)ans=(ll)ans*a%mod; a=(ll)a*a%mod;b>>=1; } return ans; } int getint(){ int X=0,w=0;char ch=0; while(!isdigit(ch))w|=ch=='-',ch=getchar(); while( isdigit(ch))X=X*10+ch-48,ch=getchar(); if(w) return -X;return X; } signed main(){ int n=getint(); printf("%lld\n",((ll)ksm(2,n-1)*n%mod+(ll)ksm(2,n-2)*n%mod*(n-1)%mod)%mod); return 0; }
2018.10.16
考回原形mmp
今天好不在狀態啊沒能秒的題就不想繼續思考了秒了的題也寫掛了啊啊啊
其實T1不掛的話其他兩個就算想不出來也能rank3啊
T1 傻逼題 注意點以后雙模數哈希別tm再打錯變量了直接崩了100pts

#include<set> #include<map> #include<cmath> #include<queue> #include<cctype> #include<vector> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using std::min; using std::max; using std::swap; using std::vector; typedef double db; const int N=1000005; const int base1=29; const int base2=31; const int mod1=1e9+7; const int mod2=1e9+9; typedef long long ll; #define pb(A) push_back(A) #define pii std::pair<int,int> #define mp(A,B) std::make_pair(A,B) char ch[N]; int len[2][N]; int qz[2],hz[2]; int qzhsh[2][N],n; int getint(){ int X=0,w=0;char ch=0; while(!isdigit(ch))w|=ch=='-',ch=getchar(); while( isdigit(ch))X=X*10+ch-48,ch=getchar(); if(w) return -X;return X; } bool check(int l1,int r1,int l2,int r2){ int ans=0; if(r1>=l1 and l2<=r2){ int a=((ll)qzhsh[0][r2]-(ll)qzhsh[0][l2-1]*len[0][r2-l2+1]%mod1+mod1)%mod1; int b=(ll)qzhsh[0][r1]*len[0][r2-l2+1]%mod1; if(((ll)a+b)%mod1!=hz[0]) return 0; a=((ll)qzhsh[1][r2]-(ll)qzhsh[1][l2-1]*len[1][r2-l2+1]%mod2+mod2)%mod2; b=((ll)qzhsh[1][r1]*len[1][r2-l2+1]%mod2); return ((ll)a+b)%mod2==hz[1]; } else if(l1>r1){ ans=((ll)qzhsh[0][r2]-(ll)qzhsh[0][l2-1]*len[0][r2-l2+1]%mod1+mod1)%mod1; if(ans!=hz[0]) return 0; ans=((ll)qzhsh[1][r2]-(ll)qzhsh[1][l2-1]*len[1][r2-l2+1]%mod2+mod2)%mod2; return ans==hz[1]; } else{ return qzhsh[0][r1]==hz[0] and qzhsh[1][r1]==hz[1]; } } bool check2(int l1,int r1,int l2,int r2){ // printf("A=%d,b=%d,c=%d,d=%d\n",l1,r1,l2,r2); if(l2<=r2){ int a=((ll)qzhsh[0][r2]-(ll)qzhsh[0][l2-1]*len[0][r2-l2+1]%mod1+mod1)%mod1; int b=((ll)qzhsh[0][r1]-(ll)qzhsh[0][l1-1]*len[0][r1-l1+1]%mod1+mod1)%mod1; // printf("a=%d,b=%d\n",a,b); b=((ll)b*len[0][r2-l2+1]%mod1); a=((ll)a+b)%mod1; // printf("a=%d,qz=%d\n",a,qz[0]); if(a!=qz[0]) return 0; a=((ll)qzhsh[1][r2]-(ll)qzhsh[1][l2-1]*len[1][r2-l2+1]%mod2+mod2)%mod2; b=((ll)qzhsh[1][r1]-(ll)qzhsh[1][l1-1]*len[1][r1-l1+1]%mod2+mod2)%mod2; b=((ll)b*len[1][r2-l2+1]%mod2); a=((ll)a+b)%mod2; return a==qz[1]; } else{ int a=((ll)qzhsh[0][r1]-(ll)qzhsh[0][l1-1]*len[0][r1-l1+1]%mod1+mod1)%mod1; if(a!=qz[0]) return 0; a=((ll)qzhsh[1][r1]-(ll)qzhsh[1][l1-1]*len[1][r1-l1+1]%mod2+mod2)%mod2; return a==qz[1]; } } signed main(){ // freopen("lgg.in","r",stdin); // freopen("lgg.out","w",stdout); len[0][0]=len[1][0]=1; for(int i=1;i<=N-5;i++){ len[0][i]=(ll)len[0][i-1]*base1%mod1; len[1][i]=(ll)len[1][i-1]*base2%mod2; } int T=getint(); while(T--){ n=getint();scanf("%s",ch+1); if(n%2==0){printf("NOT POSSIBLE\n");continue;} for(int i=1;i<=n;i++){ qzhsh[0][i]=((ll)qzhsh[0][i-1]*base1%mod1+ch[i]-'A'+1)%mod1; qzhsh[1][i]=((ll)qzhsh[1][i-1]*base2%mod2+ch[i]-'A'+1)%mod2; } int flag=0,p=n>>1,idx=0,aaa=0; qz[0]=qzhsh[0][p];qz[1]=qzhsh[1][p]; hz[0]=((ll)qzhsh[0][n]-(ll)qzhsh[0][n-p]*len[0][p]%mod1+mod1)%mod1; hz[1]=((ll)qzhsh[1][n]-(ll)qzhsh[1][n-p]*len[1][p]%mod2+mod2)%mod2; for(int i=1;i<=n;i++){ if(i<=p+1){ if(check(1,i-1,i+1,p+1)){ if(idx!=hz[0]) flag++,idx=hz[0],aaa=i; } } else{ if(check2(p+1,i-1,i+1,n)){ if(idx!=qz[0]) flag++,idx=qz[0],aaa=i; } } } if(flag>1) printf("NOT UNIQUE\n"); else if(!flag) printf("NOT POSSIBLE\n"); else { int cnt=0; for(int i=1;cnt<p and i<=n;i++) if(i!=aaa) putchar(ch[i]),cnt++; puts(""); } } return 0; }
T2 這個A+B看上去很詭異... 分析一下可以發現實際上就是某個位置既放了A又放了B
這就等價於放小於等於N個A和小於等於N個B且總和=K的方案數 可以枚舉A放了i個,算出B放了j個,然后答案加上C(N,i)*C(N,j)就行了
一堆沙雕特判出題人真是有毒

#include<set> #include<map> #include<cmath> #include<queue> #include<cctype> #include<vector> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using std::min; using std::max; using std::swap; using std::vector; typedef double db; typedef long long ll; const int N=10000005; const int mod=998244353; #define pb(A) push_back(A) #define pii std::pair<int,int> #define mp(A,B) std::make_pair(A,B) int n;ll a,b,k; int fac[N],ifac[N]; int ksm(int a,int b){ int ans=1; while(b){ if(b&1) ans=(ll)ans*a%mod; a=(ll)a*a%mod;b>>=1; } return ans; } ll getint(){ ll X=0,w=0;char ch=0; while(!isdigit(ch))w|=ch=='-',ch=getchar(); while( isdigit(ch))X=X*10+ch-48,ch=getchar(); if(w) return -X;return X; } int C(int n,int m){ return (ll)fac[n]*ifac[m]%mod*ifac[n-m]%mod; } signed main(){ n=getint(),a=getint(),b=getint(),k=getint(); fac[0]=ifac[0]=1; for(int i=1;i<=n;i++) fac[i]=(ll)fac[i-1]*i%mod; ifac[n]=ksm(fac[n],mod-2); for(int i=n-1;i;i--) ifac[i]=(ll)ifac[i+1]*(i+1)%mod; int ans=0; if(!b){ if(!a){ if(k) return printf("0"),0; return printf("%d\n",ksm(2,2*n)),0; } ll q=k/a; if(k%a) return printf("0"),0; if(q>n) return printf("0"),0; for(int i=0;i<=n;i++) ans=((ll)ans+(ll)C(n,q)*C(n,i)%mod)%mod; return printf("%d",ans),0; } for(int i=0;i<=n;i++){ if((k-a*i)%b) continue; if(k-a*i<0) break; ll j=(k-a*i)/b; if(j>n) continue; ans=((ll)ans+(ll)C(n,i)*C(n,j)%mod)%mod; } printf("%d\n",ans); return 0; }
T3 看到點數這么小直接想floyd+矩乘啊 降智越來越嚴重了啊啊啊
直接動態dp就行了

#include<set> #include<map> #include<cmath> #include<queue> #include<cctype> #include<vector> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using std::min; using std::max; using std::swap; using std::vector; const int M=2e3+5; const int N=1e5+5; typedef double db; typedef long long ll; #define pb(A) push_back(A) #define pii std::pair<int,int> #define mp(A,B) std::make_pair(A,B) #define ls cur<<1 #define rs cur<<1|1 #define lss ls,l,mid,ql,qr #define rss rs,mid+1,r,ql,qr pii qj[10005]; int dis[10][10]; int n,sbctr,cnts[2005]; struct Mat{ int a[10][10]; Mat(){ memset(a,0x3f,sizeof a); for(int i=0;i<9;i++) a[i][i]=0; } friend Mat operator*(Mat x,Mat y){ Mat z; for(int i=0;i<9;i++) for(int k=0;k<9;k++) for(int j=0;j<9;j++) z.a[i][j]=min(z.a[i][j],min(x.a[i][k]+y.a[k][j],min(x.a[i][j],y.a[i][j]))); return z; } }sum[M<<2],val[M]; struct Edge{ int x,y,prio,dis; friend bool operator<(Edge a,Edge b){ return a.prio<b.prio; } }edge[N]; int getint(){ int X=0,w=0;char ch=0; while(!isdigit(ch))w|=ch=='-',ch=getchar(); while( isdigit(ch))X=X*10+ch-48,ch=getchar(); if(w) return -X;return X; } void modify(int cur,int l,int r,int ql,int qr,Mat c){ if(l==r){sum[cur]=c;return;} int mid=l+r>>1; ql<=mid?modify(lss,c):modify(rss,c); sum[cur]=sum[ls]*sum[rs]; } Mat query(int cur,int l,int r,int ql,int qr){ if(ql<=l and r<=qr) return sum[cur]; int mid=l+r>>1; if(qr<=mid) return query(lss); if(mid<ql) return query(rss); return query(lss)*query(rss); } void build(int cur,int l,int r){ if(l==r){ sum[cur]=val[l]; return; } int mid=l+r>>1; build(ls,l,mid);build(rs,mid+1,r); sum[cur]=sum[ls]*sum[rs]; } signed main(){ n=getint(),sbctr=getint(); for(int i=1;i<=n;i++){ edge[i].prio=getint(),edge[i].x=getint(),edge[i].y=getint(),edge[i].dis=getint(); val[edge[i].prio].a[edge[i].x][edge[i].y]=min(val[edge[i].prio].a[edge[i].x][edge[i].y],edge[i].dis); } for(int i=1;i<=2000;i++) for(int k=0;k<9;k++) for(int a=0;a<9;a++) for(int b=0;b<9;b++) val[i].a[a][b]=min(val[i].a[a][b],val[i].a[a][k]+val[i].a[k][b]); build(1,1,2000); int T=getint(); while(T--){ int s=getint(),t=getint(),tot=getint(); Mat z; for(int i=0;i<9;i++) z.a[i][i]=0; for(int i=1;i<=tot;i++) qj[i].first=getint(),qj[i].second=getint(); std::sort(qj+1,qj+1+tot); for(int i=1;i<=tot;i++) z=z*query(1,1,2000,qj[i].first,qj[i].second); printf("%d\n",z.a[s][t]>=0x3f3f3f3f?-1:z.a[s][t]); } return 0; }
2018.10.27
今天的題好勁啊!
md文件打錯了掉了十幾名fuck
T1 CDQ板子題多個0??
哈哈哈哈哈哈哈zyz寫樹套樹就12分哈哈哈哈哈哈哈
考慮計算這三個東西 x=[ai<aj][bi<bj],y=[bi<bj][ci<cj],z=[ci<cj][ai<aj]
觀察到每個無序對(i,j)都被計算了至少一遍。注意是無序對,如果有序對(i,j)滿足0個或1個條件的話,(j,i)就一定滿足3個或2個了。在上面算出來的xyz中,滿足3個的無序對算了3次,滿足2個的無序對算了1次,我們的目的是求出滿足3個的個數。顯然用x+y+z減去C(n,2)再除以2就好了。
mb洛谷瞧不起我不給我分配好評測機導致現在還T着不管了

#include<set> #include<map> #include<cmath> #include<queue> #include<cctype> #include<vector> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using std::min; using std::max; using std::swap; using std::vector; const int N=2e6+6; typedef double db; typedef long long ll; #define pb(A) push_back(A) #define pii std::pair<int,int> #define all(A) A.begin(),A.end() #define mp(A,B) std::make_pair(A,B) unsigned int SA,SB,SC; int n,a[N],b[N],c[N],f[N],pos[N]; void add(int x){ while(x<=n) f[x]++,x+=x&-x; } int query(int x){ int now=0; while(x) now+=f[x],x-=x&-x; return now; } unsigned int rd(){ SA^=SA<<16;SA^=SA>>5;SA^=SA<<1; unsigned int t=SA;SA=SB;SB=SC;SC^=t^SA;return SC; } void gen(int *P){ for (int i=1;i<=n;++i) P[i]=i; for (int i=1;i<=n;++i) swap(P[i],P[1+rd()%n]); } ll sol(int *a,int *b){ for(int i=1;i<=n;i++) pos[a[i]]=i; ll now=0; for(int i=1;i<=n;i++) now+=query(b[pos[i]]),add(b[pos[i]]); for(int i=1;i<=n;i++) f[i]=0; return now; } signed main(){ freopen("cdq.in","r",stdin);freopen("cdq.out","w",stdout); scanf("%d%u%u%u",&n,&SA,&SB,&SC); gen(a);gen(b);gen(c); ll ans=sol(a,b)+sol(b,c)+sol(c,a); printf("%lld\n",(ans-(ll)n*(n-1)/2)/2); return 0; }
T2 吼題啊吼題
其實考場上立馬想到了狀壓質因子,但是之前一直在想T1導致時間不是很夠做這題的時候就非常慌張,連一個2e8以內的約數個數表都沒想着打,就以為至少得有5000個了,然后算了一下空間開不下瞬間心態爆炸放棄寫正解了。其實i207M這個博客寫的挺清楚的,1e8以內最多只有800個因數,這就很可做了。
然后考場上還有一個一直糾結的地方是不會求強制不包含某個元素的方案數。想到了前綴后綴合並但是感覺很不優美,感覺這個東西可以像消失之物那道題一樣倒着推回去,但是死活過不了樣例不知道哪里錯了最后放棄夢想敲了55分暴力。
正解實際上就是前后綴合並,但是暴力合並一次是O(4^16)的,考慮降低復雜度。
先把暴力合並的式子寫下來長這樣:f[a|b][c|d]+=x[a][c]*y[b][d] 這是一個集合並的卷積,可以用莫比烏斯變換實際上就是高維前綴和快速算出來,復雜度能降低成O(16*2^16) 。一會應該會新開個博文寫一下這個東西
但是求出來的並不是我們想求的而是一個子集和,對於每個數我們還需要減去它的子集和。這里如果用O(3^16)暴力減的話就很不優美了,我們還是可以通過高維差分再把這個子集和減回去,復雜度仍然是O(16*2^16)的,做到這樣的復雜度就很優秀了。 總復雜度O(800*16*2^16)。

#pragma GCC optimize(2) #include<set> #include<map> #include<cmath> #include<queue> #include<cctype> #include<vector> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using std::min; using std::max; using std::swap; using std::vector; const int K=8; const int N=1005; typedef double db; typedef long long ll; const int mod=1e9+7; #define pb(A) push_back(A) #define pii std::pair<int,int> #define all(A) A.begin(),A.end() #define mp(A,B) std::make_pair(A,B) int n,A,B,q; std::map<int,int> fs; int pc,p[K],cnts[2][K]; int val[2][N],tot,ans[N],g[1<<K][1<<K]; int f[1<<K][1<<K],qz[N][1<<K][1<<K],hz[N][1<<K][1<<K]; int getint(){ int X=0,w=0;char ch=0; while(!isdigit(ch))w|=ch=='-',ch=getchar(); while( isdigit(ch))X=X*10+ch-48,ch=getchar(); if(w) return -X;return X; } signed main(){ n=getint(),A=getint(),B=getint(),q=getint(); if(B%A){ puts("0"); while(q--) puts("0"); return 0; } int sq=sqrt(B),x=B; for(int i=2;i<=sq;i++){ if(x%i==0){ p[++pc]=i; while(x%i==0) x/=i,cnts[0][pc]++; } } if(x>1) p[++pc]=x,cnts[0][pc]=1; x=A; for(int i=1;i<=pc;i++){ while(x%p[i]==0) x/=p[i],cnts[1][i]++; } for(int i=1;i<=sq and i<=n;i++){ if(B%i==0 and i%A==0){ tot++;fs[i]=tot;x=i; for(int j=1;j<=pc;j++){ int now=0; while(x%p[j]==0) now++,x/=p[j]; if(now==cnts[0][j]) val[0][tot]|=1<<j-1; if(now==cnts[1][j]) val[1][tot]|=1<<j-1; } } int pp=B/i; if(i*i==B) continue; if(pp>n) continue; if(B%pp==0 and pp%A==0){ tot++;fs[pp]=tot;x=pp; for(int j=1;j<=pc;j++){ int now=0; while(x%p[j]==0) now++,x/=p[j]; if(now==cnts[0][j]) val[0][tot]|=1<<j-1; if(now==cnts[1][j]) val[1][tot]|=1<<j-1; } } } int maxn=1<<pc; qz[0][0][0]=1; for(int i=1;i<=tot;i++){ memcpy(qz[i],qz[i-1],sizeof qz[i-1]); for(int j=maxn-1;~j;j--) for(int p=maxn-1;~p;p--) (qz[i][j|val[0][i]][p|val[1][i]]+=qz[i][j][p])%=mod; } hz[tot+1][0][0]=1; for(int i=tot;i;i--){ memcpy(hz[i],hz[i+1],sizeof hz[i+1]); for(int j=maxn-1;~j;j--) for(int p=maxn-1;~p;p--) (hz[i][j|val[0][i]][p|val[1][i]]+=hz[i][j][p])%=mod; } int las=qz[tot][maxn-1][maxn-1]; memset(ans,-1,sizeof ans); while(q--){ int x=getint(); if(x%A or B%x){ puts("0"); continue; } int idx=fs[x]; if(!val[0][idx] and !val[1][idx]){ printf("%d\n",las); continue; } if(~ans[idx]){ printf("%d\n",ans[idx]); continue; } for(int p=0;p<pc;p++) for(int i=0;i<maxn;i++) if(i>>p&1){ for(int j=0;j<maxn;j++) (qz[idx-1][i][j]+=qz[idx-1][i^(1<<p)][j])%=mod, (hz[idx+1][i][j]+=hz[idx+1][i^(1<<p)][j])%=mod; } for(int p=0;p<pc;p++) for(int j=0;j<maxn;j++) if(j>>p&1){ for(int i=0;i<maxn;i++) (qz[idx-1][i][j]+=qz[idx-1][i][j^(1<<p)])%=mod, (hz[idx+1][i][j]+=hz[idx+1][i][j^(1<<p)])%=mod; } for(int i=0;i<maxn;i++) for(int j=0;j<maxn;j++) g[i][j]=(ll)qz[idx-1][i][j]*hz[idx+1][i][j]%mod; for(int p=0;p<pc;p++) for(int i=0;i<maxn;i++) if(i>>p&1) for(int j=0;j<maxn;j++) g[i][j]=(g[i][j]-g[i^(1<<p)][j]+mod)%mod; for(int p=0;p<pc;p++) for(int j=0;j<maxn;j++) if(j>>p&1) for(int i=0;i<maxn;i++) g[i][j]=(g[i][j]-g[i][j^(1<<p)]+mod)%mod; printf("%d\n",ans[idx]=(las-g[maxn-1][maxn-1]+mod)%mod); } return 0; }
10.29-10.30
找狀態
Day1
T1 數據范圍顯然O(n^2+q)的復雜度然后差分一下就沒了
T2 可以暴力記搜 考場上自以為想到了它的最優策略結果想了個錯的 其實記搜的時候前后取一個max就行了也不用想太多 該暴力的時候還是應該暴力啊
T3 比較麻煩的樹形DP
Day2
T1 有相同質因子的數連邊並查集就行了
T2 可以折半做 其實這種數據范圍比較曖昧的比如d<=30甚至d<=20除了狀壓都可以想到折半做 然后bitset優化一下就行了 然后學習了一下bitset怎么優化這玩意兒 思路要反着想挺煩人的

#include<set> #include<map> #include<cmath> #include<queue> #include<bitset> #include<cctype> #include<vector> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using std::min; using std::max; using std::swap; using std::bitset; using std::vector; const int N=91; const int maxn=1<<11; typedef double db; typedef long long ll; #define pb(A) push_back(A) #define pii std::pair<int,int> #define all(A) A.begin(),A.end() #define mp(A,B) std::make_pair(A,B) int n,m,d,cnts[1<<21]; bitset<N> g0[N],g1[N],f[11][maxn+5],g[11][N][maxn+5]; int getint(){ int X=0,w=0;char ch=0; while(!isdigit(ch))w|=ch=='-',ch=getchar(); while( isdigit(ch))X=X*10+ch-48,ch=getchar(); if(w) return -X;return X; } signed main(){ freopen("y.in","r",stdin);freopen("y.out","w",stdout); n=getint(),m=getint(),d=getint(); int k1=d>>1,k2=d-k1; for(int i=1;i<=m;i++){ int x=getint(),y=getint(),z=getint(); if(z) g1[x][y]=g1[y][x]=1; else g0[x][y]=g0[y][x]=1; } f[0][0][1]=1; for(int dep=1;dep<=k1;dep++){ for(int i=0;i<1<<k1;i++){ for(int j=1;j<=n;j++){ if(f[dep-1][i][j]){ f[dep][i<<1|1]|=g1[j]; f[dep][i<<1]|=g0[j]; } } } } for(int i=1;i<=n;i++) g[0][i][0][i]=1; for(int dep=1;dep<=k2;dep++){ for(int i=0;i<1<<k2;i++){ for(int j=1;j<=n;j++){ for(int p=1;p<=n;p++){ if(g[dep-1][j][i][p]){ g[dep][j][i<<1|1]|=g1[p]; g[dep][j][i<<1]|=g0[p]; } } } } } for(int i=0;i<1<<k1;i++){ if(f[k1][i].any()){ for(int j=1;j<=n;j++){ if(f[k1][i][j]){ for(int p=0;p<1<<k2;p++){ if(g[k2][j][p].any()) cnts[i<<k2|p]=1; } } } } } int ans=0; for(int i=0;i<1<<d;i++) if(cnts[i]) ans++; printf("%d\n",ans); return 0; }
T3 咕咕咕着先
總結一下吧這兩天考的都這么差
其實最近心太浮躁了,還有就是每天切了傻逼T1之后就有點想放棄思考了 自己做出來的別人肯定也能做出來所以應該更努力的去想T2T3正解才對
還有就是暴力怎么打都打不對了 這是最嚴重的問題 每天即使把能打的暴力分寫對甚至把寫了但寫掛的暴力分寫對也能排大概rank3,4這樣 所以暴力寫對也是很重要的一個東西
再有就是時間分配可能是個問題 大概安排一下以后考試(是那種noip級別的考試)的時間吧:5min先讀完3道題 30min爭取切掉T1並過拍 30min寫好T2T3暴力 然后抉擇T2和T3去想哪個 花上1~2h去想去寫這道題盡最大可能做出來 剩下30min想另一道題高分暴力 但主要還是檢查空間 檢查文件
嗯下次考試先用這個策略試一試
最主要的還是暴力要寫對
還有10天,不努力就真晚了
悔不當初