春節這幾天打了不少場比賽,下面作一個總結和部分題解(沒寫的不會作)c++
這是我打的第一場div1,div1和div2有很大的區別。這一次一開始打就卡在了A題,半個多小時才作出來。。spa
題意:給你一個由1和2組成的序列,你能夠翻轉其中一段,求翻轉後的最長不降低子序列。指針
數據範圍:\(1 \leqslant n \leqslant 2000\)code
看到這道題以後,能夠轉換爲求枚舉一個位置,你要使前面的1和後面的2之和儘量多。字符串
咱們能夠把這個序列分紅4段,每段長度均可覺得0,咱們須要把中間兩段交換。get
這樣咱們很容易有一個dp的思路,咱們要使第一段和第三段的1儘量多,使第二段和第四段的2儘量多。string
設\(dp[i][j]\)表示當前這一段的最後一位是i,當前在第j段的最大答案it
這樣轉移就很顯然了io
#include<bits/stdc++.h> using namespace std; #define REP(i,st,ed) for(register int i=st,i##end=ed;i<=i##end;++i) #define DREP(i,st,ed) for(register int i=st,i##end=ed;i>=i##end;--i) typedef long long ll; inline int read(){ int x; char c; int f=1; while((c=getchar())!='-' && (c>'9' || c<'0')); if(c=='-') c=getchar(),f=-1; x=c^'0'; while((c=getchar())>='0' && c<='9') x=(x<<1)+(x<<3)+(c^'0'); return x*f; } inline ll readll(){ ll x; char c; int f=1; while((c=getchar())!='-' && (c>'9' || c<'0')); if(c=='-') c=getchar(),f=-1; x=c^'0'; while((c=getchar())>='0' && c<='9') x=(x<<1ll)+(x<<3ll)+(c^'0'); return x*f; } inline bool chkmax(int &x,int y){return (y>x)?(x=y,1):0;} const int maxn=2000+10,inf=0x3f3f3f3f; int a[maxn],sum[maxn],dp[5][maxn]; int main(){ int n=read(); REP(i,1,n) a[i]=read(),sum[i]=sum[i-1]+a[i]-1; dp[0][0]=0; REP(i,1,4) REP(j,0,n) REP(k,0,j) if(!(i&1)) chkmax(dp[i][j],dp[i-1][k]+sum[j]-sum[k]); else chkmax(dp[i][j],dp[i-1][k]+(j-sum[j])-(k-sum[k])); printf("%d\n",dp[4][n]); return 0; }
題意:\(f(x)=q(x)*(x+k)+p\),其中\(f(x)\),\(x+k\)與\(q(x)\)都是多項式,給出p與k,求一個合法的\(f(x)\)使得\(f(x)\)的每一位都不超過kclass
數據範圍:\(1 \leqslant p \leqslant 10^{18}\),\(1 \leqslant k \leqslant 2000\)
式子能夠轉化\(f(x)\)除以\(x+k\)餘數爲q,咱們考慮一個多項式除以\(x+k\)的過程,發現其實就是一個相似分解成k進制可是每一位要變符號的過程
因而就很好解決了
題目中說無解輸出-1,實際上沒有無解的狀況
#include<bits/stdc++.h> using namespace std; #define REP(i,st,ed) for(register int i=st,i##end=ed;i<=i##end;++i) #define DREP(i,st,ed) for(register int i=st,i##end=ed;i>=i##end;--i) typedef long long ll; inline int read(){ int x; char c; int f=1; while((c=getchar())!='-' && (c>'9' || c<'0')); if(c=='-') c=getchar(),f=-1; x=c^'0'; while((c=getchar())>='0' && c<='9') x=(x<<1)+(x<<3)+(c^'0'); return x*f; } inline ll readll(){ ll x; char c; int f=1; while((c=getchar())!='-' && (c>'9' || c<'0')); if(c=='-') c=getchar(),f=-1; x=c^'0'; while((c=getchar())>='0' && c<='9') x=(x<<1ll)+(x<<3ll)+(c^'0'); return x*f; } const int maxn=200000+10; ll mod; int k,n1,n2; bool work(int *a,int flag,int &n){ int b=flag; ll p=mod; while(p!=0){ int tmp=p%k; if(flag==-1) tmp=(k-tmp)%k; a[++n]=tmp; flag*=-1; p+=flag*tmp; if(p%k!=0) return 0; p/=k; } if((n&1) && b==-1) return 0; if(!(n&1) && b==1) return 0; return 1; } int a[maxn],b[maxn]; int main(){ n1=n2=0; mod=readll(),k=read(); if(work(b,1,n2)){ printf("%d\n",n2); REP(i,1,n2) printf("%d%c",b[i],i==iend?'\n':' '); return 0; } printf("-1\n"); return 0; }
這是一道歐拉定理的題目,計算幾何須要增強了。
這一場拿小號_ yyc _打的(早知道拿大號打了)
這題一開始被卡題面了,其實就是一個簡單的倍增。
(題意過於複雜不想解釋)
#include<bits/stdc++.h> using namespace std; #define REP(i,st,ed) for(register int i=st,i##end=ed;i<=i##end;++i) #define DREP(i,st,ed) for(register int i=st,i##end=ed;i>=i##end;--i) typedef long long ll; inline int read(){ int x; char c; int f=1; while((c=getchar())!='-' && (c>'9' || c<'0')); if(c=='-') c=getchar(),f=-1; x=c^'0'; while((c=getchar())>='0' && c<='9') x=(x<<1)+(x<<3)+(c^'0'); return x*f; } inline ll readll(){ ll x; char c; int f=1; while((c=getchar())!='-' && (c>'9' || c<'0')); if(c=='-') c=getchar(),f=-1; x=c^'0'; while((c=getchar())>='0' && c<='9') x=(x<<1ll)+(x<<3ll)+(c^'0'); return x*f; } const int maxn=400000+10; int v[maxn][20]; ll sum[maxn][20],w[maxn]; int main(){ int q=read(),cnt=1; ll lst=0; while(q--){ int ty=read(); ll x=readll(),y=readll(); x^=lst,y^=lst; if(ty==1){ w[++cnt]=y; v[cnt][0]=x; while(v[cnt][0] && w[v[cnt][0]]<y) v[cnt][0]=v[v[cnt][0]][0]; sum[cnt][0]=w[v[cnt][0]]; for(int j=1;v[cnt][j]=v[v[cnt][j-1]][j-1];++j) sum[cnt][j]=sum[cnt][j-1]+sum[v[cnt][j-1]][j-1]; } else{ ll ans=1; if(y<w[x]){ lst=0; printf("0\n"); continue; } y-=w[x]; DREP(i,19,0){ if(!v[x][i]) continue; if(sum[x][i]>y) continue; y-=sum[x][i]; x=v[x][i]; ans+=(1<<i); } lst=ans; printf("%lld\n",ans); } } return 0; }
題意:求\(\sum_{i=1}^{n} C_n^i*i^k\)
數據範圍:\(1 \leqslant n \leqslant 10^9\),\(1 \leqslant k \leqslant 5000\)
首先能夠把整數冪轉化一下
\[ \sum_{i=1}^{n} C_n^i*\sum_{j=0}^{i} S_k^j*C_i^j*j! \]
將\(C_n^i\)放到後面,把組合數拆開
\[ \sum_{i=1}^{n} \sum_{j=0}^{i} S_k^j*\frac {n!} {i!*(n-i)!}*\frac {i!} {j!*(i-j)!}*j! \]
約分
\[ n!*\sum_{i=1}^{n} \sum_{j=0}^{i} S_k^j*\frac {1} {(n-i)!*(i-j)!} \]
後面一部分轉化一下
\[ n!*\sum_{i=1}^{n} \sum_{j=0}^{i} S_k^j*C_{n-j}^{n-i}*(n-j)! \]
交換一下位置
\[ n!*\sum_{j=0}^{n} S_k^j*(n-j)!*\sum_{i=j}^{n} C_{n-j}^{n-i} \]
組合數能夠直接轉化了
\[ \sum_{j=0}^{n} S_k^j*C_n^j*j!*2^{n-j} \]
這樣由於j>k時\(S_k^j=0\)咱們能夠\(O(k^2)\)處理出答案了
#include<bits/stdc++.h> using namespace std; #define REP(i,st,ed) for(register int i=st,i##end=ed;i<=i##end;++i) #define DREP(i,st,ed) for(register int i=st,i##end=ed;i>=i##end;--i) typedef long long ll; inline int read(){ int x; char c; int f=1; while((c=getchar())!='-' && (c>'9' || c<'0')); if(c=='-') c=getchar(),f=-1; x=c^'0'; while((c=getchar())>='0' && c<='9') x=(x<<1)+(x<<3)+(c^'0'); return x*f; } inline ll readll(){ ll x; char c; int f=1; while((c=getchar())!='-' && (c>'9' || c<'0')); if(c=='-') c=getchar(),f=-1; x=c^'0'; while((c=getchar())>='0' && c<='9') x=(x<<1ll)+(x<<3ll)+(c^'0'); return x*f; } const int maxn=5e3+10,mod=1e9+7; int S[maxn][maxn],C[maxn],fac[maxn]; int ksm(int x,int y){ int res=1; while(y){ if(y&1) res=1ll*x*res%mod; x=1ll*x*x%mod; y>>=1; } return res; } int main(){ int n=read(),k=read(); S[0][0]=1; REP(i,1,k){ S[i][0]=0; S[i][i]=1; REP(j,1,i-1) S[i][j]=(S[i-1][j-1]+1ll*j*S[i-1][j]%mod)%mod; } C[0]=1; REP(i,1,min(n,k)) C[i]=1ll*C[i-1]*(n-i+1)%mod*ksm(i,mod-2)%mod; int ans=0; fac[0]=1; REP(i,1,k) fac[i]=1ll*fac[i-1]*i%mod; REP(i,0,min(n,k)) ans=(ans+1ll*S[k][i]*fac[i]%mod*C[i]%mod*ksm(2,n-i)%mod)%mod; printf("%d\n",ans); return 0; }
這場我是那小小號zhou8888打的(我爲何有這麼多小號)
題意:對於一個nn的矩陣和一個m,咱們設x爲每一個mm的矩陣都至少有一個0的狀況下1最多的個數。
給定q個x,求對於每一個x任意一組合法的n和m
數據範圍:\(1 \leqslant q \leqslant 100\),\(1 \leqslant x \leqslant 10^9\)
很容易發現\(x=n^2-(\lfloor \frac {n} {m}\rfloor)^2=(n-\lfloor \frac {n} {m}\rfloor)*(n+\lfloor \frac {n} {m}\rfloor)\)
因而枚舉x的約數就好了
#include<bits/stdc++.h> using namespace std; #define REP(i,st,ed) for(register int i=st,i##end=ed;i<=i##end;++i) #define DREP(i,st,ed) for(register int i=st,i##end=ed;i>=i##end;--i) typedef long long ll; inline int read(){ int x; char c; int f=1; while((c=getchar())!='-' && (c>'9' || c<'0')); if(c=='-') c=getchar(),f=-1; x=c^'0'; while((c=getchar())>='0' && c<='9') x=(x<<1)+(x<<3)+(c^'0'); return x*f; } inline ll readll(){ ll x; char c; int f=1; while((c=getchar())!='-' && (c>'9' || c<'0')); if(c=='-') c=getchar(),f=-1; x=c^'0'; while((c=getchar())>='0' && c<='9') x=(x<<1ll)+(x<<3ll)+(c^'0'); return x*f; } int main(){ int T=read(); while(T--){ int x=read(),flag=0; if(x==0){ printf("1 1\n"); continue; } for(int i=1;i*i<=x;++i){ if(x%i) continue; if(i*i==x) continue; int y=x/i; if((i&1)^(y&1)) continue; int Mid=(y+i)>>1; int nm=Mid-i; if(nm>Mid) continue; int m=Mid/nm; if(nm!=Mid/m) continue; printf("%d %d\n",Mid,m); flag=1; break; } if(!flag) printf("-1\n"); } return 0; }
題意:對於一個序列a定義\(f_a\)爲
一開始\(f_a=1,M=0\)
對於每一個\(2 \leqslant i \leqslant n\)若是$a_M < a_i \(咱們讓\)f_a+=a_M,M=i$
給你一個序列,輸出這個序列的\(n!\)種排列的\(f_a\)總和
數據範圍:\(1 \leqslant n \leqslant 10^6\),\(1 \leqslant a_i \leqslant 10^9\)
首先,確定是按位考慮,對於每個數,只有當他以前的數所有小於它時,它的貢獻纔會被計算
對於每一個數,咱們能夠考慮先把全部比他小的數放進去,而後這個數的位置就肯定了,必定是第一個每被放過的位置,剩下的數也隨便放
設\(p_i\)爲比\(a_i\)小的數的個數,因此答案爲
\[ \sum_{i=1}^{n} P_n^{p_i}*(n-p_i-1)!=\sum_{i=1}^{n} \frac {n!} {n-p_i} \]
#include<bits/stdc++.h> using namespace std; #define REP(i,st,ed) for(register int i=st,i##end=ed;i<=i##end;++i) #define DREP(i,st,ed) for(register int i=st,i##end=ed;i>=i##end;--i) typedef long long ll; inline int read(){ int x; char c; int f=1; while((c=getchar())!='-' && (c>'9' || c<'0')); if(c=='-') c=getchar(),f=-1; x=c^'0'; while((c=getchar())>='0' && c<='9') x=(x<<1)+(x<<3)+(c^'0'); return x*f; } inline ll readll(){ ll x; char c; int f=1; while((c=getchar())!='-' && (c>'9' || c<'0')); if(c=='-') c=getchar(),f=-1; x=c^'0'; while((c=getchar())>='0' && c<='9') x=(x<<1ll)+(x<<3ll)+(c^'0'); return x*f; } const int maxn=2e6+10,mod=1e9+7; int ksm(int x,int y){ int res=1; while(y){ if(y&1) res=1ll*x*res%mod; x=1ll*x*x%mod; y>>=1; } return res; } int fac[maxn],ifac[maxn]; int a[maxn]; inline void add(int &x,int y){ x+=y; if(x>=mod) x-=mod; } int main(){ int n=read(); fac[0]=1; REP(i,1,n) fac[i]=1ll*i*fac[i-1]%mod; ifac[n]=ksm(fac[n],mod-2); DREP(i,n-1,0) ifac[i]=1ll*ifac[i+1]*(i+1)%mod; int ans=0; REP(i,1,n) a[i]=read(); sort(a+1,a+n+1); REP(i,1,n){ int len=1,k=i-1; while(i+1<=n && a[i]==a[i+1]) len++,++i; if(i==n) continue; add(ans,1ll*fac[n]*a[i]%mod*len%mod*ksm(n-k,mod-2)%mod); } printf("%d\n",ans); return 0; }
題意:給你一個字符串,長度爲n,令\(k=\lfloor log_2(n)\rfloor\),你能夠操做k次,第i次操做能夠刪除一個長度爲\(2^{i-1}\)的串,求最後剩下的字典序的串
數據範圍:\(1 \leqslant n \leqslant 5000\)
這樣的問題咱們通常都按位考慮,從第1位到第\((n-2^k+1)\)位,在以前的位最小的狀況下,這一位是什麼。
咱們每個二進制位上對應的長度都只能選一次,因此咱們設\(dp[i]\)表明當前這個字母可以刪那些長度,那麼咱們只要比較第i+pos位的大小就好了,上一個的最小值所在長度的集合,必須選其中至少一個,這樣就很好轉移了。
#include<bits/stdc++.h> using namespace std; #define REP(i,st,ed) for(register int i=st,i##end=ed;i<=i##end;++i) #define DREP(i,st,ed) for(register int i=st,i##end=ed;i>=i##end;--i) typedef long long ll; inline int read(){ int x; char c; int f=1; while((c=getchar())!='-' && (c>'9' || c<'0')); if(c=='-') c=getchar(),f=-1; x=c^'0'; while((c=getchar())>='0' && c<='9') x=(x<<1)+(x<<3)+(c^'0'); return x*f; } inline ll readll(){ ll x; char c; int f=1; while((c=getchar())!='-' && (c>'9' || c<'0')); if(c=='-') c=getchar(),f=-1; x=c^'0'; while((c=getchar())>='0' && c<='9') x=(x<<1ll)+(x<<3ll)+(c^'0'); return x*f; } const int maxn=5000+10; bool dp[14][maxn],f[maxn]; char s[maxn]; int main(){ scanf("%s",s+1); int n=strlen(s+1); int m=0; while((1<<m+1)<=n) ++m; f[0]=1; REP(k,1,n-(1<<m)+1){ REP(i,0,m) REP(j,0,(1<<m)-1) dp[i][j]=0; REP(i,0,(1<<m)-1) dp[0][i]=f[i]; REP(i,0,m-1) REP(j,0,(1<<m)-1) if(dp[i][j]){ dp[i+1][j]=1; dp[i+1][j|(1<<i)]=1; } int mn=40; REP(i,0,(1<<m)-1){ if(!dp[m][i]) continue; mn=min(mn,(int)(s[i+k]-'a')); } char c=mn+'a'; putchar(c); REP(i,0,(1<<m)-1){ f[i]=dp[m][i]; if(c!=s[i+k]) f[i]=0; } } putchar('\n'); return 0; }
好像難題都沒作???
通過這場以後個人小號_ yyc _和個人大號同分了。。。
題意:維護一個集合,每次加入一個大於集合內全部數的整數。詢問這個集合子集的最大值減去平均值的最大值。
數據範圍:\(1 \leqslant q \leqslant 5*10^5\)
大力猜結論!!
他的詢問咱們能夠考慮是最大值儘量大,且使平均值儘量小
首先,最大值必定取得是最後加進集合中的值,咱們能夠用反證法來證
而後咱們要使平均值儘量小,咱們就每次講比平均值小的數加進答案中。
因爲每次將最大值變大,因此每次加進來一個數後平均值是遞增的。
這樣咱們就能夠用一個指針來維護了
#include<bits/stdc++.h> using namespace std; #define REP(i,st,ed) for(register int i=st,i##end=ed;i<=i##end;++i) #define DREP(i,st,ed) for(register int i=st,i##end=ed;i>=i##end;--i) typedef long long ll; inline int read(){ int x; char c; int f=1; while((c=getchar())!='-' && (c>'9' || c<'0')); if(c=='-') c=getchar(),f=-1; x=c^'0'; while((c=getchar())>='0' && c<='9') x=(x<<1)+(x<<3)+(c^'0'); return x*f; } inline ll readll(){ ll x; char c; int f=1; while((c=getchar())!='-' && (c>'9' || c<'0')); if(c=='-') c=getchar(),f=-1; x=c^'0'; while((c=getchar())>='0' && c<='9') x=(x<<1ll)+(x<<3ll)+(c^'0'); return x*f; } const int maxn=5e5+10; int a[maxn]; double sum[maxn]; int main(){ int q=read(),L=0,R=0; double lst=0; double ans=0; while(q--){ int ty=read(); if(ty==1){ ++R; lst=read(); sum[R]=sum[R-1]+lst; ans=(sum[L]+lst)/(L+1); while(L+1<R && sum[L+1]-sum[L]<ans){ ++L; ans=(sum[L]+lst)/(L+1); } } else printf("%.10lf\n",lst-ans); } return 0; }
頭一次參加uoj的比賽,難度。。。
C題fst是由於我不知道爲何加了一個特判,而後還判錯了。。