A. Easy Number Game
貪心將第$i$小的和第$2m-i+1$小的配對即可。
#include<cstdio> #include<algorithm> using namespace std; const int N=100010; int n,m,i,Case,a[N];long long ans; int main(){ scanf("%d",&Case); while(Case--){ scanf("%d%d",&n,&m); for(i=1;i<=n;i++)scanf("%d",&a[i]); sort(a+1,a+n+1); ans=0; for(i=1;i<=m;i++)ans+=1LL*a[i]*a[m*2+1-i]; printf("%lld\n",ans); } }
B. Lucky Man
$\sum_{i=1}^n\lfloor\frac{n}{n-i+1}\rfloor=\sum_{i=1}^n\lfloor\frac{n}{i}\rfloor=\sum_{i=1}^n d(i)$
設$n=p_1^{k_1}p_2^{k_2}...p_m^{k_m}$,則$d(n)=(k_1+1)(k_2+1)...(k_m+1)$,當且僅當所有$k$都為偶數時$d(n)$模$2$才為$1$。
故答案就是$n$以內完全平方數的個數的奇偶性,即$\lfloor{\sqrt{n}}\rfloor\bmod 2$,牛頓迭代法開根號即可。
C = int(raw_input()) for i in range(0, C): n = int(raw_input()) if n < 2 : print n continue m = 2 tmpn, len = n, 0 while tmpn > 0: tmpn /= 10 len += 1 base, digit, cur = 300, len / m, len % m while (cur + m <= base) and (digit > 0): cur += m digit -= 1 div = 10 ** (digit * m) tmpn = n / div x = int(float(tmpn) ** (1.0 / m)) x *= (10 ** digit) while True: x0 = x x = x + x * (n - x ** m) / (n * m) if x == x0: break while (x + 1) ** m <= n: x = x + 1 print x % 2
C. Travel along the Line
列舉$1$的個數,那麼$-1$和$0$的個數也就知道了,組合數計算概率即可,時間複雜度$O(n)$。
#include<cstdio> const int N=1000010,P=1000000007; int i,fac[N],inv[N],Case,n,m,x,y,z,ans,p[N]; inline int C(int n,int m){return n<m?0:1LL*fac[n]*inv[m]%P*inv[n-m]%P;} int main(){ for(fac[0]=fac[1]=1,i=2;i<N;i++)fac[i]=1LL*fac[i-1]*i%P; for(inv[0]=inv[1]=1,i=2;i<N;i++)inv[i]=1LL*(P-inv[P%i])*(P/i)%P; for(i=2;i<N;i++)inv[i]=1LL*inv[i-1]*inv[i]%P; for(p[0]=1,p[1]=inv[2],i=2;i<N;i++)p[i]=1LL*p[i-1]*p[1]%P; scanf("%d",&Case); while(Case--){ scanf("%d%d",&n,&m); ans=0; for(i=0;i<=n;i++){ x=i;//1 y=i-m;//-1 z=n-x-y;//0 if(y<0||y>n||z<0||z>n)continue; ans=(1LL*C(n,x)*C(n-x,y)%P*p[(x+y)*2+z]+ans)%P; } printf("%d\n",ans); } }
D. Machine Learning on a Tree
問題等價於給每個$y=-1$的點確定一個$0$或者$1$的顏色,使得樹上相鄰異色點對數量最少。
樹形DP,設$f[i][j]$表示考慮$i$的子樹,$i$點顏色為$j$時相鄰異色點對數量的最小值。
時間複雜度$O(n)$。
#include<cstdio> const int N=100010,inf=100000000; inline void up(int&a,int b){a>b?(a=b):0;} int Case,n,a[N],i,x,y,f[N][2],h[2],g[N],v[N<<1],nxt[N<<1],ed; inline void add(int x,int y){v[++ed]=y;nxt[ed]=g[x];g[x]=ed;} void dfs(int x,int y){ f[x][0]=f[x][1]=inf; if(a[x]==0)f[x][0]=0; else if(a[x]==1)f[x][1]=0; else f[x][0]=f[x][1]=0; for(int i=g[x];i;i=nxt[i]){ int u=v[i]; if(u==y)continue; dfs(u,x); for(int j=0;j<2;j++)h[j]=inf; for(int j=0;j<2;j++)for(int k=0;k<2;k++)up(h[j],f[x][j]+f[u][k]+(j^k)); for(int j=0;j<2;j++)f[x][j]=h[j]; } } int main(){ scanf("%d",&Case); while(Case--){ scanf("%d",&n); for(i=1;i<=n;i++)scanf("%d",&a[i]); for(ed=i=0;i<=n;i++)g[i]=0; for(i=1;i<n;i++)scanf("%d%d",&x,&y),add(x,y),add(y,x); dfs(1,0); up(f[1][0],f[1][1]); printf("%d\n",f[1][0]); } }
E. Yet Another Tree Query Problem
$[l,r]$的連通塊數量$=$點數$-$邊數$=r-l+1-$兩端點都在該區間內的樹邊數量。
二維數點問題,掃描線+樹狀陣列即可。
時間複雜度$O(n\log n)$。
#include<cstdio> #include<algorithm> using namespace std; const int N=200010; int Case,n,m,i,x,y,ans[N],bit[N],ce; struct E{int x,y,t;E(){}E(int _x,int _y,int _t){x=_x,y=_y,t=_t;}}e[N*3]; inline bool cmp(const E&a,const E&b){ if(a.x!=b.x)return a.x>b.x; return a.t<b.t; } inline void add(int x){for(;x<=n;x+=x&-x)bit[x]++;} inline int ask(int x){int t=0;for(;x;x-=x&-x)t+=bit[x];return t;} int main(){ scanf("%d",&Case); while(Case--){ scanf("%d%d",&n,&m); ce=0; for(i=1;i<n;i++){ scanf("%d%d",&x,&y); if(x>y)swap(x,y); e[++ce]=E(x,y,0); } for(i=1;i<=m;i++){ scanf("%d%d",&x,&y); ans[i]=y-x+1; e[++ce]=E(x,y,i); } sort(e+1,e+ce+1,cmp); for(i=1;i<=n;i++)bit[i]=0; for(i=1;i<=ce;i++)if(e[i].t)ans[e[i].t]-=ask(e[i].y);else add(e[i].y); for(i=1;i<=m;i++)printf("%d\n",ans[i]); } }
F. And Another Data Structure Problem
對於題中所給模數,每個數操作很少步之後就會進入迴圈。
每個迴圈長度都很小,且長度不同的迴圈一共$5$種,長度之和不超過$70$。
線段樹每個區間維護區間內尚未進入迴圈的數字個數、所有數的和、以及對於每種長度的迴圈,假設長度為$len$,維護$f[0..len-1]$,其中$f[i]$表示若繼續在這個區間整體立方操作$i$次,在$len$長度的迴圈中所有數的和。
對於立方操作,若區間還存在尚未進入迴圈的數字,則暴力遞迴修改,否則打標記。
標記下放/生效時,假設標記是區間整體操作了$p$次,那麼將$f[i]$賦值給$f[(i-p)\bmod len]$即可。
時間複雜度$O(n\log n\times 常數)$。
#include<cstdio> #include<algorithm> #include<cstdlib> #include<cstring> using namespace std; const int N=100010,M=262150,P=99971; int i,j,k,vis[N],f[N],q[N],m,mark[N]; int tot,cur,cnt,have[N],len[100],st[100],en[100],posx[N],posy[N]; int tag[M],val[M][72],ok[M][5],sum[M],ret[M]; int Case,n,qqq,op,x,y,a[N]; inline void add1(int x,int p){ static int pool[1000]; tag[x]+=p; int s=sum[x]; for(int i=0;i<cnt;i++)if(ok[x][i]){ int l=len[i],ST=st[i]; int t=p%l; t=(l-t)%l; if(!t)continue; s-=val[x][ST]; if(s<0)s+=P; int j; memcpy(pool+t,val[x]+ST,(l-t)<<2); memcpy(pool,val[x]+ST+l-t,t<<2); memcpy(val[x]+ST,pool,l<<2); //for(j=0;j+t<l;j++)pool[j+t]=val[x][ST+j]; //for(;j<l;j++)pool[j+t-l]=val[x][ST+j]; //for(j=0;j<l;j++)val[x][ST+j]=pool[j]; s+=val[x][ST]; if(s>=P)s-=P; } sum[x]=s; } inline void pb(int x){ if(tag[x]){ add1(x<<1,tag[x]); add1(x<<1|1,tag[x]); tag[x]=0; } } inline void upd(int&a,int b){a=a+b<P?a+b:a+b-P;} inline void up(int x){ sum[x]=(sum[x<<1]+sum[x<<1|1])%P; ret[x]=ret[x<<1]+ret[x<<1|1]; for(int i=0;i<cnt;i++)ok[x][i]=ok[x<<1][i]+ok[x<<1|1][i]; if(!ret[x])for(int i=0;i<cur;i++){ val[x][i]=val[x<<1][i]; upd(val[x][i],val[x<<1|1][i]); } } inline void init(int x,int y){ for(int i=0;i<cnt;i++)ok[x][i]=0; sum[x]=y; if(!mark[y]){ ret[x]=1; }else{ ret[x]=0; for(int i=0;i<cur;i++)val[x][i]=0; int o=posx[y]; ok[x][o]=1; for(int i=0;i<len[o];i++){ val[x][st[o]+i]=y; y=f[y]; } } } void build(int x,int a,int b){ tag[x]=0; if(a==b){ init(x,::a[a]); return; } int mid=(a+b)>>1; build(x<<1,a,mid); build(x<<1|1,mid+1,b); up(x); } void change(int x,int a,int b,int c,int d){ if(c<=a&&b<=d){ if(!ret[x]){ add1(x,1); return; } if(a==b){ init(x,::a[a]=f[::a[a]]); return; } } pb(x); int mid=(a+b)>>1; if(c<=mid)change(x<<1,a,mid,c,d); if(d>mid)change(x<<1|1,mid+1,b,c,d); up(x); } int ask(int x,int a,int b,int c,int d){ if(c<=a&&b<=d)return sum[x]; pb(x); int mid=(a+b)>>1,t=0; if(c<=mid)t=ask(x<<1,a,mid,c,d); if(d>mid)t+=ask(x<<1|1,mid+1,b,c,d); return t%P; } int main(){ for(i=0;i<P;i++)f[i]=1LL*i*i%P*i%P; for(i=0;i<P;i++)have[i]=-1; for(i=0;i<P;i++)if(!vis[i]){ for(j=i;!vis[j];j=f[j])vis[j]=1; if(mark[j])continue; q[m=1]=j; for(k=f[j];k!=j;k=f[k])q[++m]=k; for(j=1;j<=m;j++)mark[q[j]]=1; if(have[m]==-1){ have[m]=cnt; len[cnt]=m; st[cnt]=cur; en[cnt]=cur+m; cur+=m; //[st,en) cnt++; } for(j=1;j<=m;j++)posx[q[j]]=have[m],posy[q[j]]=j-1; } scanf("%d",&Case); //Case=1; while(Case--){ scanf("%d%d",&n,&qqq); //n=100000; //qqq=100000; for(i=1;i<=n;i++){ scanf("%d",&a[i]); //a[i]=rand(); a[i]%=P; } build(1,1,n); while(qqq--){ //op=rand()%2+1; //x=rand()%n+1; //y=rand()%n+1; //if(x>y)swap(x,y); scanf("%d%d%d",&op,&x,&y); if(op==1)change(1,1,n,x,y); else printf("%d\n",ask(1,1,n,x,y)); } } }
G. Neighboring Characters
首先將環倍長,破環成鏈,那麼剩下的子串要滿足相鄰字元不同且首尾字元不同。
通過雙指標求出每一段極長的子串,滿足相鄰字元不同,設這一段長度為$len$,從$2$到$len$列舉剩下的串的長度$L$,若該子串長度為$len-L+1$的前字尾不能完全匹配,則說明存在距離為$L-1$的位置不同,也就能充當首尾,Hash判斷即可。
時間複雜度$O(n)$。
#include<cstdio> #include<cstring> typedef long long ll; const int N=2000010,D=233,P=1000000009; int C,n,m,i,j,k,len;char a[N],ans[N];ll pow[N],f[N]; int Case; inline ll hash(int l,int r){return((f[r]-f[l-1]*pow[r-l+1])%P+P)%P;} int main(){ for(pow[0]=i=1;i<N;i++)pow[i]=pow[i-1]*D%P; scanf("%d",&Case); while(Case--){ scanf("%s",a+1); n=strlen(a+1);m=n+n; for(i=1;i<=n;i++)a[i+n]=a[i]; for(i=1;i<=m;i++)f[i]=(f[i-1]*D+a[i])%P; for(i=0;i<n;i++)ans[i]='0'; for(i=1;i<=m;i=j+1){ for(j=i;j<m&&a[j]!=a[j+1];j++); len=j-i+1; for(k=2;k<=len&&k<=n;k++)if(hash(i,i+len-k)!=hash(j-len+k,j))ans[n-k]='1'; } for(i=0;i<n-1;i++)putchar(ans[i]);puts("1"); } return 0; }
H. Happy Sequence
$f[i][j]$表示長度為$i$的序列,最後一項為$j$的方案數,調和級數列舉$j$的倍數$k$轉移給$f[i+1][k]$即可。
時間複雜度$O(mn\log n)$。
#include<cstdio> const int N=2010,P=1000000007; int Case,n,m,i,j,k,f[N][N],ans; inline void up(int&a,int b){a=a+b<P?a+b:a+b-P;} int main(){ scanf("%d",&Case); while(Case--){ scanf("%d%d",&n,&m);//length m for(i=0;i<=m;i++)for(j=1;j<=n;j++)f[i][j]=0; for(i=1;i<=n;i++)f[1][i]=1; for(i=1;i<m;i++)for(j=1;j<=n;j++)for(k=j;k<=n;k+=j)up(f[i+1][k],f[i][j]); ans=0; for(i=1;i<=n;i++)up(ans,f[m][i]); printf("%d\n",ans); } }
I. Your Bridge is under Attack
因為點隨機生成,所以對所有點建立KD-Tree。
對於每個查詢操作,在KD-Tree上從根往下遞迴查詢,若查詢直線與當前點子樹的最小包圍矩形不相交,則顯然無解,直接退出。
因為隨機情況下答案很小,所以加上這條剪枝即可通過。
#include<cstdio> #include<algorithm> const int N=100010; using namespace std; typedef long long ll; int Case,n,m,i,root,cmp_d,ans,A,B; ll LB,LA,LC; struct node{ int d[2]; int l,r; int Max[2],Min[2]; int sum; }t[N]; inline bool cmp(const node&a,const node&b){ return a.d[cmp_d]<b.d[cmp_d]; } inline void umax(int&a,int b){if(a<b)a=b;} inline void umin(int&a,int b){if(a>b)a=b;} inline void up(int x){ if(t[x].l){ umax(t[x].Max[0],t[t[x].l].Max[0]); umin(t[x].Min[0],t[t[x].l].Min[0]); umax(t[x].Max[1],t[t[x].l].Max[1]); umin(t[x].Min[1],t[t[x].l].Min[1]); } if(t[x].r){ umax(t[x].Max[0],t[t[x].r].Max[0]); umin(t[x].Min[0],t[t[x].r].Min[0]); umax(t[x].Max[1],t[t[x].r].Max[1]); umin(t[x].Min[1],t[t[x].r].Min[1]); } } int build(int l,int r,int D){ int mid=(l+r)>>1; cmp_d=D; nth_element(t+l+1,t+mid+1,t+r+1,cmp); t[mid].Max[0]=t[mid].Min[0]=t[mid].d[0]; t[mid].Max[1]=t[mid].Min[1]=t[mid].d[1]; t[mid].sum=1; if(l!=mid)t[mid].l=build(l,mid-1,!D);else t[mid].l=0; if(r!=mid)t[mid].r=build(mid+1,r,!D);else t[mid].r=0; up(mid); return mid; } inline bool check(int xl,int xr,int yl,int yr){ ll t=-LB*xl+LC; if(LA*yl<=t&&t<=LA*yr)return 1; t=-LB*xr+LC; if(LA*yl<=t&&t<=LA*yr)return 1; t=-LA*yl+LC; if(LB*xl<=t&&t<=LB*xr)return 1; t=-LA*yr+LC; if(LB*xl<=t&&t<=LB*xr)return 1; return 0; } void ask(int x){ if(!check(t[x].Min[0],t[x].Max[0],t[x].Min[1],t[x].Max[1]))return; if(LB*t[x].d[0]+LA*t[x].d[1]==LC)ans++; if(t[x].l)ask(t[x].l); if(t[x].r)ask(t[x].r); } int main(){ scanf("%d",&Case); while(Case--){ scanf("%d%d",&n,&m); for(i=1;i<=n;i++)scanf("%d%d",&t[i].d[0],&t[i].d[1]); root=build(1,n,0); while(m--){ scanf("%d%d",&A,&B); LA=A; LB=B; LC=LA*LB; ans=0; ask(root); printf("%d\n",ans); } } }
J. Super Brain
按題意模擬即可。
#include<cstdio> const int N=1000010; int Case,n,i,a[N],b[N],f[N],g[N],ans; int main(){ scanf("%d",&Case); while(Case--){ scanf("%d",&n); for(i=1;i<=n;i++)scanf("%d",&a[i]),f[a[i]]=g[a[i]]=0; for(i=1;i<=n;i++)scanf("%d",&b[i]),f[b[i]]=g[b[i]]=0; for(i=1;i<=n;i++)f[a[i]]++; for(i=1;i<=n;i++)g[b[i]]++; for(i=1;i<=n;i++)if(f[a[i]]==1&&g[a[i]]==1)ans=a[i]; for(i=1;i<=n;i++)if(f[b[i]]==1&&g[b[i]]==1)ans=b[i]; printf("%d\n",ans); } }