高一下五月日记
5.1
闲话
- 早预备被拉去收拾楼下开家长会用的教室。上了三节学科自习后开家长会,然后放假。
- 十七中的宣传手册上都是熟人。
- 回到家后听群里说因 \(5.4\) 是校园开放日,遂当天不能穿校服。
5.2
闲话
- 颓。
5.3
闲话
- 早上起床后就准备返校了。进校前看见 \(HZ\) 为了开放日还专门翻新了纪念物。到宿舍后听其他同学说今天也不能穿校服。
- 下午 \(field\) 给其他人讲 \(AC\) 自动机。
- 高三的下午才放假。
做题纪要
CF995F Cowmpany Cowmpensation
-
设 \(f_{x,i}\) 表示 \(x\) 的工资为 \(i\) 时的方案数,状态转移方程为 \(f_{x,i}=\prod\limits_{y \in Son(x)}\sum\limits_{j=1}^{i}f_{y,j}\) 。
-
设 \(s_{x,i}\) 为 \(f_{x,i}\) 的前缀和,则 \(s_{x,i}\) 是关于 \(i\) 的 \(siz_{x}\) 次多项式。证明过程同 luogu P4463 [集训队互测 2012] calc 。
点击查看代码
const ll p=1000000007; ll f[3010][3010],s[3010][3010],x[3010],y[3010],limit; vector<ll>e[3010]; ll qpow(ll a,ll b,ll p) { ll ans=1; while(b) { if(b&1) ans=ans*a%p; b>>=1; a=a*a%p; } return ans; } void add(ll u,ll v) { e[u].push_back(v); } ll lagrange(ll n,ll _x) { ll ans=0; for(ll i=1;i<=n;i++) { ll up=y[i],down=1; for(ll j=1;j<=n;j++) if(i!=j) { up=up*(_x-x[j]+p)%p; down=down*(x[i]-x[j]+p)%p; } ans=(ans+up*qpow(down,p-2,p)%p)%p; } return ans; } void dfs(ll x) { for(ll i=1;i<=limit;i++) f[x][i]=1; for(ll i=0;i<e[x].size();i++) { dfs(e[x][i]); for(ll j=1;j<=limit;j++) f[x][j]=f[x][j]*s[e[x][i]][j]%p; } for(ll i=1;i<=limit;i++) s[x][i]=(s[x][i-1]+f[x][i])%p; } int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif ll n,d,u,i; cin>>n>>d; limit=min(d+1,n+1); for(i=2;i<=n;i++) { cin>>u; add(u,i); } dfs(1); for(i=1;i<=limit;i++) { x[i]=i; y[i]=s[1][i]; } cout<<lagrange(limit,d)<<endl; return 0; }
luogu P3270 [JLOI2016] 成绩比较
-
恰好 \(k\) 个人 不太不做,考虑二项式反演计算至少 \(k\) 个人。
-
每节课分开考虑,有 \(g_{i}=\dbinom{n-1}{i}\prod\limits_{j=1}^{m}\sum\limits_{k=1}^{u_{j}}\dbinom{n-1-i}{r_{j}-1}k^{n-r_{j}}(u_{j}-k)^{r_{j}-1}\) 。
-
\(\sum\limits_{k=1}^{u_{j}}k^{n-r_{j}}(u_{j}-k)^{r_{j}-1}\) 二项式定理将幂次展开后可知其是一个关于 \(u_{j}\) 的 \(n\) 次多项式。
点击查看代码
const ll p=1000000007; ll g[110],u[110],r[110],x[110],y[110],C[110][110]; ll qpow(ll a,ll b,ll p) { ll ans=1; while(b) { if(b&1) ans=ans*a%p; b>>=1; a=a*a%p; } return ans; } void init(ll n) { C[0][0]=C[1][0]=C[1][1]=1; for(ll i=2;i<=n;i++) { C[i][0]=1; for(ll j=1;j<=i;j++) C[i][j]=(C[i-1][j]+C[i-1][j-1])%p; } } ll lagrange(ll n,ll _x) { ll ans=0; for(ll i=1;i<=n;i++) { ll up=y[i],down=1; for(ll j=1;j<=n;j++) if(i!=j) { up=up*(_x-x[j]+p)%p; down=down*(x[i]-x[j]+p)%p; } ans=(ans+up*qpow(down,p-2,p)%p)%p; } return ans; } int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif ll n,m,k,ans=0,i,j; cin>>n>>m>>k; init(n); for(i=1;i<=m;i++) cin>>u[i]; for(i=1;i<=m;i++) cin>>r[i]; for(j=1;j<=m;j++) { for(i=1;i<=n+1;i++) { x[i]=i; y[i]=(y[i-1]+qpow(i,n-r[j],p)*qpow(u[j]-i,r[j]-1,p)%p)%p; } u[j]=lagrange(n+1,u[j]); } for(i=k;i<=n-1;i++) { g[i]=C[n-1][i]; for(j=1;j<=m;j++) g[i]=g[i]*C[n-1-i][r[j]-1]%p*u[j]%p; if((i-k)%2==0) ans=(ans+C[i][k]*g[i]%p)%p; else ans=(ans-C[i][k]*g[i]%p+p)%p; } cout<<ans<<endl; return 0; }
BZOJ1420 Discrete Root
-
同余两边同时取对数有 \(k \gamma(x) \equiv \gamma(a) \pmod{\varphi(p)}\) 。
-
使用 \(exgcd\) 求解 \(\gamma(x)\) 后求得其他幂次处的值即可。
点击查看代码
vector<ll>result,ans; void divide(ll n) { result.clear(); for(ll i=2;i*i<=n;i++) { if(n%i==0) { result.push_back(i); while(n%i==0) n/=i; } } if(n>1) result.push_back(n); } ll qpow(ll a,ll b,ll p) { ll ans=1; while(b) { if(b&1) ans=ans*a%p; b>>=1; a=a*a%p; } return ans; } bool check(ll x,ll p,ll phi) { if(qpow(x,phi,p)!=1) return false; for(ll i=0;i<result.size();i++) { if(qpow(x,phi/result[i],p)==1) return false; } return true; } ll min_pr(ll p,ll phi) { for(ll i=1;i<=p-1;i++) { if(check(i,p,phi)==true) return i; } return -1; } struct BSGS { unordered_map<ll,ll>vis; ll k,base; void init(ll g,ll p) { vis.clear(); k=sqrt(p)+1; for(ll i=0,mi=1;i<=k-1;i++,mi=mi*g%p) vis[mi]=i; base=qpow(qpow(g,k,p),p-2,p); } ll query(ll x,ll p,ll phi) { for(ll i=0,mi=x;i*k<=phi;i++,mi=mi*base%p) { if(vis.find(mi)!=vis.end()) return i*k+vis[mi]; } return -1; } }B; ll exgcd(ll a,ll b,ll &x,ll &y) { if(b==0) { x=1; y=0; return a; } else { ll d=exgcd(b,a%b,y,x); y-=a/b*x; return d; } } int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif ll k,p,a,phi,g,lg,x,y,d,t,i; cin>>p>>k>>a; phi=p-1; divide(phi); g=min_pr(p,phi); B.init(g,p); lg=B.query(a,p,phi); d=exgcd(k,phi,x,y); t=phi/d; if(lg%d==0) { for(i=(x%t+t)%t*(lg/d)%t;i<=phi-1;i+=t) ans.push_back(qpow(g,i,p)); sort(ans.begin(),ans.end()); } cout<<ans.size()<<endl; for(i=0;i<ans.size();i++) cout<<ans[i]<<endl; return 0; }
luogu P10515 转圈
-
\(\delta_{n}(m+1)\) 即为所求。
-
特判 \(n=m+1\) 时输出 \(2\) 。
点击查看代码
int prime[700010],vis[10000010],len=0; int qpow(int a,int b,int p) { int ans=1; while(b) { if(b&1) ans=1ll*ans*a%p; b>>=1; a=1ll*a*a%p; } return ans; } void isprime(int n) { memset(vis,0,sizeof(vis)); for(int i=2;i<=n;i++) { if(vis[i]==0) { len++; prime[len]=i; } for(int j=1;j<=len&&1ll*i*prime[j]<=n;j++) { vis[i*prime[j]]=1; if(i%prime[j]==0) break; } } } int delta(int x,int p,int phi) { int ans=phi; for(int i=1;i<=len&&prime[i]*prime[i]<=phi;i++) { while(ans%prime[i]==0&&qpow(x,ans/prime[i],p)==1) ans/=prime[i]; while(phi%prime[i]==0) phi/=prime[i]; } if(phi>1&&qpow(x,ans/phi,p)==1) ans/=phi; return ans; } int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int t,n,m,i; scanf("%d",&t); isprime(10000000); for(i=1;i<=t;i++) { scanf("%d%d",&n,&m); printf("%d\n",((m+1==n)?2:delta(m+1,n,n-1))); } return 0; }
BZOJ2219 数论之神
-
由 \(CRT\) 相关理论,不妨将 \(2k+1\) 进行质因数分解后每一个奇素数的若干次幂分开考虑,方案数之间相乘即可。
-
现在就只需要求出形如 \(x^{a} \equiv b \pmod{p_{i}^{c_{i}}}\) 的方案数。尝试取对数但要求 \(\gcd(b,p_{i}^{c_{i}})=1\) ,不妨同余两边同时除以 \(\gcd(b,p_{i}^{c_{i}})\) 然后再将方案进行相应放缩。
-
设 \(\gcd(b,p_{i}^{c_{i}})=p_{i}^{w}\) 则有 \((\dfrac{x}{p_{i}^{\frac{w}{a}}})^{a} \equiv \dfrac{b}{p_{i}^{w}} \pmod{p_{i}^{c_{i}-w}}\) ,求出 \(\dfrac{x}{p_{i}^{\frac{w}{a}}}\) 的方案数后再乘以 \(p_{i}^{w-\left\lceil \frac{w}{a} \right\rceil}\) 即可。
点击查看代码
vector<pair<ll,ll> >all; vector<ll>part; void divide(ll n,ll op) { if(op==1) { all.clear(); for(ll i=2;i*i<=n;i++) { if(n%i==0) { all.push_back(make_pair(i,0)); for(;n%i==0;n/=i) all.back().second++; } } if(n>1) all.push_back(make_pair(n,1)); } else { part.clear(); for(ll i=2;i*i<=n;i++) { if(n%i==0) { part.push_back(i); for(;n%i==0;n/=i); } } if(n>1) part.push_back(n); } } ll qpow(ll a,ll b,ll p) { ll ans=1; while(b) { if(b&1) ans=ans*a%p; b>>=1; a=a*a%p; } return ans; } ll spow(ll a,ll b) { ll ans=1; while(b) { if(b&1) ans=ans*a; b>>=1; a=a*a; } return ans; } bool check(ll x,ll p,ll phi) { if(qpow(x,phi,p)!=1) return false; for(ll i=0;i<part.size();i++) { if(qpow(x,phi/part[i],p)==1) return false; } return true; } ll min_pr(ll p,ll phi) { for(ll i=1;i<=p-1;i++) { if(check(i,p,phi)==true) return i; } return -1; } struct BSGS { unordered_map<ll,ll>vis; ll k,base; void init(ll g,ll p,ll phi) { vis.clear(); k=sqrt(p)+1; for(ll i=0,mi=1;i<=k-1;i++,mi=mi*g%p) vis[mi]=i; base=qpow(qpow(g,k,p),phi-1,p); } ll query(ll x,ll p,ll phi) { for(ll i=0,mi=x;i*k<=phi;i++,mi=mi*base%p) { if(vis.find(mi)!=vis.end()) return i*k+vis[mi]; } return -1; } }B; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif ll t,a,b,c,k,p,phi,g,d,lg,ans,i,j; cin>>t; for(;t>=1;t--) { cin>>a>>b>>k; ans=1; divide(2*k+1,1); for(i=0;i<all.size();i++) { p=spow(all[i].first,all[i].second); c=b%p; d=__gcd(c,p); for(j=0,k=1;j<=all[i].second;j++,k*=all[i].first) if(k==d) { ans*=spow(all[i].first,j-ceil(1.0*j/a)); break; } if(p!=d) { p/=d; c/=d; phi=p-p/all[i].first; divide(phi,2); g=min_pr(p,phi); B.init(g,p,phi); lg=B.query(c,p,phi); d=__gcd(a,phi); ans*=(lg%d==0)*d; } } cout<<ans<<endl; } return 0; }
5.4
闲话
- 早上体活一直到 \(7:30\) ,直接睡觉。
- 白天是校园开放日,新晋团员强制要求回来当志愿者。
- 午饭让去西扩食堂二楼吃饭。
- 临吃晚饭的时候高三返校了。
- 因晚上其他人要打模拟赛,所以和 \(miaomiao\) 交涉了下,所以不用打模拟赛了,并且让晚上先去 \(504\) 。
- 夜聊时听 @wang54321 讲了下在二南省集的经历。
做题纪要
HZTG2919. 小 w 的代数
luogu P11831 [省选联考 2025] 追忆
-
将 \(a_{i}\) 看做第二维限制条件后因为经常修改,线段树并不能很好地维护,不妨考虑操作序列分块套序列分块。
-
设 \(f_{x,i}\) 表示 \(x\) 能到达的点中 \(a_{y}\) 属于第 \(i\) 个块的 \(b_{y}\) 的最大值。
-
对操作序列分块时将修改的位置的影响消去,可以重新预处理一遍 \(f\) 。询问时注意加上本次修改的影响。
-
在 \(n,m,q\) 同阶时取块长为 \(n^{\frac{2}{3}}\) ,时间复杂度为 \(O(\frac{n^{2}}{w}+n^{\frac{5}{3}})\) 。
-
略带卡常。
点击查看代码
struct node { int nxt,to; }e[200010]; int head[100010],a[100010],b[100010],L[100010],R[100010],id[100010],pos[100010],op[2210],d[4410],x[2210],l[2210],r[2210],f[100010][50],cnt=0,klen,ksum; bitset<100010>g[100010],vis; void add(int u,int v) { cnt++; e[cnt]=(node){head[u],v}; head[u]=cnt; } void top_sort(int n) { for(int x=n;x>=1;x--) { g[x].reset(); g[x][x]=1; for(int i=head[x];i!=0;i=e[i].nxt) g[x]|=g[e[i].to]; } } void init(int n) { klen=2200; ksum=n/klen; for(int i=1;i<=ksum;i++) { L[i]=R[i-1]+1; R[i]=R[i-1]+klen; } if(R[ksum]<n) { ksum++; L[ksum]=R[ksum-1]+1; R[ksum]=n; } for(int i=1;i<=ksum;i++) for(int j=L[i];j<=R[i];j++) id[j]=i; } int query(int x,int l,int r) { int ans=0; if(id[l]==id[r]) { for(int i=l;i<=r;i++) if(g[x][pos[i]]==1) ans=max(ans,b[pos[i]]); } else { for(int i=l;i<=R[id[l]];i++) if(g[x][pos[i]]==1) ans=max(ans,b[pos[i]]); for(int i=id[l]+1;i<=id[r]-1;i++) ans=max(ans,f[x][i]); for(int i=L[id[r]];i<=r;i++) if(g[x][pos[i]]==1) ans=max(ans,b[pos[i]]); } for(int i=1;i<=d[0];i++) if(l<=a[d[i]]&&a[d[i]]<=r&&g[x][d[i]]==1) ans=max(ans,b[d[i]]); return ans; } void solve(int n,int len) { vis.reset(); memset(f,0,sizeof(f)); d[0]=0; for(int i=1;i<=len;i++) if(op[i]<=2) { d[0]++; d[d[0]]=x[i]; d[0]++; d[d[0]]=l[i]; vis[x[i]]=vis[l[i]]=1; } sort(d+1,d+1+d[0]); d[0]=unique(d+1,d+1+d[0])-(d+1); for(int x=n;x>=1;x--) { for(int i=head[x];i!=0;i=e[i].nxt) for(int j=1;j<=ksum;j++) f[x][j]=max(f[x][j],f[e[i].to][j]); if(vis[x]==0) f[x][id[a[x]]]=max(f[x][id[a[x]]],b[x]); } for(int i=1;i<=len;i++) { if(op[i]==1) { swap(a[x[i]],a[l[i]]); swap(pos[a[x[i]]],pos[a[l[i]]]); } if(op[i]==2) swap(b[x[i]],b[l[i]]); if(op[i]==3) printf("%d\n",query(x[i],l[i],r[i])); } } int main() { // #define Isaac #ifdef Isaac freopen("recall.in","r",stdin); freopen("recall.out","w",stdout); #endif int c,t,n,m,q,u,v,i,w; scanf("%d%d",&c,&t); for(;t>=1;t--) { cnt=0; memset(e,0,sizeof(e)); memset(head,0,sizeof(head)); scanf("%d%d%d",&n,&m,&q); init(n); for(i=1;i<=m;i++) { scanf("%d%d",&u,&v); add(u,v); } top_sort(n); for(i=1;i<=n;i++) { scanf("%d",&a[i]); pos[a[i]]=i; } for(i=1;i<=n;i++) scanf("%d",&b[i]); for(i=1;i<=q;i++) { w=(i-1)%klen+1; scanf("%d%d%d",&op[w],&x[w],&l[w]); if(op[w]==3) scanf("%d",&r[w]); if(i%klen==0||i==q) solve(n,w); } } return 0; }
T3236.圣诞树
「JOISC 2017 Day 3」長距離バス (Long Distance Coach)
- 详见 欢欢乐乐赛赛 B P186. 长途巴士 。
luogu P10774 BZOJ3563 DZY Loves Chinese
-
getline
大法好,暴力做最后一组询问即可。点击查看代码
int u[500010],v[500010],c[500010],vis[500010]; string s; struct DSU { int fa[500010],siz[500010]; void init(int n) { for(int i=1;i<=n;i++) { fa[i]=i; siz[i]=1; } } int find(int x) { return fa[x]==x?x:fa[x]=find(fa[x]); } void merge(int x,int y) { x=find(x); y=find(y); if(x!=y) { fa[x]=y; siz[y]+=siz[x]; } } }D; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int n,m,q,ans=0,len,i,j; scanf("%d%d",&n,&m); D.init(n); for(i=1;i<=m;i++) scanf("%d%d",&u[i],&v[i]); scanf("%d",&q); getline(cin,s); for(j=1;j<=q;j++) { getline(cin,s); len=c[0]=0; for(i=0;i<s.size();i++) { if('0'<=s[i]&&s[i]<='9') c[len]=c[len]*10+s[i]-'0'; else { len++; c[len]=0; } } for(i=1;i<=len;i++) c[i]^=(c[0]^len); if(j>=2) { if((c[0]^len)==ans) printf("Disconnected\n"); else printf("Connected\n"); ans=c[0]^len; } } for(i=1;i<=len;i++) vis[c[i]]=1; for(i=1;i<=m;i++) if(vis[i]==0) D.merge(u[i],v[i]); if(D.siz[D.find(1)]==n) printf("Connected\n"); else printf("Disconnected\n"); return 0; }
luogu P4869 albus就是要第一个出场
-
设线性基大小为 \(siz\) ,则 \(B\) 中所有数能被表示 \(2^{n-siz}\) 次。所以只需要知道 \(q\) 在去重后的 \(B\) 中的排名即可。
-
二分套 HDU3949 XOR 的做法是没有必要的。
-
注意到线性基中各数二进制最高位不同且可以替换成每一个二进制位仅在它这一位的基底上出现,故取 \(q\) 二进制表示下为 \(1\) 的位求出 \(<q\) 的数的个数进行计算即可。
点击查看代码
const int p=10086; struct Liner_Base { int d[40]; int insert(int x) { for(int i=31;i>=0;i--) { if((x>>i)&1) { if(d[i]==0) { d[i]=x; return 1; } x^=d[i]; } } return 0; } int query_rk(int x) { int ans=0,cnt=0; for(int i=0;i<=31;i++) { if(d[i]!=0) { if((x>>i)&1) ans|=(1<<cnt); cnt++; } } return ans+1; } }L; int qpow(int a,int b,int p) { int ans=1; while(b) { if(b&1) ans=ans*a%p; b>>=1; a=a*a%p; } return ans; } int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int n,x,siz=0,i; cin>>n; for(i=1;i<=n;i++) { cin>>x; siz+=L.insert(x); } cin>>x; cout<<((L.query_rk(x)-1)%p*qpow(2,n-siz,p)%p+1)%p<<endl; return 0; }
5.5
闲话
- 下午让 \(15:30\) 回班。但回班后发现其他奥赛实际要求下午一起床就回班写假期作业。
- 晚一开冲刺期末大会。被迫上台领取“突破之星”的奖状。
做题纪要
luogu P11713 [清华集训 2014] 玛里苟斯
-
对于二进制下第 \(i\) 位为 \(1\) 其贡献为 \(2^{i}\) ,又因为各有 \(\frac{1}{2}\) 的概率最终能取到(选择奇数、偶数次的概率相同),所以最终答案小数至多为 \(0.5\) 。
-
\(k=1,2\) 的做法同 冲刺CSP联训模拟2 T1 P294. 挤压 ,按位考虑贡献。
- 注意在 \(k=2\) 时线性无关的概率为 \(\frac{1}{4}\) ,否则为 \(\frac{1}{2}\) 。
-
否则因为保证最终答案 \(< 2^{63}\) 即 \(x<2^{22}\) ,直接枚举能否被表出即可。
点击查看代码
struct Liner_Base { ull d[70]; int insert(ull x) { for(int i=63;i>=0;i--) { if((x>>i)&1) { if(d[i]==0) { d[i]=x; return 1; } x^=d[i]; } } return 0; } bool check(ull x) { for(int i=63;i>=0;i--) { if((x>>i)&1) { if(d[i]==0) return 0; x^=d[i]; } } return 1; } ull query() { ull ans=0; for(int i=63;i>=0;i--) ans|=d[i]; return ans; } }L; __int128_t sx_pow(__int128_t x,int k) { if(k==3) return x*x*x; if(k==4) return x*x*x*x; if(k==5) return x*x*x*x*x; return 0; } int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int n,m,siz=0,flag,i,j,k; ull x; __int128_t ans=0,sum=0; cin>>n>>m; for(i=1;i<=n;i++) { cin>>x; siz+=L.insert(x); } siz=((m<=2)?m:siz); sum=L.query(); if(m==1) { for(i=63;i>=0;i--) if((sum>>i)&1) ans+=(1ull<<i); } else if(m==2) { for(i=63;i>=0;i--) if((sum>>i)&1) for(j=63;j>=0;j--) if((sum>>j)&1) { flag=1; for(k=63;k>=0;k--) flag&=(((L.d[k]>>i)&1)+((L.d[k]>>j)&1)!=1); // flag=1 表示线性相关,能同时表示出来 ans+=(1ull<<(i+j+flag)); } } else { for(i=0;i<=4200000;i++) if(L.check(i)) ans+=sx_pow(i,m); } printf("%lld",ans>>siz); if(ans%(1ull<<siz)!=0) printf(".5"); return 0; }
luogu P10778 BZOJ3569 DZY Loves Chinese II
-
强制在线和 luogu P10075 [GDKOI2024 普及组] 切割 中 \(10^{6}\) 的数据范围限制了 luogu P5227 [AHOI2013] 连通图 的线段树分治无法通过。
-
注意到对于原图的一张生成树中的树边,如果它与覆盖它的返祖边都断开了就会变得不连通。
-
不妨考虑异或哈希,对返祖边随机赋权,树边的权值为覆盖它的返祖边的权值的异或和。询问时通过线性基判断能否正确插入来得到是否同时出现。
-
随着 \(k\) 的逐渐扩大,错误性也越来越高。分析过程基本同 luogu P5556 圣剑护符 。
点击查看代码
mt19937_64 rng(random_device{}()); struct node { int nxt,to,id; }e[1000010]; int head[500010],u[500010],v[500010],vis[500010],ins[500010],cnt=0; ull w[500010]; void add(int u,int v,int id) { cnt++; e[cnt]=(node){head[u],v,id}; head[u]=cnt; } void dfs(int x,int fa) { vis[x]=ins[x]=1; for(int i=head[x];i!=0;i=e[i].nxt) { if(e[i].id!=fa) { if(vis[e[i].to]==0) dfs(e[i].to,e[i].id); else if(ins[e[i].to]==1) w[e[i].id]=rng(); w[fa]^=w[e[i].id]; } } ins[x]=0; } struct Liner_Base { ull d[70]; void clear() { memset(d,0,sizeof(d)); } int insert(ull x) { for(int i=63;i>=0;i--) { if((x>>i)&1) { if(d[i]==0) { d[i]=x; return 1; } x^=d[i]; } } return 0; } }L; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int n,m,q,k,x,ans=0,flag,i,j; scanf("%d%d",&n,&m); for(i=1;i<=m;i++) { scanf("%d%d",&u[i],&v[i]); add(u[i],v[i],i); add(v[i],u[i],i); } dfs(1,0); scanf("%d",&q); for(i=1;i<=q;i++) { scanf("%d",&k); L.clear(); flag=1; for(j=1;j<=k;j++) { scanf("%d",&x); x^=ans; flag&=L.insert(w[x]); } ans+=flag; if(flag==0) printf("Disconnected\n"); else printf("Connected\n"); } return 0; }
luogu P12389 COmPoUNdS
-
维护差分数组的哈希值即可。
点击查看代码
const int mod=1000003579,base=13331; int jc[1000010],a[1000010],p; struct SMT { struct SegmentTree { int lazy,hsh,len; }tree[4000010]; #define lson(rt) (rt<<1) #define rson(rt) (rt<<1|1) void pushup(int rt) { tree[rt].hsh=(1ll*tree[lson(rt)].hsh*jc[tree[rson(rt)].len]%mod+tree[rson(rt)].hsh)%mod; } void build(int rt,int l,int r) { tree[rt].len=r-l+1; if(l==r) { tree[rt].lazy=a[l]; tree[rt].hsh=(a[l]-a[l-1]+p)%p; return; } int mid=(l+r)/2; build(lson(rt),l,mid); build(rson(rt),mid+1,r); pushup(rt); } void update1(int rt,int l,int r,int pos,int val) { if(l==r) { tree[rt].hsh=(tree[rt].hsh+val)%p; return; } int mid=(l+r)/2; if(pos<=mid) update1(lson(rt),l,mid,pos,val); else update1(rson(rt),mid+1,r,pos,val); pushup(rt); } void update2(int rt,int l,int r,int x,int y,int val) { if(x<=l&&r<=y) { tree[rt].lazy=(tree[rt].lazy+val)%p; return; } int mid=(l+r)/2; if(x<=mid) update2(lson(rt),l,mid,x,y,val); if(y>mid) update2(rson(rt),mid+1,r,x,y,val); } int query1(int rt,int l,int r,int pos) { if(l==r) return tree[rt].lazy; int mid=(l+r)/2; if(pos<=mid) return (query1(lson(rt),l,mid,pos)+tree[rt].lazy)%p; else return (query1(rson(rt),mid+1,r,pos)+tree[rt].lazy)%p; } pair<int,int> query2(int rt,int l,int r,int x,int y) { if(x<=l&&r<=y) return make_pair(tree[rt].hsh,tree[rt].len); int mid=(l+r)/2; if(y<=mid) return query2(lson(rt),l,mid,x,y); if(x>mid) return query2(rson(rt),mid+1,r,x,y); pair<int,int>p=query2(lson(rt),l,mid,x,y); pair<int,int>q=query2(rson(rt),mid+1,r,x,y); return make_pair((1ll*p.first*jc[q.second]%mod+q.first)%mod,p.second+q.second); } }T; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int n,m,pd,l1,r1,l2,r2,x,i; scanf("%d%d%d",&n,&p,&m); jc[0]=1; for(i=1;i<=n;i++) { scanf("%d",&a[i]); jc[i]=1ll*jc[i-1]*base%mod; } T.build(1,1,n); for(i=1;i<=m;i++) { scanf("%d%d%d",&pd,&l1,&r1); if(pd==1) { scanf("%d",&x); T.update1(1,1,n,l1,x); if(r1+1<=n) T.update1(1,1,n,r1+1,(p-x)%p); T.update2(1,1,n,l1,r1,x); } else { scanf("%d%d",&l2,&r2); if(T.query1(1,1,n,l1)==T.query1(1,1,n,l2)) { if(r1>l1) { if(T.query2(1,1,n,l1+1,r1).first==T.query2(1,1,n,l2+1,r2).first) printf("Yes\n"); else printf("No\n"); } else printf("Yes\n"); } else printf("No\n"); } } return 0; }
luogu P10638 BZOJ4355 Play with sequence
-
做法同 UOJ 164. 【清华集训2015】V 。
点击查看代码
const ll inf=0x3f3f3f3f3f; ll a[300010]; struct SMT { struct SegmentTree { ll mn,se,cnt,cov,add; }tree[1200010]; #define lson(rt) (rt<<1) #define rson(rt) (rt<<1|1) void pushup(ll rt) { tree[rt].mn=min(tree[lson(rt)].mn,tree[rson(rt)].mn); if(tree[lson(rt)].mn<tree[rson(rt)].mn) { tree[rt].se=min(tree[lson(rt)].se,tree[rson(rt)].mn); tree[rt].cnt=tree[lson(rt)].cnt; } if(tree[lson(rt)].mn==tree[rson(rt)].mn) { tree[rt].se=min(tree[lson(rt)].se,tree[rson(rt)].se); tree[rt].cnt=tree[lson(rt)].cnt+tree[rson(rt)].cnt; } if(tree[lson(rt)].mn>tree[rson(rt)].mn) { tree[rt].se=min(tree[lson(rt)].mn,tree[rson(rt)].se); tree[rt].cnt=tree[rson(rt)].cnt; } } void build(ll rt,ll l,ll r) { tree[rt].cov=-1; if(l==r) { tree[rt].mn=a[l]; tree[rt].se=inf; tree[rt].cnt=1; return; } ll mid=(l+r)/2; build(lson(rt),l,mid); build(rson(rt),mid+1,r); pushup(rt); } void pushlazy(ll rt,ll add,ll cov) { tree[rt].mn+=add; if(tree[rt].se!=inf) tree[rt].se+=add; tree[rt].add+=add; if(tree[rt].cov!=-1) tree[rt].cov+=add; if(cov==-1||tree[rt].mn>=cov) return; tree[rt].mn=tree[rt].cov=cov; } void pushdown(ll rt) { pushlazy(lson(rt),tree[rt].add,tree[rt].cov); pushlazy(rson(rt),tree[rt].add,tree[rt].cov); tree[rt].add=0; tree[rt].cov=-1; } void update1(ll rt,ll l,ll r,ll x,ll y,ll val) { if(x<=l&&r<=y) { pushlazy(rt,val,-1); return; } pushdown(rt); ll mid=(l+r)/2; if(x<=mid) update1(lson(rt),l,mid,x,y,val); if(y>mid) update1(rson(rt),mid+1,r,x,y,val); pushup(rt); } void update2(ll rt,ll l,ll r,ll x,ll y,ll val) { if(tree[rt].mn>=val) return; if(x<=l&&r<=y&&tree[rt].se>val) { pushlazy(rt,0,val); return; } pushdown(rt); ll mid=(l+r)/2; if(x<=mid) update2(lson(rt),l,mid,x,y,val); if(y>mid) update2(rson(rt),mid+1,r,x,y,val); pushup(rt); } ll query(ll rt,ll l,ll r,ll x,ll y) { if(x<=l&&r<=y) return (tree[rt].mn==0)*tree[rt].cnt; pushdown(rt); ll mid=(l+r)/2,ans=0; if(x<=mid) ans+=query(lson(rt),l,mid,x,y); if(y>mid) ans+=query(rson(rt),mid+1,r,x,y); return ans; } }T; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif ll n,m,pd,l,r,x,i; cin>>n>>m; for(i=1;i<=n;i++) cin>>a[i]; T.build(1,1,n); for(i=1;i<=m;i++) { cin>>pd>>l>>r; if(pd==1) { cin>>x; T.update1(1,1,n,l,r,-inf); T.update2(1,1,n,l,r,x); } if(pd==2) { cin>>x; T.update1(1,1,n,l,r,x); T.update2(1,1,n,l,r,0); } if(pd==3) cout<<T.query(1,1,n,l,r)<<endl; } return 0; }
5.6
闲话
- 晚上的奥赛课改成地理自习了。
- 晚三因我们所在的教学楼和滏阳楼是学考考点,所以要收拾考场,但每天考完后还要再在班里上课,不考试的人去食堂上自习。
5.7
闲话
- 历史和地理在一个场考,只是座位号变了。题数好像改了,和之前做的时候不太一样。
- 考历史时的副监考是 \(miaomiao\) 。
(我刚打算进场,突然 \(miaomiao\) 探出头来)
我:不是,哥们?
\(miaomiao\) :嗯?进来吧。 - 晚上是政治自习。
5.8
闲话
- 生物学考完安排了两节奥赛课来代替去食堂上自习。
做题纪要
luogu P10779 BZOJ4316 小 C 的独立集
-
考虑在圆方树上进行 \(DP\) 。
-
将方点看做环内其他圆点向割点转移的载体,转移是一个类似求序列上最大独立集的形式。
点击查看代码
struct node { int nxt,to; }e[120010]; int head[50010],dfn[50010],low[50010],f[100010][2],dp[100010][2],tot=0,cnt=0,n,v_dcc=0; vector<int>g[100010],c; stack<int>s; void add(int u,int v) { cnt++; e[cnt]=(node){head[u],v}; head[u]=cnt; } void tarjan(int x) { tot++; dfn[x]=low[x]=tot; s.push(x); for(int i=head[x];i!=0;i=e[i].nxt) { if(dfn[e[i].to]==0) { tarjan(e[i].to); low[x]=min(low[x],low[e[i].to]); if(low[e[i].to]==dfn[x]) { v_dcc++; g[v_dcc].push_back(x); g[x].push_back(v_dcc); int tmp=0; while(e[i].to!=tmp) { tmp=s.top(); s.pop(); g[v_dcc].push_back(tmp); g[tmp].push_back(v_dcc); } } } else low[x]=min(low[x],dfn[e[i].to]); } } void dfs(int x,int fa) { f[x][0]=0; f[x][1]=1; for(int i=0;i<g[x].size();i++) if(g[x][i]!=fa) dfs(g[x][i],x); if(x>n) { c.clear(); for(int i=0;i<g[x].size();i++) if(g[x][i]!=fa) c.push_back(g[x][i]); if(c.size()==2) { f[fa][0]+=max({f[c[0]][0]+f[c[1]][1],f[c[0]][1]+f[c[1]][0],f[c[0]][0]+f[c[1]][0]}); f[fa][1]+=f[c[0]][0]+f[c[1]][0]; return; } dp[0][0]=f[c[0]][0]; dp[0][1]=-0x3f3f3f3f; for(int i=1;i<c.size();i++) { dp[i][0]=max(dp[i-1][0],dp[i-1][1])+f[c[i]][0]; dp[i][1]=dp[i-1][0]+f[c[i]][1]; } f[fa][1]+=dp[c.size()-1][0]; dp[0][0]=f[c[0]][0]; dp[0][1]=f[c[0]][1]; for(int i=1;i<c.size();i++) { dp[i][0]=max(dp[i-1][0],dp[i-1][1])+f[c[i]][0]; dp[i][1]=dp[i-1][0]+f[c[i]][1]; } f[fa][0]+=max(dp[c.size()-1][0],dp[c.size()-1][1]); } } int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int m,u,v,ans=0,i; cin>>n>>m; v_dcc=n; for(i=1;i<=m;i++) { cin>>u>>v; add(u,v); add(v,u); } for(i=1;i<=n;i++) if(dfn[i]==0) { tarjan(i); dfs(i,0); ans+=max(f[i][0],f[i][1]); } cout<<ans<<endl; return 0; }
luogu P1019 [NOIP 2000 提高组] 单词接龙
-
敢写敢过。
点击查看代码
int cnt[30],ans=0,n; string s[30]; void dfs(string t) { ans=max(ans,(int)t.size()); for(int i=1;i<=n;i++) { if(cnt[i]<=1) for(int j=1;j<min(t.size(),s[i].size());j++) if(t.substr(t.size()-j)==s[i].substr(0,j)) { cnt[i]++; dfs(t+s[i].substr(j)); cnt[i]--; } } } int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif char c; cin>>n; for(int i=1;i<=n;i++) cin>>s[i]; cin>>c; for(int i=1;i<=n;i++) { if(s[i][0]==c) { cnt[i]++; dfs(s[i]); cnt[i]--; } } cout<<ans<<endl; return 0; }
5.9
闲话
- 本部的排水系统还是太劣了。
- 课表恢复到了添加学考课目前,保留了两节公自和一节劳动,物化生每周各少一节课。
做题纪要
LibreOJ 2601. 「NOIP2011」观光公交
-
在可行范围内选择人数较多的进行加速,通过优先队列进行划分区间,使用 \(ST\) 表查询最小值所在位置。
-
特殊处理下区间开闭。
点击查看代码
ll d[100010],s[100010],c[100010],cnt[100010],mx[100010]; struct ST { ll fminn[20][100010]; ll sx_min(ll x,ll y) { return c[x]<c[y]?x:y; } void init(ll n) { for(ll i=1;i<=n;i++) fminn[0][i]=i; for(ll j=1;j<=__lg(n);j++) for(ll i=1;i+(1<<j)-1<=n;i++) fminn[j][i]=sx_min(fminn[j-1][i],fminn[j-1][i+(1<<(j-1))]); } ll query(ll l,ll r) { ll t=__lg(r-l+1); return sx_min(fminn[t][l],fminn[t][r-(1<<t)+1]); } }S; struct node { ll l,r,val; bool operator < (const node &another) const { return cnt[another.r]-cnt[another.l-1]>cnt[r]-cnt[l-1]; } }tmp; priority_queue<node>q; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif ll n,m,k,t,u,v,ans=0,i; cin>>n>>m>>k; for(i=2;i<=n;i++) cin>>d[i]; for(i=1;i<=m;i++) { cin>>t>>u>>v; mx[u]=max(mx[u],t); cnt[v]++; ans-=t; } for(i=1;i<=n;i++) { s[i]=max(s[i-1],mx[i-1])+d[i]; c[i]=max(s[i]-mx[i],0ll); ans+=s[i]*cnt[i]; cnt[i]+=cnt[i-1]; } S.init(n); c[0]=0x3f3f3f3f; q.push((node){2,n,0}); while(k>=1&&q.empty()==0) { tmp=q.top(); q.pop(); if(tmp.l<=tmp.r) { u=(tmp.l<=tmp.r-1)?S.query(tmp.l,tmp.r-1):0; v=min({k,d[tmp.l],c[u]-tmp.val}); k-=v; d[tmp.l]-=v; tmp.val+=v; ans-=(cnt[tmp.r]-cnt[tmp.l-1])*v; if(c[u]==tmp.val) { q.push((node){tmp.l,u,tmp.val}); q.push((node){u+1,tmp.r,tmp.val}); } else q.push((node){tmp.l+1,tmp.r,tmp.val}); } } cout<<ans<<endl; return 0; }
BZOJ2322 梦想封印
-
删边转成加边后维护生成树上的根链的前缀异或和与产生的环的贡献,处理方法同 luogu P4151 [WC2011] 最大XOR和路径 。
-
此时若固定终点的话是容易维护的,但如果直接用
set
对根链的前缀异或和去重仍会有算重的贡献。 -
考虑固定线性基内的元素,若待查询值 \(x,y\) 与线性基内元素异或得到的所有数的集合相同则进行去重,不妨使用对线性基的最小异或和来进行判断。
- 若 \(x,y\) 得到的集合相等等价于 \(x \bigoplus y\) 能被线性基表出。
- \(x,y\) 对线性基的最小异或和相等则说明一定存在线性基的一个子集的异或和等于 \(x \bigoplus y\) ,因此该做法具有正确性。
点击查看代码
struct node { ll nxt,to,w; }e[40010]; ll head[20010],u[20010],v[20010],w[20010],a[20010],vis[20010],sum[20010],ans[20010],cnt=0,siz=0; vector<ll>tmp; set<ll>s; set<ll>::iterator it; void add(ll u,ll v,ll w) { cnt++; e[cnt]=(node){head[u],v,w}; head[u]=cnt; } void rebuild(); struct Liner_Base { ll d[70]; ll insert(ll x) { for(ll i=62;i>=0;i--) { if((x>>i)&1) { if(d[i]==0) { d[i]=x; rebuild(); return 1; } x^=d[i]; } } return 0; } ll query(ll ans) { for(ll i=62;i>=0;i--) ans=min(ans,ans^d[i]); return ans; } }L; void rebuild() { tmp.clear(); for(it=s.begin();it!=s.end();it++) tmp.push_back(L.query(*it)); s.clear(); for(ll i=0;i<tmp.size();i++) s.insert(tmp[i]); } void dfs(ll x) { vis[x]=1; s.insert(L.query(sum[x])); for(ll i=head[x];i!=0;i=e[i].nxt) { if(vis[e[i].to]==0) { sum[e[i].to]=sum[x]^e[i].w; dfs(e[i].to); } else siz+=L.insert(sum[x]^sum[e[i].to]^e[i].w); } } int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif ll n,m,q,_u,_v,_w,i; cin>>n>>m>>q; for(i=1;i<=m;i++) cin>>u[i]>>v[i]>>w[i]; for(i=1;i<=q;i++) { cin>>a[i]; vis[a[i]]=1; } for(i=1;i<=m;i++) if(vis[i]==0) { add(u[i],v[i],w[i]); add(v[i],u[i],w[i]); } memset(vis,0,sizeof(vis)); dfs(1); ans[q]=(1ll<<siz)*s.size()-1; for(i=q;i>=1;i--) { _u=u[a[i]]; _v=v[a[i]]; _w=w[a[i]]; add(_u,_v,_w); add(_v,_u,_w); if(vis[_u]==1&&vis[_v]==1) siz+=L.insert(sum[_u]^sum[_v]^_w); else if(vis[_u]==1||vis[_v]==1) { if(vis[_u]==0) swap(_u,_v); sum[_v]=sum[_u]^_w; dfs(_v); } ans[i-1]=(1ll<<siz)*s.size()-1; } for(i=0;i<=q;i++) cout<<ans[i]<<endl; return 0; }
5.10
闲话
- 语文老师出差回来了;物理老师又换了。
- 今天改上周五的课,明天上周六的课。
做题纪要
luogu P3733 [HAOI2017] 八纵八横
-
线段树分治套线性基。
点击查看代码
int st[3510],ed[3510]; bitset<1010>tmp; string s; void input(bitset<1010>&w) { cin>>s; reverse(s.begin(),s.end()); w.reset(); for(int i=0;i<s.size();i++) w[i]=(s[i]=='1'); } void print(bitset<1010>w) { int flag=0; for(int i=1005;i>=0;i--) { if(w[i]==1) { cout<<1; flag=1; } else if(flag==1&&w[i]==0) cout<<0; } if(flag==0) cout<<0; cout<<endl; } struct node { int u,v; bitset<1010>w; }e[3510]; struct quality { int id,fa,siz; bitset<1010>dis; }; struct DSU { int fa[510],siz[510]; bitset<1010>dis[510]; void init(int n) { for(int i=1;i<=n;i++) { fa[i]=i; siz[i]=1; } } int find(int x) { return fa[x]==x?x:find(fa[x]); } bitset<1010>get_dis(int x) { return fa[x]==x?dis[x]:dis[x]^get_dis(fa[x]); } void merge(int x,int y,bitset<1010>z,stack<quality>&s) { x=find(x); y=find(y); if(x!=y) { s.push((quality){x,fa[x],siz[x],dis[x]}); s.push((quality){y,fa[y],siz[y],dis[y]}); if(siz[x]<siz[y]) swap(x,y); fa[y]=x; dis[y]=z; siz[x]+=siz[y]; } } void split(stack<quality>&s) { while(s.empty()==0) { fa[s.top().id]=s.top().fa; siz[s.top().id]=s.top().siz; dis[s.top().id]=s.top().dis; s.pop(); } } }D; struct Liner_Base { bitset<1010>d[1010]; void insert(bitset<1010>x) { for(int i=1005;i>=0;i--) { if(x[i]==1) { if(d[i]==0) { d[i]=x; break; } x^=d[i]; } } } bitset<1010>query() { bitset<1010>ans; for(int i=1005;i>=0;i--) if(ans[i]==0) ans^=d[i]; return ans; } }L[15]; struct SMT { vector<node>tree[5010]; #define lson(rt) (rt<<1) #define rson(rt) (rt<<1|1) void update(int rt,int l,int r,int x,int y,node id) { if(x<=l&&r<=y) { tree[rt].push_back(id); return; } int mid=(l+r)/2; if(x<=mid) update(lson(rt),l,mid,x,y,id); if(y>mid) update(rson(rt),mid+1,r,x,y,id); } void solve(int rt,int l,int r,int dep) { stack<quality>s; L[dep]=L[dep-1]; int mid=(l+r)/2,x,y; for(int i=0;i<tree[rt].size();i++) { x=D.find(tree[rt][i].u); y=D.find(tree[rt][i].v); tmp=tree[rt][i].w^D.get_dis(tree[rt][i].u)^D.get_dis(tree[rt][i].v); if(x==y) L[dep].insert(tmp); else D.merge(tree[rt][i].u,tree[rt][i].v,tmp,s); } if(l==r) print(L[dep].query()); else { solve(lson(rt),l,mid,dep+1); solve(rson(rt),mid+1,r,dep+1); } D.split(s); } }T; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int n,m,_m,q,x,i; cin>>n>>m>>q; _m=m; for(i=1;i<=m;i++) { cin>>e[i].u>>e[i].v; input(e[i].w); T.update(1,0,q,0,q,e[i]); } for(i=1;i<=q;i++) { cin>>s; if(s=="Add") { _m++; cin>>e[_m].u>>e[_m].v; input(e[_m].w); st[_m]=i; ed[_m]=-1; } if(s=="Cancel") { cin>>x; x+=m; T.update(1,0,q,st[x],i-1,e[x]); ed[x]=i-1; } if(s=="Change") { cin>>x; x+=m; T.update(1,0,q,st[x],i-1,e[x]); st[x]=i; input(e[x].w); } } for(i=m+1;i<=_m;i++) if(ed[i]==-1) T.update(1,0,q,st[i],q,e[i]); D.init(n); T.solve(1,0,q,1); return 0; }
5.11
闲话
- 早上没有体活,让 \(6:42\) 到教室进行公共早读。
做题纪要
luogu P11731 [集训队互测 2015] 最大异或和
-
同 luogu P11620 [Ynoi Easy Round 2025] TEST_34 ,考虑维护差分序列的线性基。同时暴力进行操作 \(1,2\) ,由 luogu P4690 [Ynoi2016] 镜中的昆虫 可知此时区间推平次数为 \(O(n+q)\) ,即差分数组最多修改 \(O(n+q)\) 次。
-
前缀线性基/可删除线性基
- 添加过程中对于每个向量 \(v\) ,都标记它出现的最大下标 \(t\) 。查询一段区间 \([l,r]\) 的线性基时只需要保留 \(t \ge l\) 的向量即可。
- 不妨称每个向量的出现下标 \(t\) 为其时间戳,维护时只需要贪心地选取尽可能新的向量替换掉旧的向量即可。
- 仍基于贪心法构造线性基,做法如下:
- 为线性基内每个向量 \(b_{i}\) 维护一个时间戳 \(t_{i}\) ,假设要插入一个向量 \(v\) ,当前时间为 \(t\) 。
- 仍从高到低顺次扫,如果 \(v\) 的第 \(i\) 位是 \(1\) ,比较 \(t_{i}\) 和 \(t\) 的大小关系。若 \(t>t_{i}\) 即 \(v\) 的添加时间更晚,则交换 \((v,b_{i}),(t,t_{i})\) ;否则不进行处理。接着用 \(b_{i} \bigoplus v\) 继续更新即可。
- 也就是说,如果当前位可以通过较新的向量表示,就直接用较新的向量;否则,保留原来的向量。
-
离线后按照时间更新即可。
点击查看代码
int op[2010],st[2010],m; bitset<2010>a[2010],d[2010],s; vector<pair<bitset<2010>,int> >e[2010]; void print(bitset<2010>x) { for(int i=m-1;i>=0;i--) cout<<x[i]; cout<<endl; } struct Liner_Base { int t[2010]; bitset<2010>d[2010]; void insert(bitset<2010>v,int now) { for(int i=m-1;i>=0;i--) { if(v[i]==1) { if(t[i]==0) { d[i]=v; t[i]=now; break; } if(now>t[i]) { swap(v,d[i]); swap(now,t[i]); } v^=d[i]; } } } void del(int now) { for(int i=m-1;i>=0;i--) if(t[i]==now) { d[i].reset(); t[i]=0; } } bitset<2010>query() { bitset<2010>ans; for(int i=m-1;i>=0;i--) if(ans[i]==0&&t[i]!=0) ans^=d[i]; return ans; } }L; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int n,q,l,r,i,j; cin>>n>>m>>q; for(i=1;i<=n;i++) { cin>>a[i]; d[i]=a[i-1]^a[i]; } for(i=1;i<=q;i++) { cin>>op[i]; if(op[i]<=2) cin>>l>>r>>s; if(op[i]==1) for(j=l;j<=r;j++) a[j]^=s; if(op[i]==2) for(j=l;j<=r;j++) a[j]=s; if(op[i]<=2) for(j=l;j<=min(r+1,n);j++) { s=a[j]^a[j-1]; if(d[j]!=s) { if(d[j].any()==1) e[st[j]].push_back(make_pair(d[j],i)); d[j]=s; st[j]=i; } } } for(i=1;i<=n;i++) if(d[i].any()==1) e[st[i]].push_back(make_pair(d[i],q+1)); for(i=0;i<=q;i++) { L.del(i); for(j=0;j<e[i].size();j++) L.insert(e[i][j].first,e[i][j].second); if(op[i]==3) print(L.query()); } return 0; }
5.12
闲话
做题纪要
luogu P7710 [Ynoi2077] stdmxeypz
-
将修改操作转化为 \([dfn_{u},out_{u}]\) 内深度 \(\equiv (dep_{u}+y) \pmod{x}\) 的点权值加 \(z\) 。
-
分块。整块进行根号分治,对于 \(\ge \sqrt{n}\) 的进行差分,查询时暴力扫一遍。
-
稍调块长使得空间能开下。
点击查看代码
struct node { int nxt,to; }e[300010]; int head[300010],dep[300010],dfn[300010],out[300010],L[260],R[260],pos[300010],a[300010],f[260][260][260],d[300010][260],cnt=0,tot=0,klen,ksum; void add(int u,int v) { cnt++; e[cnt]=(node){head[u],v}; head[u]=cnt; } void dfs(int x,int fa) { tot++; dfn[x]=tot; dep[tot]=dep[fa]+1; for(int i=head[x];i!=0;i=e[i].nxt) dfs(e[i].to,dfn[x]); out[x]=tot; } void init(int n) { klen=1200; ksum=n/klen; for(int i=1;i<=ksum;i++) { L[i]=R[i-1]+1; R[i]=R[i-1]+klen; } if(R[ksum]<n) { ksum++; L[ksum]=R[ksum-1]+1; R[ksum]=n; } for(int i=1;i<=ksum;i++) for(int j=L[i];j<=R[i];j++) pos[j]=i; } void update(int l,int r,int x,int y,int z,int n) { if(pos[l]==pos[r]) { for(int i=l;i<=r;i++) if(dep[i]%x==y) a[i]+=z; } else { for(int i=l;i<=R[pos[l]];i++) if(dep[i]%x==y) a[i]+=z; for(int i=L[pos[r]];i<=r;i++) if(dep[i]%x==y) a[i]+=z; if(x<=ksum) for(int i=pos[l]+1;i<=pos[r]-1;i++) f[i][x][y]+=z; else for(int i=y;i<=n;i+=x) { d[i][pos[l]+1]+=z; d[i][pos[r]]-=z; } } } int query(int x) { int ans=a[x]; for(int i=1;i<=ksum;i++) ans+=f[pos[x]][i][dep[x]%i]; for(int i=1;i<=pos[x];i++) ans+=d[dep[x]][i]; return ans; } int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int n,m,u,v,pd,x,y,i; scanf("%d%d",&n,&m); init(n); for(i=2;i<=n;i++) { scanf("%d",&u); add(u,i); } dfs(1,0); for(i=1;i<=m;i++) { scanf("%d%d",&pd,&u); if(pd==1) { scanf("%d%d%d",&x,&y,&v); update(dfn[u],out[u],x,(dep[dfn[u]]+y)%x,v,n); } else printf("%d\n",query(dfn[u])); } return 0; }
luogu P6086 【模板】Prufer 序列
-
Prüfer 序列
- Prüfer 序列可以将一个有标号 \(n\) 个点的无根树用 \([1,n]\) 的 \(n-2\) 个数来表示,即 \(n\) 个点的完全图的生成树与长度为 \(n-2\) 的数列之间的数列间的双射。
- 对树建 Prüfer 序列
- 核心思想是每次选择一个编号最小的叶子节点并删掉它,然后在序列中记录下它的父亲结点。重复 \(n-2\) 次后就只剩下两个结点,算法结束。
- 使用堆容易得到一个 \(O(n \log n)\) 的做法,但我们有更优的 \(O(n)\) 构造方法。
- 考虑维护一个指针 \(p\) 指向我们将要删除的节点。初始时 \(p\) 指向编号最小的叶结点。同时我们维护每个结点的度数,方便我们知道在删除结点的时侯是否产生新的叶结点。操作如下:
- 删除 \(p\) 指向的结点,并检查是否产生新的叶结点。
- 如果产生新的叶结点,假设编号为 \(x\) ,我们比较 \(p,x\) 的大小关系。如果 \(x>p\) ,那么不做其他操作;否则就立刻删除 \(x\) ,然后检查删除 \(x\) 后是否产生新的叶结点,重复当前步骤,直到未产生新节点或者新节点的编号 \(>p\) 。
- 若 \(x>p\) 在 \(p\) 往后扫的过程中会扫到 \(p\) 。
- 若 \(x<p\) 说明删去 \(p\) 后 \(x\) 是当前编号最小的叶子节点。
- 让指针 \(p\) 自增直到遇到一个未被删除叶结点为止。
- 由上述构造流程可知构造完 Prüfer 序列后原树会剩下两个节点,其中一个是 \(n\) ;每个节点在 Prüfer 序列中出现的次数是其度数 \(-1\) 。
- 用 Prüfer 序列建树
- 核心思想是每次我们选择一个度数为 \(1\) 的编号最小的结点,与当前枚举到的 Prüfer 序列的点相连,然后删去这个节点。到最后我们剩下两个度数为 \(1\) 的点,其中一个是结点 \(n\) 。就把它们相连。
- 使用堆容易得到一个 \(O(n \log n)\) 的做法,但我们有更优的 \(O(n)\) 构造方法,基本过程同上。
- Prüfer 序列常用于进行计数,而不是维护树形结构。
-
本题中因限制了根节点为 \(n\) ,在构造时不妨钦定 \(p_{n-1}=n\) 。实际应用时也可类似这样建树。
点击查看代码
ll fa[5000010],p[5000010],du[5000010]; void tree_to_prufer(ll n) { for(ll i=1;i<=n-1;i++) du[fa[i]]++; for(ll i=1,j=1;i<=n-2;i++,j++) { for(;du[j]!=0;j++); p[i]=fa[j]; for(;i<=n-2;i++) { du[p[i]]--; if(du[p[i]]==0&&p[i]<j) p[i+1]=fa[p[i]]; else break; } } } void prufer_to_tree(ll n) { for(ll i=1;i<=n-2;i++) du[p[i]]++; p[n-1]=n; for(ll i=1,j=1;i<=n-1;i++,j++) { for(;du[j]!=0;j++); fa[j]=p[i]; for(;i<=n-1;i++) { du[p[i]]--; if(du[p[i]]==0&&p[i]<j) fa[p[i]]=p[i+1]; else break; } } } ll query(ll n,ll a[]) { ll ans=0; for(ll i=1;i<=n;i++) ans^=i*a[i]; return ans; } int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif ll n,m,i; scanf("%lld%lld",&n,&m); if(m==1) { for(i=1;i<=n-1;i++) scanf("%lld",&fa[i]); tree_to_prufer(n); printf("%lld\n",query(n-2,p)); } else { for(i=1;i<=n-2;i++) scanf("%lld",&p[i]); prufer_to_tree(n); printf("%lld\n",query(n-1,fa)); } return 0; }
UVA10843 Anne's game
-
Cayley 公式/凯莱公式
- 完全图 \(K_{n}\) 有 \(n^{n-2}\) 棵生成树。
- 证明:由于生成树与 Prüfer 序列构成一组双射,故方案数为 \(n^{n-2}\) 。
点击查看代码
const ll p=2000000011; ll qpow(ll a,ll b,ll p) { ll ans=1; while(b) { if(b&1) ans=ans*a%p; b>>=1; a=a*a%p; } return ans; } int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif ll t,n,i; cin>>t; for(i=1;i<=t;i++) { cin>>n; cout<<"Case #"<<i<<": "<<((n==1)?1:qpow(n,n-2,p))<<endl; } return 0; }
- 完全图 \(K_{n}\) 有 \(n^{n-2}\) 棵生成树。
luogu P4981 父子
-
无根树个数乘 \(n\) 就是有根树个数。
点击查看代码
const ll p=1000000009; ll qpow(ll a,ll b,ll p) { ll ans=1; while(b) { if(b&1) ans=ans*a%p; b>>=1; a=a*a%p; } return ans; } int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif ll t,n,i; cin>>t; for(i=1;i<=t;i++) { cin>>n; cout<<qpow(n,n-1,p)<<endl; } return 0; }
5.13
闲话
- 因 HZ 要举办市足球联赛,故周三的体育课停上了。
做题纪要
luogu P4430 小猴打架
-
每棵生成树的连边方式为 \((n-1)!\) 种。
点击查看代码
n=int(input()) ans=1 for i in range(1,n): ans=ans*i%9999991 for i in range(1,n-1): ans=ans*n%9999991 print(ans)
luogu P2290 [HNOI2004] 树的计数
-
已知度数后在 Prüfer 序列进行重排列即可,有 \(\dfrac{(n-2)!}{\prod\limits_{i=1}^{n}(du_{i}-1)!}\) 即为所求。
点击查看代码
ll prime[200],vis[200],c[200],du[200],len; void isprime(ll n) { memset(vis,0,sizeof(vis)); for(ll i=2;i<=n;i++) { if(vis[i]==0) { len++; prime[len]=i; } for(ll j=1;j<=len&&i*prime[j]<=n;j++) { vis[i*prime[j]]=1; if(i%prime[j]==0) break; } } } void divide(ll n,ll op) { for(ll i=1;i<=len&&prime[i]<=n;i++) { for(;n%prime[i]==0;n/=prime[i]) c[i]+=op; } } ll qpow(ll a,ll b) { ll ans=1; while(b) { if(b&1) ans=ans*a; b>>=1; a=a*a; } return ans; } int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif ll n,ans=0,i,j; cin>>n; isprime(n); for(i=1;i<=n-2;i++) divide(i,1); for(i=1;i<=n;i++) { cin>>du[i]; ans+=du[i]; for(j=1;j<=du[i]-1;j++) divide(j,-1); } ans=(ans==2*(n-1)); for(i=1;i<=n;i++) ans&=(du[i]<=n-1); for(i=1;i<=len;i++) ans=ans*qpow(prime[i],c[i]); cout<<ans<<endl; return 0; }
CF156D Clues
-
扩展凯莱公式
- 考虑枚举每个连通块的度数,有 \(\sum\limits_{du_{i} \ge 0,\sum du_{i}=2k-2}\dbinom{k-2}{du_{1}-1,du_{2}-1, \dots ,du_{k}-1}\prod\limits_{i=1}^{k}siz_{i}^{du_{i}}\) 即为所求。
- 考虑多元二项式定理 \((x_{1}+x_{2}+ \dots + x_{n})^{k}=\sum\limits_{c_{i} \ge 0,\sum c_{i}=k} \dbinom{k}{x_{1},x_{2}, \dots ,x_{n}} \prod\limits_{i=1}^{n}x_{i}^{c_{i}}\) 化简后得到 \((siz_{1}+siz_{2}+ \dots + siz_{n})^{k-2}\prod\limits_{i=1}^{k}siz_{i}=n^{k-2}\prod\limits_{i=1}^{k}siz_{i}\) 即为所求。
点击查看代码
ll vis[100010],siz; vector<ll>e[100010]; void add(ll u,ll v) { e[u].push_back(v); } ll qpow(ll a,ll b,ll p) { ll ans=1; while(b) { if(b&1) ans=ans*a%p; b>>=1; a=a*a%p; } return ans; } void dfs(ll x) { siz++; vis[x]=1; for(ll i=0;i<e[x].size();i++) if(vis[e[x][i]]==0) dfs(e[x][i]); } int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif ll n,m,p,u,v,ans=1,sum=0,i; cin>>n>>m>>p; for(i=1;i<=m;i++) { cin>>u>>v; add(u,v); add(v,u); } for(i=1;i<=n;i++) if(vis[i]==0) { siz=0; dfs(i); sum++; ans=ans*siz%p; } cout<<((sum==1)?1:ans*qpow(n,sum-2,p))%p<<endl; return 0; }
5.14
闲话
- 教室能稳定开空调了。
做题纪要
CF1109D Sasha and Interesting Fact from Graph Theory
-
广义凯莱公式
- \(n\) 个标号节点形成一个有 \(k\) 棵树的森林,使得给定的 \(k\) 个点没有两个点属于同一棵树的方案数为 \(kn^{n-k-1}\) 。
- 证明
- 考虑新加入一个虚点 \(0\) 与形成的 \(k\) 棵树分别相连,此时 \(n+1\) 个点的 Prüfer 序列上 \(0\) 的出现次数就为 \(k-1\) 。 剩下的 \(n-k\) 个点可以任意选,此时方案数为 \(\dbinom{n-1}{k-1}n^{n-k}\) 。
- 注意到此时忽略了这 \(k\) 个点是给定的限制,所以再除以 \(\dbinom{n}{k}\) 进行映射即可。得到最终方案数为 \(kn^{n-k-1}\) 。
-
考虑枚举 \(a,b\) 间有多少条边,插板后有 \(\sum\limits_{i=1}^{n-1}\dbinom{m-1}{i-1}m^{n-1-i}A_{n-2}^{i-1}(i+1)n^{n-(i+1)-1}\) 即为所求。
-
特判 \(n-1\) 处的转移。
点击查看代码
const ll p=1000000007; ll jc[1000010],inv[1000010],jc_inv[1000010]; ll qpow(ll a,ll b,ll p) { ll ans=1; while(b) { if(b&1) ans=ans*a%p; b>>=1; a=a*a%p; } return ans; } ll C(ll n,ll m,ll p) { return (n>=m&&n>=0&&m>=0)?jc[n]*jc_inv[n-m]%p*jc_inv[m]%p:0; } ll A(ll n,ll m,ll p) { return (n>=m&&n>=0&&m>=0)?jc[n]*jc_inv[n-m]%p:0; } int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif ll n,m,ans=0,u,v,i; cin>>n>>m>>u>>v; jc[0]=jc_inv[0]=jc[1]=inv[1]=jc_inv[1]=1; for(i=2;i<=max(n,m);i++) { jc[i]=jc[i-1]*i%p; inv[i]=(p-p/i)*inv[p%i]%p; jc_inv[i]=jc_inv[i-1]*inv[i]%p; } for(i=1;i<=n-1;i++) { if(i!=n-1) ans=(ans+C(m-1,i-1,p)*qpow(m,n-1-i,p)%p*A(n-2,i-1,p)%p *(i+1)%p*qpow(n,n-(i+1)-1,p)%p)%p; else ans=(ans+C(m-1,i-1,p)*qpow(m,n-1-i,p)%p*A(n-2,i-1,p)%p)%p; } cout<<ans<<endl; return 0; }
luogu P2624 [HNOI2008] 明明的烦恼
-
考虑把无要求的点插入到 Prüfer 序列求方案数。
-
设 \(m=\sum\limits_{i=1}^{n}[du_{i} \ne -1] \times (du_{i}-1),cnt=\sum\limits_{i=1}^{n}[du_{i}=-1]\) ,则有 \(\dbinom{n-2}{m} \dfrac{m!}{\prod\limits (du_{i}-1)!} (n-m)^{n-2-m}\) 即为所求。
点击查看代码
ll prime[1010],vis[1010],c[1010],du[1010],len=0,a[1000010]; void isprime(ll n) { memset(vis,0,sizeof(vis)); for(ll i=2;i<=n;i++) { if(vis[i]==0) { len++; prime[len]=i; } for(ll j=1;j<=len&&i*prime[j]<=n;j++) { vis[i*prime[j]]=1; if(i%prime[j]==0) break; } } } void divide(ll n,ll op) { for(ll i=1;i<=len&&prime[i]<=n;i++) { for(;n%prime[i]==0;n/=prime[i]) c[i]+=op; } } void cheng(ll a[],ll val) { ll x=0; for(ll i=1;i<=a[0];i++) { a[i]=a[i]*val+x; x=a[i]/10000; a[i]%=10000; } a[0]++; a[a[0]]=x; while(a[0]>0&&a[a[0]]==0) a[0]--; } int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif ll n,sum=0,cnt=0,flag=1,i,j; cin>>n; isprime(n); for(i=1;i<=n;i++) { cin>>du[i]; if(du[i]==-1) cnt++; else { sum+=du[i]-1; flag&=(du[i]<=n-1); for(j=1;j<=du[i]-1;j++) divide(j,-1); } } for(i=1;i<=n-2;i++) divide(i,1); for(i=1;i<=n-2-sum;i++) divide(i,-1); divide(cnt,n-2-sum); if(sum>n-2||(sum<n-2&&cnt==0)||flag==0) cout<<0<<endl; else { a[0]=a[1]=1; for(i=1;i<=len;i++) for(j=1;j<=c[i];j++) cheng(a,prime[i]); cout<<a[a[0]]; for(i=a[0]-1;i>=1;i--) printf("%04lld",a[i]); } return 0; }
luogu P5367 【模板】康托展开
-
多倍经验: P2524 Uim的情人节礼物·其之弐 | luogu P11319 [NOISG 2020 Qualification] Cryptography
-
康托展开
- 康托展开是指一种将自然数展开为数列的方法。它可以看作是一种特殊的进制,也叫做 阶乘进制 。这种进制中,不同的数位对应的底数并不相同。比如,十进制数 \(463_{10}\) 可以在阶乘进制中表示为 \(463_{10}=341010_{!}\) ,它表示 \(463=3 \times 5!+4 \times 4!+1 \times 3!+0 \times 2!+1 \times 1!+0\times 0!\) 。
- 定义长度为 \(n\) 的排列 \(\sigma\) 的 Lehmer 码为 \(L_{\sigma}(i)=\sum\limits_{j=i+1}^{n}[\sigma(j)<\sigma(i)]\) 。此时一个排列的排名的康托展开就对应了该排列的 Lehmer 码。
- 正确性考虑从高到低按位计算,类似数位 DP 的过程。
- 权值树状数组维护 \(L_{\sigma}(i)\) 即可。
点击查看代码
const int p=998244353; int a[1000010]; struct BIT { int c[1000010]; int lowbit(int x) { return (x&(-x)); } void add(int n,int x,int val) { for(int i=x;i<=n;i+=lowbit(i)) c[i]+=val; } int getsum(int x) { int ans=0; for(int i=x;i>=1;i-=lowbit(i)) ans+=c[i]; return ans; } }T; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int n,ans=1,jc=1,i; scanf("%d",&n); for(i=1;i<=n;i++) scanf("%d",&a[i]); for(i=n;i>=1;i--,jc=1ll*jc*(n-i)%p) { ans=(ans+1ll*T.getsum(a[i]-1)*jc%p)%p; T.add(n,a[i],1); } printf("%d\n",ans); return 0; }
UVA11525 Permutation
-
权值线段树上二分查询第 \(s_{i}+1\) 小即可。
点击查看代码
struct SMT { struct SegmentTree { int sum; }tree[200010]; #define lson(rt) (rt<<1) #define rson(rt) (rt<<1|1) void pushup(int rt) { tree[rt].sum=tree[lson(rt)].sum+tree[rson(rt)].sum; } void build(int rt,int l,int r) { tree[rt].sum=r-l+1; if(l==r) return; int mid=(l+r)/2; build(lson(rt),l,mid); build(rson(rt),mid+1,r); } int query(int rt,int l,int r,int k) { if(l==r) { tree[rt].sum=0; return l; } int mid=(l+r)/2,ans=0; if(k<=tree[lson(rt)].sum) ans=query(lson(rt),l,mid,k); else ans=query(rson(rt),mid+1,r,k-tree[lson(rt)].sum); pushup(rt); return ans; } }T; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int t,n,x,i; cin>>t; for(;t>=1;t--) { cin>>n; T.build(1,1,n); for(i=1;i<=n;i++) { cin>>x; cout<<T.query(1,1,n,x+1); if(i!=n) cout<<" "; } cout<<endl; } return 0; }
luogu P3014 [USACO11FEB] Cow Line S
-
逆康托展开
- 将排名转成 Lehmer 码的形式后权值线段树上二分查询第 \(k\) 小即可。
- 转 Lehmer 码时预处理阶乘是不必要的,只需要倒着边除边取模即可。
点击查看代码
ll a[50]; struct BIT { ll c[50]; ll lowbit(ll x) { return (x&(-x)); } void clear() { memset(c,0,sizeof(c)); } void add(ll n,ll x,ll val) { for(ll i=x;i<=n;i+=lowbit(i)) c[i]+=val; } ll getsum(ll x) { ll ans=0; for(ll i=x;i>=1;i-=lowbit(i)) ans+=c[i]; return ans; } }B; struct SMT { struct SegmentTree { ll sum; }tree[210]; #define lson(rt) (rt<<1) #define rson(rt) (rt<<1|1) void pushup(ll rt) { tree[rt].sum=tree[lson(rt)].sum+tree[rson(rt)].sum; } void build(ll rt,ll l,ll r) { tree[rt].sum=r-l+1; if(l==r) return; ll mid=(l+r)/2; build(lson(rt),l,mid); build(rson(rt),mid+1,r); } ll query(ll rt,ll l,ll r,ll k) { if(l==r) { tree[rt].sum=0; return l; } ll mid=(l+r)/2,ans=0; if(k<=tree[lson(rt)].sum) ans=query(lson(rt),l,mid,k); else ans=query(rson(rt),mid+1,r,k-tree[lson(rt)].sum); pushup(rt); return ans; } }T; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif ll n,m,ans,jc,i; char c; cin>>n>>m; for(;m>=1;m--) { cin>>c; if(c=='P') { cin>>ans; T.build(1,1,n); ans--; for(i=1;i<=n;i++) { a[n-i+1]=ans%i; ans/=i; } for(i=1;i<=n;i++) cout<<T.query(1,1,n,a[i]+1)<<" "; cout<<endl; } else { ans=jc=1; B.clear(); for(i=1;i<=n;i++) cin>>a[i]; for(i=n;i>=1;i--,jc*=(n-i)) { ans+=B.getsum(a[i]-1)*jc; B.add(n,a[i],1); } cout<<ans<<endl; } } return 0; }
5.15
闲话
- 宿舍能稳定开空调了。
做题纪要
CF501D Misha and Permutations Summation
-
转成 Lehmer 码后模拟进位。
点击查看代码
int a[200010],l[200010]; struct BIT { int c[200010]; int lowbit(int x) { return (x&(-x)); } void clear() { memset(c,0,sizeof(c)); } void add(int n,int x,int val) { for(int i=x;i<=n;i+=lowbit(i)) c[i]+=val; } int getsum(int x) { int ans=0; for(int i=x;i>=1;i-=lowbit(i)) ans+=c[i]; return ans; } }B; struct SMT { struct SegmentTree { int sum; }tree[800010]; #define lson(rt) (rt<<1) #define rson(rt) (rt<<1|1) void pushup(int rt) { tree[rt].sum=tree[lson(rt)].sum+tree[rson(rt)].sum; } void build(int rt,int l,int r) { tree[rt].sum=r-l+1; if(l==r) return; int mid=(l+r)/2; build(lson(rt),l,mid); build(rson(rt),mid+1,r); } int query(int rt,int l,int r,int k) { if(l==r) { tree[rt].sum=0; return l; } int mid=(l+r)/2,ans=0; if(k<=tree[lson(rt)].sum) ans=query(lson(rt),l,mid,k); else ans=query(rson(rt),mid+1,r,k-tree[lson(rt)].sum); pushup(rt); return ans; } }T; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int n,i,j; cin>>n; T.build(1,1,n); for(j=1;j<=2;j++) { B.clear(); for(i=1;i<=n;i++) { cin>>a[i]; a[i]++; } for(i=n;i>=1;i--) { l[i]+=B.getsum(a[i]-1); B.add(n,a[i],1); } } for(i=n;i>=1;i--) { l[i-1]+=l[i]/(n-i+1); l[i]%=(n-i+1); } for(i=1;i<=n;i++) cout<<T.query(1,1,n,l[i]+1)-1<<" "; return 0; }
luogu P1403 [AHOI2005] 约数研究
-
统计约数个数转化为统计倍数个数。
点击查看代码
int d[10000010]; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int n,i,j; ll ans=0; cin>>n; for(i=1;i<=n;i++) for(j=i;j<=n;j+=i) d[j]++; for(i=1;i<=n;i++) ans+=1ll*i*d[i]; cout<<ans<<endl; return 0; }
5.16
闲话
做题纪要
SP18715 BORING - Boring Factorials (Reloaded)
-
由威尔逊定理,有 \((p-1)! \equiv (-1) \pmod{p}\) ,计算 \([n+1,p-1]\) 的逆元即可。
点击查看代码
ll read() { ll x=0,f=1; char c=getchar(); for(;c>'9'||c<'0';c=getchar()) if(c=='-') f=-1; for(;'0'<=c&&c<='9';c=getchar()) x=x*10+c-'0'; return x*f; } ll qpow(ll a,ll b,ll p) { ll ans=1; while(b) { if(b&1) ans=ans*a%p; b>>=1; a=a*a%p; } return ans; } int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif ll t,n,p,ans,i; t=read(); for(;t>=1;t--) { n=read(); p=read(); if(n>=p) printf("0\n"); else { ans=1; for(i=n+1;i<=p-1;i++) ans=ans*i%p; printf("%lld\n",(p-1)*qpow(ans,p-2,p)%p); } } return 0; }
5.17
闲话
- 上午 \(11:20\) 放假。
5.18
闲话
做题纪要
SP4305 AE3A - Drilling
-
设 \(f_{l,r}\) 表示在确定油 \(\in [l,r]\) ,至少要花多少钱才能保证知道电线的长度,状态转移方程为 \(f_{l,r}=\min\limits_{k=l+1}^{r-1}\{ \max(f_{l,k-1},f_{k+1,r})+a_{k} \}=\min( \max\limits_{k=l+1}^{r-1}\{f_{l,k-1}+a_{k} \} ,\max\limits_{k=l+1}^{r-1}\{f_{k+1,r}+a_{k} \})\) 。
-
单调队列维护即可。
点击查看代码
int a[2010],f[2010][2010]; deque<int>q[2010],_q; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int n,i,l,r; cin>>n; for(i=1;i<=n;i++) cin>>a[i]; for(l=n;l>=1;l--) { _q.clear(); for(r=l;r<=n;r++) { while(q[r].empty()==0&&f[q[r].front()+1][r]<f[l][q[r].front()-1]) q[r].pop_front(); while(q[r].empty()==0&&f[q[r].back()+1][r]+a[q[r].back()]>=f[l+1][r]+a[l]) q[r].pop_back(); q[r].push_back(l); while(_q.empty()==0&&f[l][_q.front()-1]<f[_q.front()+1][r]) _q.pop_front(); while(_q.empty()==0&&f[l][_q.back()-1]+a[_q.back()]>=f[l][r-1]+a[r]) _q.pop_back(); _q.push_back(r); f[l][r]=min(f[l][_q.front()-1]+a[_q.front()],f[q[r].front()+1][r]+a[q[r].front()]); } } cout<<f[1][n]<<endl; return 0; }
luogu P9774 [HUSTFC 2023] 新取模运算
-
\((n!)_{p} \equiv (p-1)^{\left \lfloor n/p \right \rfloor} (n \bmod p)!(\left \lfloor n/p \right \rfloor!)_{p} \pmod{p}\) 即为所求。
点击查看代码
ll jc[1000010],p; ll qpow(ll a,ll b,ll p) { ll ans=1; while(b) { if(b&1) ans=ans*a%p; b>>=1; a=a*a%p; } return ans; } ll wilson(ll x) { return (x<p)?jc[x]:qpow(p-1,x/p,p)*jc[x%p]%p*wilson(x/p)%p; } int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif ll t,n,i; cin>>t>>p; jc[0]=1; for(i=1;i<=p-1;i++) jc[i]=jc[i-1]*i%p; for(i=1;i<=t;i++) { cin>>n; cout<<wilson(n)<<endl; } return 0; }
5.19
闲话
- 早预备举行升旗仪式。
- 上午第四、五节北航某教授来做讲座,奥赛班不去参加。
做题纪要
luogu P1925 最大划分乘积
-
最大值在 \(x=\mathrm{e}\) 处取到,对应的 \(k\) 取 \(\left \lfloor \frac{n}{\mathrm{e}} \right \rfloor / \left \lceil \frac{n}{\mathrm{e}} \right \rceil\) 。
-
判断有限/无限小数只需要不断除以 \(2,5\) 即可。
点击查看代码
const double e=2.7182818284590452354; double f(int n,int k) { return 1.0*k*log(1.0*n/k); } bool check(int n) { int k=n/e; if(f(n,k)<f(n,k+1)) k++; k/=__gcd(n,k); while(k%2==0) k/=2; while(k%5==0) k/=5; return (k==1); } int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int n,ans=0,i; cin>>n; for(i=5;i<=n;i++) ans+=((check(i)==true)?-1:1)*i; cout<<ans<<endl; return 0; }
luogu B4325 [语言月赛 202505] 翻书
-
顺序结构。
点击查看代码
n=int(input())+1 print((n+1)//2-1)
luogu B4326 [语言月赛 202505] 毕业论文
-
分支结构。
点击查看代码
w,f,a,r=map(int,input().split()) if(w>=r and (f/w)<=0.2 and (a/w)<=0.3): print("Accepted") else: print("Rejected")
luogu B4327 [语言月赛 202505] 第二数位翻转
-
循环结构。
点击查看代码
deque<int>q,ans; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif char c; while(cin>>c) q.push_back(c-'0'); if(q.size()%2!=0) q.push_front(0); for(int i=q.size()-2;i>=0;i-=2) { ans.push_back(q[i]); ans.push_back(q[i+1]); } while(ans.empty()==0&&ans.front()==0) ans.pop_front(); for(int i=0;i<ans.size();i++) cout<<ans[i]; return 0; }
luogu B4328 [语言月赛 202505] 偏向色
-
循环结构。
点击查看代码
ans=0 m=int(input()) for i in range(256): for j in range(256): for k in range(256): ans+=(i-j>=m and i-k>=m) print(ans)
luogu B4329 [语言月赛 202505] 等分差试题序列
-
循环结构。
点击查看代码
int a[5010]; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int n,ans=2,sum=0,last=0,i; cin>>n; for(i=1;i<=n;i++) { cin>>a[i]; if(a[i]-a[i-1]==last) sum++; else { last=a[i]-a[i-1]; sum=2; } ans=max(ans,sum); } cout<<ans<<endl; return 0; }
luogu B4330 [语言月赛 202505] 种子队
-
转义。
点击查看代码
int vis[1000010]; int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int n,k,x,flag=1,sum,i,j; scanf("%d%d",&n,&k); for(i=1;i<=k;i++) { scanf("%d",&x); vis[x]=1; } for(i=1;i<=k;i++) { sum=0; for(j=1;j<=n/k;j++) { scanf("%d",&x); sum+=vis[x]; } flag&=(sum==1); } if(flag==1) printf("\"MiaoW\"\n"); else printf("\\QAQ/\n"); return 0; }
luogu B4331 [语言月赛 202505] 通配回文
-
循环结构。
点击查看代码
char s[510]; int check(int l,int r,int len) { for(int i=l,j=r;i<=r,j>=l;i++,j--) if(!(s[i]==s[j]||s[i]=='?'||s[j]=='?')) return 0; return 1; } int main() { // #define Isaac #ifdef Isaac freopen("in.in","r",stdin); freopen("out.out","w",stdout); #endif int t,n,ans,len,l,r; cin>>t; for(;t>=1;t--) { cin>>(s+1); n=strlen(s+1); ans=0; for(len=1;len<=n;len++) { for(l=1,r=l+len-1;r<=n;l++,r++) { ans+=check(l,r,len); } } cout<<ans<<endl; } return 0; }
本文来自博客园,作者:hzoi_Shadow,原文链接:https://www.cnblogs.com/The-Shadow-Dragon/p/18858533,未经允许严禁转载。
版权声明:本作品采用 「署名-非商业性使用-相同方式共享 4.0 国际」许可协议(CC BY-NC-SA 4.0) 进行许可。