2021.01.01
[] CF142D
考虑以 $F,G$ 之间的距离为石子个数,则直接跑 $K-Nim$ 即可。
#include<iostream> #include<cstring> #include<cstdio> #include<cstring> #include<vector> #include<queue> #include<algorithm> #include<climits> #define pii pair<int,int> #define pb push_back #define mp make_pair #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=111; int N,M,A[MAXN],K; char str[MAXN]; int main(){ N=read(),M=read(),K=read(); bool RR=0,GG=0,GGG=0; for(int i=1;i<=N;i++){ scanf("%s",str+1); int totR=0,totG=0,psR=0,psG=0; for(int j=1;j<=M;j++){ if(str[j]=='R') totR++,psR=j; if(str[j]=='G') totG++,psG=j,GGG=1; } if(totR&&totG){A[++A[0]]=max(psR,psG)-min(psR,psG)-1;continue;} if(M==totR) RR=1; if(M==totG) GG=1; } if(A[0]){ bool ff=1; for(int i=10;i>=0;i--){ int tot=0; for(int j=1;j<=A[0];j++) if(A[j]&(1<<i)) tot++; ff&=(tot%(K+1)==0); } if(ff) printf("Second "); else printf("First "); return 0; } if(!GGG){printf("Second ");return 0;} if(!GG&&!RR) printf("Draw ");else if(GG) printf("Second ");else if(RR) printf("First ");return 0; }
[] CF512E
很自然想到我们可以规到 $1$ 与其他点均含边,对于与 $1$ 有边的点 $u,v$ ,若 $|u-v|>1$ ,则他们之间肯定存在一条边,则反转这条边就会得到一条 $1-p$ 的边。
构造即可。
#include<iostream> #include<cstring> #include<cstdio> #include<cstring> #include<vector> #include<queue> #include<algorithm> #include<climits> #define pii pair<int,int> #define pb push_back #define mp make_pair #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=1e3+11; int E[MAXN][MAXN],N,tmp[MAXN],Num[MAXN]; vector<pii> Ans[2]; bool check(){for(int i=3;i<N;i++) if(!E[1][i]) return 0; return 1;} pii Do(int X,int Y){ memset(Num,0,sizeof(Num)); for(int i=1;i<=N;i++) if(E[X][i]) Num[i]=1; for(int i=2;i<=N;i++) if(E[Y][i]&&Num[i]){ E[X][Y]=E[Y][X]=0; E[1][i]=E[i][1]=1; return mp(1,i); } } void solve(int opt){ for(int i=1;i<=N-1;i++) E[i][i+1]=E[i+1][i]=1; E[1][N]=E[N][1]=1; while(!check()){ tmp[0]=0; for(int i=2;i<=N;i++) if(E[1][i]) tmp[++tmp[0]]=i; for(int i=2;i<=tmp[0];i++){ if(tmp[i]-tmp[i-1]>1){ pii p=Do(tmp[i-1],tmp[i]); if(!opt) Ans[0].pb(mp(tmp[i-1],tmp[i])); else Ans[1].pb(p); break; } } }return; } int main(){ //freopen("3.in","r",stdin); N=read(); for(int i=1;i<=N-3;i++){int u=read(),v=read();E[u][v]=E[v][u]=1;} solve(0);memset(E,0,sizeof(E)); for(int i=1;i<=N-3;i++){int u=read(),v=read();E[u][v]=E[v][u]=1;} solve(1); printf("%lu ",Ans[0].size()+Ans[1].size()); for(int i=0;i<Ans[0].size();i++) printf("%d %d ",Ans[0][i].fi,Ans[0][i].se); for(int i=Ans[1].size()-1;i>=0;i--) printf("%d %d ",Ans[1][i].fi,Ans[1][i].se); }
[x] CF605E
神仙题目。
我们设 $E_u$ 表示 $u$ 到 $n$ 的期望最短时间。假设我们知道了所有点的 $E$ 值,则我们发现一个点 $u$ 可以走到点 $v$ 的条件是 $E_u>E_v$ ,则转移图是个 $DAG$ 。
我们如何去维护上述过程,显然 $u$ 会走一个期望最小的。 那么我们设 $f_u$ 表示在 $u$ 号结点且 $E_u>E_v$ 的所有转移代价。
由于时间的独立性可得 $$f_u=sum E_v imes p_{u,v}prod_x (1-p_{u,x})$$ $x$ 代表 $E$ 值在 $v$ 前面的数。
而对于 $E_u$ 因为存在时刻1无边可走则 $E_u=P(E_u+1)+f_u$ ,$P$ 值啥边都没有的概率。解得 $E_u=dfrac{f_u+P}{1-P}$ ,暴力维护即可。
#include<iostream> #include<cstring> #include<cstdio> #include<cstring> #include<vector> #include<queue> #include<algorithm> #include<climits> #define pii pair<int,int> #define pb push_back #define mp make_pair #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=1e3+11; int N,vis[MAXN]; double p[MAXN][MAXN]; double f[MAXN],now[MAXN]; double calc(int u){return (f[u]+now[u])/(1-now[u]);} void update(int u){ vis[u]=1; for(int i=1;i<=N;i++) if(!vis[i]) f[i]+=(calc(u)+1)*p[i][u]*now[i],now[i]*=(1-p[i][u]); return; } int main(){ N=read(); for(int i=1;i<=N;i++) for(int j=1;j<=N;j++){int x=read();p[i][j]=(double)x/100;} if(N==1){printf("0 ");return 0;} for(int i=1;i<N;i++) now[i]=1;update(N); while(1){ int ps=0; for(int i=1;i<=N;i++) if(!vis[i]) if(!ps||calc(i)<calc(ps)) ps=i; //cerr<<"ps:"<<ps<<" calc:"<<calc(ps)<<endl; update(ps); if(ps==1){printf("%.10lf ",calc(1));return 0;} } }
2021.01.02
[] CF513E2
考虑单独计算每个数的贡献,可以发现 $s$ 值最优肯定是上升下降的,若出现连续 $3$ 个下降那么其实可以将中间的贡献舍去。
那么最后答案形式肯定是 $1,+2,-2,+2,-2,1$ 类似的,$dp$ 即可。
#include<iostream> #include<cstring> #include<cstdio> #include<cstring> #include<vector> #include<queue> #include<algorithm> #include<climits> #define int long long #define pii pair<int,int> #define pb push_back #define mp make_pair #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=3e4+11; const int MAXK=211; int f[MAXN][MAXK][5],N,K,A[MAXN]; signed main(){ N=read(),K=read(); for(int i=1;i<=N;i++) A[i]=read(); memset(f,-127/3,sizeof(f)); for(int i=0;i<=3;i++) for(int j=0;j<=N;j++) f[j][0][i]=0; for(int i=1;i<=N;i++){ for(int j=1;j<=K;j++){ int s=1+(j!=1&&j!=K); f[i][j][0]=max(f[i-1][j-1][3],f[i-1][j][0])+s*A[i]; f[i][j][1]=max(f[i][j][0],f[i-1][j][1]); f[i][j][2]=max(f[i-1][j-1][1],f[i-1][j][2])-s*A[i]; f[i][j][3]=max(f[i][j][2],f[i-1][j][3]); if(s==2){ f[i][j][1]=max(f[i][j][1],f[i-1][j-1][1]); f[i][j][3]=max(f[i][j][3],f[i-1][j-1][3]); } } } printf("%lld ",max(max(f[N][K][0],f[N][K][1]),max(f[N][K][2],f[N][K][3]))); return 0; }
[x] CF578D
我们考虑 $S$ 如何生成所有的答案串,可以将 $S$ 中选取一位删掉然后在剩下 $n$ 个位置中插入一个字符。
若对于 $S=aaa|bb|c$ ,可以发现若删除一个 $a$ 则 $S'=aa|bb|c$ ,在 $a,b$ 的间隔与第一个 $b$ 的后面如果都填 $b$ 则会算重,则我们只能填 $m-1$ 个字符对任意位置。
而这样也会算重,因为对于串类似 $abababab$ 会算重,因为将 $a$ 放在最后或把 $b$ 放在最前是一样的,所以在找一下减去这样的串即可。
#include<iostream> #include<cstring> #include<cstdio> #include<cstring> #include<vector> #include<queue> #include<algorithm> #include<climits> #define pii pair<int,int> #define pb push_back #define mp make_pair #define fi first #define se second #define int long long using namespace std; inline int read(){ int f=1,ans=0;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=1e6+11; char str[MAXN]; int N,M,Ans; signed main(){ N=read(),M=read(); scanf("%s",str+1); for(int i=1;i<=N;i++) if(str[i]!=str[i-1]) Ans+=N*(M-1); int l=1; for(int i=2;i<=N+1;i++){ if(l==1){if(str[i]!=str[i-1]) l++;} else if(str[i]==str[i-2]) ++l; else{ //cerr<<"l:"<<l<<endl; Ans-=(l*(l-1))/2; if(str[i]!=str[i-1]) l=2; else l=1; } } printf("%lld ",Ans); return 0; }
[x] CF757F
建出最短路树后问题转化为删掉 $u$ 后有多少个点走不到 $S$ ,支配树模板。
#include<iostream> #include<cstring> #include<cstdio> #include<cstring> #include<vector> #include<queue> #include<algorithm> #include<climits> #define int long long #define pii pair<int,int> #define pb push_back #define mp make_pair #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=2e5+11; vector<pii> vec[MAXN]; vector<int> Vec[MAXN],g[MAXN],G[MAXN]; priority_queue<pii> que; queue<int> Q; int dis[MAXN],N,M,S,vis[MAXN],d[MAXN],sta[MAXN],fa[MAXN][21],siz[MAXN],dep[MAXN]; void dijkstra(){ memset(dis,127/3,sizeof(dis)); que.push(mp(0,S)),dis[S]=0; while(!que.empty()){ int xx=que.top().se; que.pop(); if(vis[xx]) continue; vis[xx]=1; for(auto pp:vec[xx]){ int v=pp.fi,w=pp.se; if(dis[v]>dis[xx]+w){dis[v]=dis[xx]+w;que.push(mp(-dis[v],v));} } }return; } void topsort(){ Q.push(S);while(!Q.empty()){ int xx=Q.front(); Q.pop(); sta[++sta[0]]=xx; for(auto v:g[xx]){d[v]--;if(!d[v]) Q.push(v);} }return; } int Lca(int u,int v){ if(dep[u]<dep[v]) swap(u,v); for(int i=20;i>=0;i--) if(dep[fa[u][i]]>=dep[v]) u=fa[u][i]; if(u==v) return u; for(int i=20;i>=0;i--) if(fa[u][i]!=fa[v][i]) u=fa[u][i],v=fa[v][i]; return fa[u][0]; } void dfs(int u){siz[u]=1;for(auto v:Vec[u]) dfs(v),siz[u]+=siz[v];return;} signed main(){ N=read(),M=read(),S=read(); for(int i=1;i<=M;i++){int u=read(),v=read(),w=read();vec[u].pb(mp(v,w)),vec[v].pb(mp(u,w));} dijkstra(); for(int i=1;i<=N;i++) for(auto v:vec[i]) if(dis[v.fi]==dis[i]+v.se) g[i].pb(v.fi),G[v.fi].pb(i),d[v.fi]++;topsort(); dep[S]=1; for(int i=2;i<=sta[0];i++){ int u=sta[i],L=G[u][0]; for(int j=1;j<G[u].size();j++) L=Lca(L,G[u][j]); Vec[L].pb(u); dep[u]=dep[L]+1; fa[u][0]=L; for(int j=1;(1<<j)<=dep[u];j++) fa[u][j]=fa[fa[u][j-1]][j-1]; } dfs(S); int Maxn=0; for(int i=1;i<=N;i++) if(i!=S) Maxn=max(Maxn,siz[i]); printf("%lld ",Maxn); return 0; }
2021.01.03
[] CF248E
设 $f_{i,j}$ 表示 $i$ 号杯内有 $j$ 个未吃过一次的概,转移即可。
#include<iostream> #include<cstring> #include<cstdio> #include<cstring> #include<vector> #include<queue> #include<algorithm> #include<climits> #define pii pair<int,int> #define pb push_back #define mp make_pair #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=1e5+11; int N,A[MAXN],all[MAXN],Q; double f[MAXN][111],Ans; double C(int a,int b){ double ans=1; for(int i=a;i>=a-b+1;i--) ans*=i; for(int i=2;i<=b;i++) ans/=i; return ans; } int main(){ //freopen("6.in","r",stdin); N=read(); for(int i=1;i<=N;i++) A[i]=read(),f[i][A[i]]=1; for(int i=1;i<=N;i++) Ans+=f[i][0],all[i]=A[i]; Q=read();while(Q--){ int u=read(),v=read(),k=read(); Ans-=f[u][0]; for(int i=0;i<=A[u];i++){ double s=0; for(int j=0;j<=k&&i+j<=all[u]&&i+j<=A[u];j++) s+=(double)(f[u][i+j]*C(i+j,j)*C(all[u]-i-j,k-j))/(double)C(all[u],k); f[u][i]=s; } all[u]-=k,all[v]+=k; Ans+=f[u][0]; printf("%.10lf ",Ans); }return 0; }
[x] CF646D
可以发现每个是相同的,则我们只要专心弄一个将答案乘 $k$ 即可。
设 $f_{i,j}$ 表示前 $i$ 个中当前我们的装备在 $j$ 级的方案数。 转移复杂度 $O(n^2)$ ,无法通过此题。
而又因为本题允许有一定的精度误差?故可以将 $j$ 进行减少,在 $j$ 取 $1000$ 时 $eps$ 已经可以通过。
而还有个细节我们可以倒推 $f$ 使得每个概率都为 $1$ 。
#include<iostream> #include<cstring> #include<cstdio> #include<cstring> #include<vector> #include<queue> #include<algorithm> #include<climits> #define pii pair<int,int> #define pb push_back #define mp make_pair #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=1e5+11; double f[2][1001]; int N,K,cur; int main(){ N=read(),K=read(); for(int i=1;i<=N;i++){ cur^=1;memset(f[cur],0,sizeof(f[cur])); for(int j=1;j<=1000;j++){ double p1=(1/(double)K),p2=1-p1; f[cur][j]+=((double)(f[cur^1][j+1]+j)/(double)(j+1)+(double)(f[cur^1][j]+(double)(j+1)/2)*j/(double)(j+1))*p1; f[cur][j]+=f[cur^1][j]*p2; } } printf("%.10lf ",f[cur][1]*K); return 0; }
[] CF906D
扩展欧拉定理模板题。
#include<iostream> #include<cstring> #include<cstdio> #include<cstring> #include<vector> #include<queue> #include<algorithm> #include<climits> #define int long long #define pii pair<int,int> #define pb push_back #define mp make_pair #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=1e5+11; int N,p,A[MAXN],ps,Q,ste[MAXN],nex[MAXN]; int Get(int x){ int Ans=x; for(int i=2;i*i<=x;i++){ if((x%i)) continue; while((x%i)==0) x/=i; Ans/=i,Ans*=(i-1); } if(x!=1) Ans/=x,Ans*=(x-1); return Ans; } bool FF; int ksm(int a,int b,int mod){int ans=1;while(b){if(b&1) ans*=a,ans%=mod;a*=a,a%=mod;b>>=1;}return ans;} bool pd(int l,int r,int Limit){// 1 < if(A[l]==1) return (1<Limit); int L=l,R=min(r,nex[l]-1),Up=A[R]; if(Up>=Limit) return 0; for(int i=R-1;i>=L;i--){ int res=A[i],cnt=Up; int s1=ksm(res,cnt,Limit),s2=ksm(res,cnt,Limit+1),s3=ksm(res,cnt,Limit+2); if(s1!=s2||s2!=s3||s1!=s3) return 0; Up=s1; }return 1; } int solve(int l,int r,int step){ if(A[l]==1) return A[l]%ste[step]; if(l==r) return A[l]%ste[step]; if(ste[step]==1) return 0; bool f=pd(l+1,r,ste[step+1]); if(!f) return ksm(A[l],solve(l+1,r,step+1)+ste[step+1],ste[step]); return ksm(A[l],solve(l+1,r,step+1),ste[step]); } signed main(){ //freopen("5.in","r",stdin); N=read(),p=read(); for(int i=1;i<=N;i++) A[i]=read(); ste[1]=p; for(int i=2;ste[i-1]!=1;i++) ste[i]=Get(ste[i-1]); ps=N+1; for(int i=N;i>=1;i--){nex[i]=ps;if(A[i]==1) ps=i;} Q=read();while(Q--){ int l=read(),r=read(); printf("%lld ",solve(l,r,1)); } }
2020.01.04
[] CF113D
设 $f_{i,j}$ 表示 $A$ 在 $i$ ,$B$ 在 $j$ 的概率,对于初始化来说默认将 $f_{S,T}$ 先置为 $1$ 。大力高斯消元即可。
#include<iostream> #include<cstring> #include<cstdio> #include<climits> #include<algorithm> #include<queue> #include<vector> #define pii pair<int,int> #define mp make_pair #define pb push_back #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=23; int N,M,S,T; vector<int> vec[MAXN]; double p[MAXN],A[MAXN*MAXN][MAXN*MAXN]; int Q(int u,int v){return (u-1)*N+v;} void Init(){ for(int i=1;i<=N;i++){ for(int j=1;j<=N;j++){ if(i==j){ A[Q(i,i)][Q(i,i)]=1; continue; } A[Q(i,j)][Q(i,j)]=1-p[i]*p[j]; int cnt=0; cnt=vec[j].size(); for(auto v:vec[j]) A[Q(i,v)][Q(i,j)]-=p[i]*(1-p[j])/cnt; cnt=vec[i].size(); for(auto v:vec[i]) A[Q(v,j)][Q(i,j)]-=p[j]*(1-p[i])/cnt; cnt=vec[i].size()*vec[j].size(); for(auto v1:vec[i]) for(auto v2:vec[j]) A[Q(v1,v2)][Q(i,j)]-=(1-p[i])*(1-p[j])/cnt; } } return; } void Gause(){ for(int i=1;i<=N*N;i++){ int ps=-1; for(int j=i;j<=N*N;j++) if(A[j][i]!=0){ps=j;break;} for(int j=0;j<=N*N;j++) swap(A[i][j],A[ps][j]); for(int j=1;j<=N*N;j++){ if(j==i||A[j][i]==0) continue; double K=A[j][i]/A[i][i]; for(int s=0;s<=N*N;s++) A[j][s]-=A[i][s]*K; } } return; } void print(){ for(int i=1;i<=N*N;i++){ for(int j=0;j<=N*N;j++) cout<<A[i][j]<<" "; printf(" "); }return; } int main(){ //freopen("A.in","r",stdin); N=read(),M=read(),S=read(),T=read(); for(int i=1;i<=M;i++){int u=read(),v=read();vec[u].pb(v),vec[v].pb(u);} for(int i=1;i<=N;i++) cin>>p[i]; Init(); A[Q(S,T)][0]=1; Gause(); for(int i=1;i<=N;i++) printf("%.10lf ",A[Q(i,i)][0]/A[Q(i,i)][Q(i,i)]); printf(" "); return 0; }
[x] CF603E
可以发现一个图可以选出一个点集使得度数都为奇的充要条件是每个联通块都为偶数。
证明可以考虑我们将度数想成异或 $1$ ,那么问题变成从全 $0$ 转换成全 $1$ 。则我们每次可以选择联通块中任意两个异或 $1$ 。
则对于偶数联通快很容易做到这一点,而对于奇数联通快无法做到。 因为最后会有 $n$ 个 $1$ ,而我们每次会让 $1$ 的个数 $+2,-2,+0$ 无法变成奇数,故得证。
我们发现答案是非严格单调不升,可以选择 $lct$ 或整体二分来实现。
考虑如何整体二分,设 $solve(l,r,x,y)$ 表示只考虑询问在 $[l,r]$ 中,且最后答案在 $[x,y]$ 中的情况,利用可撤销并查集即可维护。
#include<iostream> #include<cstring> #include<cstdio> #include<climits> #include<algorithm> #include<queue> #include<vector> #include<stack> #define pii pair<int,int> #define mp make_pair #define pb push_back #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=3e5+11; int N,M,Ans[MAXN],tmp[MAXN],W,Sum,id[MAXN]; struct node{int u,v,w,id;}E[MAXN],e[MAXN]; stack<pii> sta; struct Union{ int siz[MAXN],f[MAXN],Res[MAXN]; void init(){for(int i=1;i<=N;i++) siz[i]=1,f[i]=i,Res[i]=1;return;} int find(int x){return f[x]==x?x:find(f[x]);} void merge(int x,int y){ int t1=find(x),t2=find(y); if(t1==t2) return; if(siz[t1]<siz[t2]) swap(t1,t2); Sum-=(siz[t1]&1),Sum-=(siz[t2]&1); siz[t1]+=siz[t2];f[t2]=t1; Sum+=(siz[t1]&1); sta.push(mp(t1,t2));return; } void reset(){ int t1=sta.top().fi,t2=sta.top().se; sta.pop(); Sum-=(siz[t1]&1); siz[t1]-=siz[t2];f[t2]=t2; Sum+=(siz[t1]&1),Sum+=(siz[t2]&1); return; } }U; bool cmp(node x1,node x2){return x1.w<x2.w;} vector<int> vec[MAXN]; void solve(int l,int r,int x,int y){ //cerr<<"l:"<<l<<" r:"<<r<<" x:"<<x<<" y:"<<y<<endl; if(l>r) return; int mid=(l+r)>>1,siz=sta.size(); for(int i=l;i<=mid;i++) if(id[i]<x) U.merge(E[i].u,E[i].v); int ans=-1;for(int i=x;i<=y;i++){ if(e[i].id<=mid) U.merge(e[i].u,e[i].v); if(!Sum){ans=i;break;} } //cerr<<mid<<" "<<ans<<endl;//exit(0); if(ans==-1){ while(sta.size()!=siz) U.reset(); siz=sta.size(); for(int i=l;i<=mid;i++) if(id[i]<x) U.merge(E[i].u,E[i].v); solve(mid+1,r,x,y); while(sta.size()!=siz) U.reset(); return; }Ans[mid]=e[ans].w; while(sta.size()!=siz) U.reset(); siz=sta.size(); for(int i=l;i<=mid;i++) if(id[i]<x) U.merge(E[i].u,E[i].v); solve(mid+1,r,x,ans); while(sta.size()!=siz) U.reset(); siz=sta.size(); for(int i=x;i<ans;i++) if(e[i].id<l) U.merge(e[i].u,e[i].v); solve(l,mid-1,ans,y); while(sta.size()!=siz) U.reset(); return; } int main(){ //freopen("B.in","r",stdin); N=read(),M=read(); U.init(); Sum=N; for(int i=1;i<=M;i++) E[i].u=read(),E[i].v=read(),E[i].id=i,tmp[i]=E[i].w=read(),e[i]=E[i]; sort(e+1,e+M+1,cmp); for(int i=1;i<=M;i++) id[e[i].id]=i; solve(1,M,1,M); for(int i=1;i<=M;i++) if(!Ans[i]) Ans[i]=-1; for(int i=1;i<=M;i++) printf("%d ",Ans[i]); return 0; }/* 4 4 1 3 4 2 4 8 1 2 2 3 4 3 */
[] CF1363F
之前做过。
可以发现一次操作 $[A_l,A_{l+1},...,A_r]$ 变为 $[A_r,A_l,A_{l+1},...,A_{r-1}]$ 可以看做将 $A_r$ 换到任意在它前面字符的前面。
则无解情况就是二者的某一字符出现次数不同,否则肯定有解。
可以看出 $A_r$ 最多会往前 $1$ 次,则我们让不动点最多,即选择最多点的点集使得其字符相同。
问题相当于求两个串的 $lcs$ ,而对于 $S_i=T_j$ 时我们需要取判断是否将 $i$ 点固定是否有解,即 $S$ 后缀的字符数均大于等于 $T$ 的字符数。
#include<iostream> #include<cstring> #include<cstdio> #include<climits> #include<algorithm> #include<queue> #include<vector> #define pii pair<int,int> #define mp make_pair #define pb push_back #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=2e3+11; int f[MAXN][MAXN],cas,N,suf[MAXN][27][2]; char S[MAXN],T[MAXN]; bool check(int x,int y){ for(int i=0;i<26;i++) if(suf[x][i][0]<suf[y][i][1]) return 0; return 1; } int main(){ //freopen("C.in","r",stdin); cas=read(); while(cas--){ N=read(); for(int i=0;i<=N;i++) for(int j=0;j<=N;j++) f[i][j]=-INT_MAX/3; scanf("%s%s",S+1,T+1); for(int i=0;i<=N;i++) for(int j=0;j<26;j++) for(int k=0;k<2;k++) suf[i][j][k]=0; for(int i=N;i>=1;i--){ for(int j=0;j<26;j++) suf[i][j][0]=suf[i+1][j][0],suf[i][j][1]=suf[i+1][j][1]; suf[i][S[i]-'a'][0]++,suf[i][T[i]-'a'][1]++; } bool ff=1; for(int i=0;i<26;i++) ff&=(suf[1][i][0]==suf[1][i][1]); if(!ff){printf("-1 "); continue;} f[0][0]=0; for(int i=1;i<=N;i++) f[0][i]=f[i][0]=0; for(int i=1;i<=N;i++){ for(int j=1;j<=N;j++){ f[i][j]=max(f[i-1][j],f[i][j-1]); if(S[i]==T[j]&&check(i,j)) f[i][j]=max(f[i][j],f[i-1][j-1]+1); } } printf("%d ",N-f[N][N]); }return 0; }
20200106
[x] CF600F
可以发现答案的下界肯定是度数的最大值,我们尝试是否能取到下界。
考虑归纳构造,当前已得到前 $i-1$ 条边的方案,我们对于 $(u,v)$ 分别求 $mex$ ,若颜色相同即成功。
否则,我们不断改变,即找到一条 $c,c',c,c',c,c',...$ 的增广路,而停止条件是不存在该颜色。
若可以一直循环的条件是我们会遇到一个环,即从 $v$ 开始走会走到 $u$ ,但是二分图无奇环,即不存在这样的边。
所以按上述方法构造即可,时间复杂度 $O(nm)$ 。 这个貌似叫做正则二分图 $k$ 染色。
#include<iostream> #include<cstring> #include<cstdio> #include<climits> #include<algorithm> #include<queue> #include<vector> #define pii pair<int,int> #define mp make_pair #define pb push_back #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXM=1e5+11; const int MAXN=2e3+11; int nex[MAXN][MAXN],M,A,B,U[MAXM],V[MAXM],d[MAXN],Ans; int main(){ A=read(),B=read(),M=read(); for(int i=1;i<=M;i++) U[i]=read(),V[i]=read()+A,d[U[i]]++,d[V[i]]++; for(int i=1;i<=A+B;i++) Ans=max(Ans,d[i]); printf("%d ",Ans); for(int p=1;p<=M;p++){ int c1=-1,c2=-1,u=U[p],v=V[p]; for(int i=1;i<=Ans;i++) if(!nex[u][i]){c1=i;break;} for(int i=1;i<=Ans;i++) if(!nex[v][i]){c2=i;break;} nex[u][c1]=v,nex[v][c2]=u; if(c1==c2) continue; for(int c0=c2,i=v;i;i=nex[i][c0],c0^=c1^c2) swap(nex[i][c1],nex[i][c2]); } for(int i=1;i<=M;i++){ for(int j=1;j<=Ans;j++) if(nex[U[i]][j]==V[i]){printf("%d ",j);break;} }printf(" "); return 0; }
[] CF809E
第一道自己做出来的莫比乌斯反演题!(虽然写了很久
$$
Ans=sum_{i=1}^nsum_{j=1}^n phi(A_iA_j)cdot dis_{i,j}=sum_isum_j {V(gcd(A_i,A_j))} phi(A_i)cdot phi(A_j)cdot dis_{i,j}
$$
其中 $V(x)=prod_{pin prime} (1-dfrac{1}{p})$ 。
则
$$
Ans=sum_x V_xsum_isum_j [gcd(A_i,A_j)==x]cdot phi(A_i)cdot phi(A_j)cdot dis_{i,j}\=sum_x V(x)f(x)
$$
其中 $f(x)=[gcd(A_i,A_j)==x]cdot phi(A_i)cdot phi(A_j)cdot dis_{i,j}$ 。对 $f$ 反演,设 $F(x)=sum_{x|d} f(d)$ 。
则
$$
F(x)=sum_isum_j [x|A_i]cdot [x|A_j]cdot dis_{i,j}cdot phi(A_i)cdot phi A(j)
$$
而对于 $F(x)$ ,我们每次可以暴力把 $x|A_i$ 的 $i$ 点摘出来,每个点对在 $lca$ 处出现一次,故可以用虚树维护。
具体的,我们设摘出来的点为 $1-k$ ,则 $F(x)=sum_isum_j phi(A_i)cdot phi(A_j)cdot(dep_i+dep_j-2dep_{LCA})$ ,维护 $sum phi(A)$ 与 $sum phi(A)cdot dep_i$ 即可。
时间复杂度 $O(nlog^2 n)$ 。
#include<iostream> #include<cstring> #include<cstdio> #include<climits> #include<algorithm> #include<queue> #include<vector> #define pii pair<int,int> #define mp make_pair #define pb push_back #define fi first #define se second #define int long long #define mod 1000000007 using namespace std; inline int read(){ int f=1,ans=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=4e5+11; int phi[MAXN],N,A[MAXN],id[MAXN],pri[MAXN],mu[MAXN],v[MAXN],inv[MAXN],V[MAXN]; vector<int> vec[MAXN],Vec[MAXN]; void Init(){ mu[1]=1,phi[1]=1; V[1]=1; for(int i=2;i<=N;i++){ if(!v[i]){pri[++pri[0]]=i,v[i]=i,mu[i]=-1,phi[i]=i-1;V[i]=inv[i-1]*i%mod;} for(int j=1;j<=pri[0]&&i<=N/pri[j];j++){ v[i*pri[j]]=pri[j]; if(!(i%pri[j])){phi[i*pri[j]]=phi[i]*pri[j];V[i*pri[j]]=V[i];mu[i*pri[j]]=0;break;} phi[i*pri[j]]=phi[i]*(pri[j]-1); mu[i*pri[j]]=-mu[i]; V[i*pri[j]]=V[i]*V[pri[j]]%mod; } } return; } int ksm(int a,int b){int ans=1;while(b){if(b&1) ans*=a,ans%=mod;a*=a,a%=mod;b>>=1;}return ans;} int in[MAXN],out[MAXN],rev[MAXN],dep[MAXN],tot,Log[MAXN],F[MAXN],sta[MAXN],Q[MAXN],q[MAXN],f[MAXN]; vector<int> C; void dfs(int u,int fath){in[u]=++tot,rev[tot]=u,dep[u]=dep[fath]+1; for(auto v:vec[u]) if(v!=fath){dfs(v,u);rev[++tot]=u;} out[u]=tot;} bool cmp(int x,int y){return in[x]<in[y];} struct RMQ{ int Minn[MAXN][21],ps[MAXN][21]; void init(){ for(int i=1;i<=tot;i++) Minn[i][0]=dep[rev[i]],ps[i][0]=rev[i]; for(int j=1;(1<<j)<=tot;j++) for(int i=1;i+(1<<j)-1<=tot;i++){ if(Minn[i][j-1]<=Minn[i+(1<<(j-1))][j-1]) Minn[i][j]=Minn[i][j-1],ps[i][j]=ps[i][j-1]; else Minn[i][j]=Minn[i+(1<<(j-1))][j-1],ps[i][j]=ps[i+(1<<(j-1))][j-1]; } return; } int Qlca(int u,int v){ if(in[u]>in[v]) swap(u,v); int L=in[u],R=in[v],len=R-L+1,k=Log[len]; int w1=Minn[L][k],w2=Minn[R-(1<<k)+1][k]; if(w1<=w2) return ps[L][k]; return ps[R-(1<<k)+1][k]; } }S; void ins(int x){ if(sta[0]==1){if(sta[1]!=x) sta[++sta[0]]=x;return;} int now=S.Qlca(x,sta[sta[0]]); while(sta[0]>1&&dep[sta[sta[0]-1]]>=dep[now]) Vec[sta[sta[0]-1]].pb(sta[sta[0]]),sta[0]--; if(dep[sta[sta[0]]]>dep[now]) Vec[now].pb(sta[sta[0]]),sta[0]--; if(sta[sta[0]]!=now) sta[++sta[0]]=now; sta[++sta[0]]=x; return; } void build(){ sta[0]=0;sta[++sta[0]]=1; for(int i=1;i<=Q[0];i++)ins(Q[i]); while(sta[0]>1) Vec[sta[sta[0]-1]].pb(sta[sta[0]]),sta[0]--; return; } int S1[MAXN],S2[MAXN],res,D; // S1 dep*V S2 V void Clear(){for(auto v:C) Vec[v].clear(),S1[v]=S2[v]=0;C.clear();return;} void dfs1(int u){ int now1=0,now2=0; C.pb(u); for(auto v:Vec[u]){ dfs1(v); if(!(A[u]%D)) now2+=(S1[v]*phi[A[u]])%mod,now2-=phi[A[u]]*S2[v]%mod*dep[u]%mod,now2=((now2%mod)+mod)%mod; now1-=2*dep[u]*S2[u]%mod*S2[v]%mod,now1=(now1%mod+mod)%mod; now1+=S1[u]*S2[v],now1+=S2[u]*S1[v],now1%=mod; S1[u]+=S1[v],S2[u]+=S2[v]; S1[u]%=mod,S2[u]%=mod; } if(!(A[u]%D)) S1[u]+=dep[u]*phi[A[u]]%mod,S2[u]+=phi[A[u]]; S1[u]%=mod,S2[u]%=mod; res+=(now1+now2)*2%mod; return; } signed main(){ //freopen("1.in","r",stdin); N=read(); for(int i=1;i<=N;i++) A[i]=read(),id[A[i]]=i; inv[1]=1; for(int i=2;i<=N;i++) inv[i]=(mod-mod/i)*inv[mod%i]%mod; for(int i=1;i<N;i++){int u=read(),v=read();vec[u].pb(v),vec[v].pb(u);} Init(); dfs(1,0); Log[0]=-1; for(int i=1;i<=tot;i++) Log[i]=Log[i>>1]+1; S.init(); for(int i=1;i<=N;i++){ D=i;Q[0]=0,sta[0]=0; bool ff=0;for(int j=i;j<=N;j+=i) Q[++Q[0]]=id[j]; sort(Q+1,Q+Q[0]+1,cmp); build(); res=0; //cerr<<Q[0]<<" "<<i<<endl; dfs1(1); F[i]=res; Clear(); } for(int i=1;i<=N;i++) for(int j=i;j<=N;j+=i) f[i]+=mu[j/i]*F[j],f[i]=(f[i]%mod+mod)%mod; int Ans=0; for(int i=1;i<=N;i++) Ans+=V[i]*f[i],Ans%=mod; printf("%lld ",Ans*ksm(N*(N-1)%mod,mod-2)%mod); return 0; }/* 3 1 2 3 1 2 1 3 */
[x] CF814E
显然,若我们按照最短路进行分层每层点是连续的且每个点都仅和上一层的一个点连边。
很简单的想法是设 $f_{i,j}$ 表示当前层的区间为 $[i-j+1,i]$ 的方案数,而我们发现每一层的状态还与下一层点的个数有关。
故可以设 $g_{i,c2,c3}$ 表示当前层有 $i$ 个结点,且上一层中有 $c2$ 个度数为 $2$ 的点,有 $c3$ 个度数为 $3$ 的点,且连的只有上层点的边与这层与上层的连边的方案数。
故我们修改 $f$ 的定义,将其设为不管当前层的连边的方案数。 则 $f_{i,j}=sum f_{i-j,k} imes g_{j,c2,c3}$ ,$c2$ 指 $[i-j-k+1,i-j]$ 中度数为 $2$ 的点的个数,$c3$ 同理。
则我们只要求 $g$ 即可,由于 $nleq 50$ ,乱写个 $dp$ 就过了?实现精细一点可以做到 $O(n^3)$ 。
#include<iostream> #include<cstring> #include<cstdio> #include<climits> #include<algorithm> #include<queue> #include<vector> #define pii pair<int,int> #define mp make_pair #define pb push_back #define fi first #define se second #define int long long #define mod 1000000007 using namespace std; inline int read(){ int f=1,ans=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=51; const int inv2=(mod+1)>>1; int N,d[MAXN],S2[MAXN],S3[MAXN],f[MAXN][MAXN],g[MAXN][MAXN][MAXN]; int fac[MAXN],ifac[MAXN],inv[MAXN]; int C(int a,int b){return fac[a]*ifac[b]%mod*ifac[a-b]%mod;} signed main(){ //freopen("C.in","r",stdin); N=read(); for(int i=1;i<=N;i++) d[i]=read(); fac[0]=fac[1]=ifac[0]=ifac[1]=inv[1]=1; for(int i=2;i<=N;i++) fac[i]=fac[i-1]*i%mod,inv[i]=(mod-mod/i)*inv[mod%i]%mod,ifac[i]=ifac[i-1]*inv[i]%mod; for(int i=1;i<=N;i++){ S2[i]=S2[i-1],S3[i]=S3[i-1]; if(d[i]==2) S2[i]++; else S3[i]++; } g[0][0][0]=1; for(int i=1;i<=N;i++) for(int j=2;j<=i;j++) if(i-j-1>=0) g[0][0][i]+=g[0][0][i-j-1]*C(i-1,j)%mod*fac[j]%mod*inv2%mod; for(int i=1;i<=N;i++){ for(int j=0;j<=N;j++){ if(i>=2) g[0][i][j]+=(i-1)*g[0][i-2][j]%mod; if(j>=1) g[0][i][j]+=j*g[0][i][j-1]%mod; g[0][i][j]%=mod; } } for(int i=1;i<=N;i++){ for(int j=0;j<=N;j++){ for(int k=0;k<=N;k++){ g[i][j][k]=g[i-1][j-1][k]*j+g[i-1][j+1][k-1]*k; g[i][j][k]%=mod; } } } f[1+d[1]][d[1]]=1; for(int i=2+d[1];i<=N;i++){ for(int j=1;j<=i;j++){ for(int k=1;k<=i-j-1;k++){ //i-j-k+1 i-j f[i][j]+=f[i-j][k]*g[j][S2[i-j]-S2[i-j-k]][S3[i-j]-S3[i-j-k]]; f[i][j]%=mod; } } } int Ans=0; for(int i=1;i<=N-1;i++) Ans+=f[N][i]*g[0][S2[N]-S2[N-i]][S3[N]-S3[N-i]],Ans%=mod; printf("%lld ",Ans); return 0; }
20200107
[x] CF461E
可以发现对于对于 $B$ 来说肯定是能取就取的。所以我们只需要维护 $c1$ 字母开头,$c2$ 字母为第二串的开头的最小长度,即在 $SAM$ 上第一个走 $c1$ ,走 $c2$ 时会失配的最小长度。
由于值不会太大故我们只需要利用 $Trie$ 维护即可。 所以现在的问题是 $4$ 个点的完全图最多走少次使得总距离不超过 $n$ ,可以用过二分矩阵快速幂维护。 时间复杂度 $O(4^3 imes n)$ 。
#include<iostream> #include<cstring> #include<cstdio> #include<climits> #include<algorithm> #include<queue> #include<vector> #define int long long #define pii pair<int,int> #define mp make_pair #define pb push_back #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=1e5+11; int N,M; char S[MAXN]; struct Matrix{int A[4][4];void init(){memset(A,127/3,sizeof(A));return;}}F; Matrix operator*(Matrix x1,Matrix x2){ Matrix x3; x3.init(); for(int i=0;i<=3;i++) for(int j=0;j<=3;j++) for(int k=0;k<=3;k++) x3.A[i][j]=min(x3.A[i][j],x1.A[i][k]+x2.A[k][j]); return x3; } Matrix ksm(Matrix a,int b){ b--; Matrix Ans=a;while(b){if(b&1) Ans=Ans*a; a=a*a,b>>=1;}return Ans; } struct Trie{ int ch[MAXN*11][5],dep[MAXN*11],tot=1; void ins(int l){ int x=1; for(int i=1;i<=min(M-l+1,10ll);i++){ int c=S[l+i-1]-'A'; if(!ch[x][c]) ch[x][c]=++tot; dep[ch[x][c]]=dep[x]+1; x=ch[x][c]; }return; } void Query(int l){ int x=1; for(int i=1;i<=min(M-l+1,10ll);i++){ int c=S[l+i-1]-'A'; x=ch[x][c]; for(int j=0;j<=3;j++) if(!ch[x][j]) F.A[S[l]-'A'][j]=min(F.A[S[l]-'A'][j],dep[x]); }return; } }T; signed main(){ //freopen("1.in","r",stdin); N=read(); scanf("%s",S+1); M=strlen(S+1); for(int i=1;i<=M;i++) T.ins(i); F.init(); for(int i=1;i<=M;i++) T.Query(i); int l=1,r=N,res=0; /*for(int i=0;i<=3;i++){for(int j=0;j<=3;j++)printf("%d ",F.A[i][j]);printf(" ");} return 0;*/ while(l<=r){ int mid=(l+r)>>1; Matrix G=ksm(F,mid); int Minn=LLONG_MAX; for(int j=0;j<=3;j++) for(int k=0;k<=3;k++) Minn=min(Minn,G.A[j][k]); if(Minn<N){res=mid,l=mid+1;} else r=mid-1; } printf("%lld ",res+1); return 0; }/* 5 ABCCAD */
[x] CF464E
考虑通过 $dijkstra$ 的方法更新,可以发现瓶颈是如何维护 $dis$ 数组。
而假设我们已经知道 $dis_u$ ,对于边 $(u,v,w)$ 我们要做的是 $dis_u+2^w$ 即在 $dis_u$ 二进制分解后快速把 $w$ 位后的连续 $1$ 置为 $0$ ,然后将后面的那个 $0$ 置为 $1$ 。
这个过程可以通过主席树优化,时间复杂度 $O(nlog^2 n)$ 。
#include<iostream> #include<cstring> #include<cstdio> #include<climits> #include<algorithm> #include<queue> #include<vector> #define pii pair<int,int> #define mp make_pair #define pb push_back #define fi first #define se second #define LL long long #define mod 1000000007 using namespace std; inline int read(){ int f=1,ans=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=1e5+23; int rt[MAXN],pw[MAXN],Lim=1e5+20,N,M,Len[MAXN],SS,TT; struct Segment{ int Hash[MAXN*51],rs[MAXN*51],ls[MAXN*51],tot,siz[MAXN*51]; void pushup(int k,int l,int r){ int mid=(l+r)>>1,len=mid-l+1; siz[k]=siz[ls[k]]+siz[rs[k]]; Hash[k]=(LL)((LL)Hash[rs[k]]*pw[len]%mod+Hash[ls[k]])%mod; return; } void build0(int &k,int l,int r){ if(!k) k=++tot; Len[r-l+1]=k;if(l==r) return; int mid=(l+r)>>1; build0(ls[k],l,mid),build0(rs[k],mid+1,r); return; } void build1(int &k,int l,int r){ if(!k) k=++tot; if(l==r){Hash[k]=1,siz[k]=1;return;} int mid=(l+r)>>1; build1(ls[k],l,mid),build1(rs[k],mid+1,r); pushup(k,l,r);return; } bool cmp(int p,int q,int l,int r){ //cerr<<p<<" "<<q<<" "<<l<<" "<<r<<" "<<endl; if(l==r) return Hash[p]<Hash[q]; int mid=(l+r)>>1; if(Hash[rs[p]]!=Hash[rs[q]]) return cmp(rs[p],rs[q],mid+1,r); return cmp(ls[p],ls[q],l,mid); } void Modify(int &p,int l,int r,int ps){// ps 1 int las=p; p=++tot; if(l==r){siz[p]=Hash[p]=1;return;} ls[p]=ls[las],rs[p]=rs[las]; int mid=(l+r)>>1; if(ps<=mid) Modify(ls[p],l,mid,ps); else Modify(rs[p],mid+1,r,ps); pushup(p,l,r); return; } void Clear(int &p,int l,int r,int x,int y){ if(x<=l&&r<=y){p=Len[r-l+1];return;} int las=p; p=++tot; ls[p]=ls[las],rs[p]=rs[las]; int mid=(l+r)>>1; if(x<=mid) Clear(ls[p],l,mid,x,y); if(mid<y) Clear(rs[p],mid+1,r,x,y); pushup(p,l,r); return; } int Ans=-1; void Find1(int k,int l,int r){if(l==r){Ans=l;return;} int mid=(l+r)>>1; if(siz[ls[k]]==mid-l+1) Find1(rs[k],mid+1,r); else Find1(ls[k],l,mid);return;} void Find2(int k,int l,int r,int x,int y){ if(Ans!=-1) return; if(x<=l&&r<=y){if(siz[k]==r-l+1)return;Find1(k,l,r);return;} int mid=(l+r)>>1; if(x<=mid) Find2(ls[k],l,mid,x,y); if(Ans!=-1) return; if(mid<y) Find2(rs[k],mid+1,r,x,y); return; } int find(int id,int ps){Ans=-1;Find2(rt[id],0,Lim,ps,Lim);return Ans;} void Add(int id,int ps){int x=find(id,ps);if(ps<=x-1) Clear(rt[id],0,Lim,ps,x-1);Modify(rt[id],0,Lim,x);return;} }S; vector<pii> vec[MAXN]; struct node{int x;bool operator < (const node &p)const{return !S.cmp(rt[x],rt[p.x],1,Lim);}}O; priority_queue<node> que; int vis[MAXN],Que[MAXN],pre[MAXN]; vector<int> ANS; signed main(){ //freopen("maker.in","r",stdin); N=read(),M=read(); for(int i=1;i<=M;i++){int u=read(),v=read(),w=read();vec[u].pb(mp(v,w)),vec[v].pb(mp(u,w));} SS=read(),TT=read(); pw[0]=1; for(int i=1;i<=Lim;i++) pw[i]=(LL)pw[i-1]*2ll%mod; S.build0(rt[0],0,Lim);S.build1(rt[N+1],0,Lim);for(int i=1;i<=N;i++) rt[i]=rt[N+1]; O.x=SS; rt[SS]=rt[0]; que.push(O); Que[SS]=1; while(!que.empty()){ int xx=que.top().x; que.pop(); if(vis[xx]) continue;vis[xx]=1; if(xx==TT){ printf("%d ",S.Hash[rt[xx]]); int ee=TT; while(ee) ANS.pb(ee),ee=pre[ee]; printf("%lu ",ANS.size()); for(int i=ANS.size()-1;i>=0;i--) printf("%d ",ANS[i]); printf(" "); return 0; } for(auto pp:vec[xx]){ int v=pp.fi,w=pp.se; if(vis[v]) continue;rt[N+2]=rt[xx]; S.Add(N+2,w); if(S.cmp(rt[N+2],rt[v],0,Lim)){rt[v]=rt[N+2]; pre[v]=xx;O.x=v;que.push(O);} } }printf("-1 "); return 0; }
[] CF494C
很显然可以按照区间建树,设 $f_{i,j}$ 表示当前考虑到第 $i$ 个结点,且最大值 $leq max+j$ 的方案数,$max$ 表示当前区间的最大值。
答案即为 $sum (f_{1,i}-f_{1,i-1}) imes (max+i)$ 。而对于转移考虑 $i$ 结点是否选取即可。 时间复杂度 $O(q^2)$ 。
#include<iostream> #include<cstring> #include<cstdio> #include<climits> #include<algorithm> #include<queue> #include<vector> #include<map> #define pii pair<int,int> #define mp make_pair #define pb push_back #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=2e5+11; const int MAXM=5e3+11; struct node{int l,r,w; double p;} tr[MAXN]; int tot,N,M,A[MAXN],Maxn,d[MAXN],Id[MAXN],Log[MAXN]; map<pii,bool> Ma; vector<int> vec[MAXN]; double f[MAXM][MAXM<<1]; bool cmp(node x1,node x2){return x1.r-x1.l>x2.r-x2.l;} struct RMQ{ int Maxn[MAXN][21]; void init(){for(int i=1;i<=N;i++) Maxn[i][0]=A[i]; for(int j=1;(1<<j)<=N;j++) for(int i=1;i+(1<<j)-1<=N;i++) Maxn[i][j]=max(Maxn[i][j-1],Maxn[i+(1<<(j-1))][j-1]);} int Query(int L,int R){if(!L) L=1; if(R==N+1) R=N;int k=Log[R-L+1];return max(Maxn[L][k],Maxn[R-(1<<k)+1][k]);} }S; void dfs(int u){ if(!d[u]){f[u][0]=1-tr[u].p;for(int i=1;i<=M;i++) f[u][i]=1;return;} int L=tr[u].l,R=tr[u].r,Max=S.Query(L,R); for(auto v:vec[u]) dfs(v); for(int i=0;i<=M;i++){ double p0=tr[u].p,p1=1-tr[u].p; for(auto v:vec[u]){ int max=S.Query(tr[v].l,tr[v].r); //cerr<<max<<endl; if(i+Max-max-1>=0&&i+Max-max-1<=M) p0*=f[v][i+Max-max-1]; else if(i+Max-max-1<0) p0=0; if(i+Max-max>=0&&i+Max-max<=M) p1*=f[v][i+Max-max]; else if(i+Max-max<0) p1=0; } if(!i) p0=0; f[u][i]=p0+p1; //cerr<<"i:"<<i<<" f:"<<p0+p1<<endl; }//exit(0);return; } int main(){ //freopen("C.in","r",stdin); Log[0]=-1; for(int i=1;i<MAXN;i++) Log[i]=Log[i>>1]+1; N=read(),M=read(); for(int i=1;i<=N;i++) A[i]=read(),Maxn=max(Maxn,A[i]); S.init(); for(int i=1;i<=M;i++){ tr[++tot].l=read(),tr[tot].r=read(); cin>>tr[tot].p; tr[tot].w=1; } tr[++tot].l=0,tr[tot].r=N+1,tr[tot].p=0; sort(tr+1,tr+tot+1,cmp); for(int i=2;i<=tot;i++) for(int j=i-1;j>=1;j--) if(tr[j].l<=tr[i].l&&tr[i].r<=tr[j].r) {vec[j].pb(i),d[j]++;break;} dfs(1); //for(int i=1;i<=N;i++) for(int j=i;j<=N;j++) printf("(%d,%d):%d ",i,j,S.Query(i,j)); /*for(int i=1;i<=tot;i++){ for(int j=0;j<=M;j++) printf("%.3lf ",f[i][j]);printf(" "); }*///return 0; double Ans=0; for(int i=0;i<=M;i++){ double p=f[1][i]; if(i) p-=f[1][i-1]; //cerr<<p<<" "<<S.Query(1,N)<<endl; Ans+=p*(S.Query(1,N)+i); } printf("%.10lf ",Ans);return 0; } /* 3 2 1 2 3 1 3 0.500 1 2 0.800 */
20210109
[] CF285E
我们设 $f(i)$ 表示钦定 $i$ 个位置满足 $|p_i-i|=1$ 的方案数,$F(i)$ 表示恰好有 $i$ 个位置的方案数。
则 $f(i)=sum_j dbinom{j}{i} F(j)$ ,根据二项式反演可得 $F(i)=sum_{j} (-1)^{j-i}cdot dbinom{j}{i}cdot f(i)$ ,故现在只要求 $f(i)$ 即可。
对 $f(i)$ 我们可以将其看成一个二分图找出一个为 $i$ 的匹配,直接将二分图摘出来跑普通 $dp$ 即可。
#include<iostream> #include<cstring> #include<cstdio> #include<climits> #include<algorithm> #include<queue> #include<vector> #define pii pair<int,int> #define mp make_pair #define pb push_back #define fi first #define se second #define int long long #define mod 1000000007 using namespace std; inline int read(){ int f=1,ans=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=2e3+11; int N,M,f[MAXN][MAXN][2],fac[MAXN],C[MAXN][MAXN]; signed main(){ //freopen("3.in","r",stdin); N=read(),M=read(); fac[0]=1; for(int i=1;i<=N;i++) fac[i]=fac[i-1]*i%mod; //2N-2 C[0][0]=1; for(int i=1;i<=N;i++){C[i][0]=1; for(int j=1;j<=N;j++) C[i][j]=C[i-1][j]+C[i-1][j-1],C[i][j]%=mod;} f[0][0][0]=1; for(int i=1;i<=2*N-2;i++){ for(int j=0;j<=N;j++){ //f[i][j][0]+=f[i-1][j-`1] f[i][j][0]+=f[i-1][j][0]+f[i-1][j][1]; if(j&&i!=N) f[i][j][1]+=f[i-1][j-1][0]; if(j&&i==N) f[i][j][1]+=f[i-1][j-1][0]+f[i-1][j-1][1]; f[i][j][0]%=mod,f[i][j][1]%=mod; } }// g_i int Ans=0,pw=1; for(int i=M;i<=N;i++){ int W=((f[2*N-2][i][0]+f[2*N-2][i][1])*fac[N-i])%mod*C[i][M]%mod; Ans+=pw*W,Ans%=mod;pw*=-1; Ans=((Ans%mod)+mod)%mod; } printf("%lld ",Ans); //for(int i=0;i<=N;i++) printf("%lld ",(f[2*N-2][i][0]+f[2*N-2][i][1])*fac[N-i]);printf(" "); }
[x] CF436F
题目求的是对于每个 $c$ ,求 $max{sum [b_igeq c]cdot wcdot c+sum [b_i<c][a_igeq p] p}$ 。
可以发现前一段是定值,而后一段是要求维护一个前缀加等差数列,求最大值的数据结构。
而普通的 $log$ 数据结构难以维护这个类似凸包的东西,考虑分块。
我们对于块维护凸包,如何处理整块我们记录当前整块被覆盖多少次,设为 $cnt$ ,$d_i$ 表示散块对 $i$ 的贡献。
设 $i<j$ ,若比较 $d_i+icdot cnt$ 与 $d_j+jcdot cnt$ 的大小其实比的就是 $dfrac{d_i-d_j}{j-i}$ 与 $cnt$ 的大小,那么维护一个上凸壳即可。
而对于散块我们可以暴力重构当前块,时间复杂度 $O(nsqrt{n})$ 。
#include<iostream> #include<cstring> #include<cstdio> #include<cstring> #include<vector> #include<queue> #include<algorithm> #include<climits> #define pii pair<int,int> #define pb push_back #define mp make_pair #define fi first #define se second #define int long long using namespace std; inline int read(){ int f=1,ans=0;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=2e5+11; const int MAXM=451; vector<int> vec[MAXN]; int N,A[MAXN],B[MAXN],W,Id[MAXN],U; int Q[MAXM][MAXM],head[MAXM],tail[MAXM],Cnt[MAXN],d[MAXN],Maxn,Ans; bool cmp(int id1,int id2,int id3){return (d[id1]-d[id2])*(id3-id2)<=(d[id2]-d[id3])*(id2-id1);} void calc(int id,int S){while(head[id]<tail[id]&&(d[Q[id][head[id]]]-d[Q[id][head[id]+1]])<=S*(Q[id][head[id]+1]-Q[id][head[id]])) head[id]++;return;} void rebuild(int id){ head[id]=1,tail[id]=0; int l=(id-1)*U+1,r=min(100000ll,id*U); for(int i=l;i<=r;i++){ while(head[id]<tail[id]&&!cmp(Q[id][tail[id]-1],Q[id][tail[id]],i)) tail[id]--; Q[id][++tail[id]]=i; } calc(id,0); return; } void Add(int u){ if(!u) return; //cerr<<u<<"-haha"<<endl; for(int i=1;i<Id[u];i++) Cnt[i]++,calc(i,Cnt[i]); int l=(Id[u]-1)*U+1,r=min(Id[u]*U,100000ll); for(int i=l;i<=r;i++) d[i]+=Cnt[Id[u]]*i; for(int i=l;i<=u;i++) d[i]+=i; Cnt[Id[u]]=0; rebuild(Id[u]); return; } signed main(){ //freopen("1.in","r",stdin); N=read(),W=read(); for(int i=1;i<=N;i++) A[i]=read(),B[i]=read(),Maxn=max(Maxn,B[i]); U=sqrt(100000); for(int i=1;i<=100000;i++) Id[i]=(i-1)/U+1; for(int i=1;i<=Id[100000];i++) rebuild(i); for(int i=1;i<=N;i++) vec[B[i]].pb(i); int cnt=N; for(int i=0;i<=Maxn+1;i++){ if(i){for(auto v:vec[i-1]) Add(A[v]),cnt--;} int Ans1=cnt*W*i,Ans2=-1,ps=-1; for(int j=1;j<=Id[100000];j++){int x=Q[j][head[j]];if(d[x]+Cnt[j]*x>=Ans2)Ans2=d[x]+Cnt[j]*x,ps=x;} //cerr<<"Ans2:"<<Ans2<<" ps:"<<ps<<endl; Ans=Ans1+Ans2; printf("%lld %lld ",Ans1+Ans2,ps); //if(i==2) return 0; }return 0; }
[x] CF449E
小学奥数题。
设 $f(i)$ 表示四个点可以划分到一个 $icdot i$ 的矩形内的包含小方格的总个数。
我们令 $nleq m$ 则 $Ans=sum_{i=1}^n(n-i+1)cdot (m-i+1)cdot f(i)$ 。
而
$$
f(n)=n^2+sum_{i=1}^{n-1} (n-2cdot i)^2+4cdot g(i,n-i)
$$
$g(i,j)$ 表示在一个 $i,j$ 为底的直角三角形中包含的小方格个数。
由于皮克定理可得
$$
g(i,j)=dfrac{nm-n-m+gcd(n,m)}{2}
$$
通过化简式子那么可以快速求得 $f(i)$ 。
则现在只要快速求
$$
sum_isum_j (n-i+1)cdot (m-i+1)cdot f_i
$$
设 $A_{i,j}$ 表示 $n=i,m=j$ 时的答案。
可以发现, $A_{i,i}$ 与 $A_{i-1,i-1}$ 是有递推关系的,$A_{i,i}$ 与 $A_{i,j}$ 也是有递推关系的,则我们只要处理 $sum f_i$ 与 $sum f_icdot i$ 即可。 (但其实只要再将原式化简就好了
时间复杂度 $O(nlog n)$ 。
#include<iostream> #include<cstring> #include<cstdio> #include<cstring> #include<vector> #include<queue> #include<algorithm> #include<climits> #define int long long #define mod 1000000007 #define pii pair<int,int> #define pb push_back #define mp make_pair #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=1e6+11; const int inv2=500000004; const int inv6=166666668; const int Lim=1e6; int N,M,F[MAXN],W[MAXN],v[MAXN],pri[MAXN],phi[MAXN]; int ksm(int a,int b){int ans=1;while(b){if(b&1) ans*=a,ans%=mod;a*=a,a%=mod;b>>=1;}return ans;} int gcd(int a,int b){return !b?a:gcd(b,a%b);} int g(int a,int b){return (a*b-a-b+gcd(a,b))/2;} int Q1(int a){return a*(a+1)%mod*inv2%mod;} int Q2(int a){return a*(a+1)%mod*(2*a+1)%mod*inv6%mod;} void Init(){ phi[1]=1; for(int i=2;i<=Lim;i++){ if(!v[i]){v[i]=i;pri[++pri[0]]=i;phi[i]=i-1;} for(int j=1;pri[j]*i<=Lim&&j<=pri[0];j++){ v[pri[j]*i]=pri[j]; if(!(i%pri[j])){phi[i*pri[j]]=phi[i]*pri[j];break;} phi[pri[j]*i]=phi[i]*(pri[j]-1); } }return; } int G[MAXN],S1[MAXN],S2[MAXN]; signed main(){ //freopen("5.in","r",stdin); Init(); for(int i=1;i<=Lim;i++){ for(int j=1;i*j<=Lim;j++) W[i*j]+=phi[i]*j%mod,W[i*j]%=mod; W[i]-=i,W[i]=((W[i]%mod)+mod)%mod; } int cas=read(); for(int i=1;i<=Lim;i++){ int res=0; res=i*i*i; res%=mod; res-=2*i*Q1(i-1);res%=mod; res+=2*Q2(i-1); res%=mod; res-=2*(i-1)*i; res%=mod;res+=2*W[i],res%=mod; res=(res%mod+mod)%mod; F[i]=res; } for(int i=1;i<=Lim;i++){ G[i]=G[i-1]+F[i]+(2*i+1)*S1[i-1]-2*S2[i-1]; G[i]=((G[i]%mod)+mod)%mod; S1[i]=(S1[i-1]+F[i])%mod,S2[i]=(S2[i-1]+F[i]*i)%mod; } while(cas--){ N=read(),M=read(); if(N>M) swap(N,M); /*int Ans=0; for(int i=1;i<=N;i++) Ans+=(N-i+1)*(M-i+1)%mod*F[i]%mod,Ans%=mod;*/ int del=M-N,Ans=G[N],res=0; res=del*((N+1)*S1[N]%mod-S2[N])%mod,res=((res%mod)+mod)%mod; printf("%lld ",(Ans+res)%mod); } }
20210110
清华集训 $2014space day1$ 。
20210111
[] CF444E
答案由于具有单调性考虑二分,则现在的问题变为给每个点找个匹配使得最后满足 $x$ 的要求。
猜一下若每个点都符合条件则肯定能找出匹配。证明考虑我们把图建成二分图要求为完美匹配,利用 $HALL$ 定理判定可以发现单个的肯定强于多个的,故得证。
发现这个东西其实不用二分直接排序计算即可。
#include<iostream> #include<cstring> #include<cstdio> #include<climits> #include<algorithm> #include<queue> #include<vector> #define pii pair<int,int> #define mp make_pair #define pb push_back #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=3e3+11; int N,X[MAXN],sum; struct Edge{int u,v,w;} E[MAXN]; bool cmp(Edge x1,Edge x2){return x1.w<x2.w;} struct Union{ int f[MAXN],s[MAXN],siz[MAXN]; void init(){for(int i=1;i<=N;i++) s[i]=X[i],f[i]=i,siz[i]=1;return;} int find(int x){return f[x]==x?x:f[x]=find(f[x]);} void merge(int x,int y){ int t1=find(x),t2=find(y); if(t1==t2) return; f[t2]=t1,s[t1]+=s[t2]; siz[t1]+=siz[t2]; } }U; int main(){ //freopen("3.in","r",stdin); N=read(); for(int i=1;i<N;i++) E[i].u=read(),E[i].v=read(),E[i].w=read(); for(int i=1;i<=N;i++) X[i]=read(),sum+=X[i]; U.init(); sort(E+1,E+N,cmp); for(int i=1;i<N;i++){ int u=E[i].u,v=E[i].v,w=E[i].w; U.merge(u,v); int rt=U.find(u); int Siz=U.siz[rt],All=U.s[rt]; //for(int j=1;j<=N;j++) printf("%d ",U.s[j]);printf(" "); //for(int j=1;j<=N;j++) printf("%d ",U.siz[j]); printf(" "); if(Siz>sum-All){printf("%d ",E[i].w);return 0;} }printf("0 "); return 0; }/* 4 1 2 1 2 3 2 3 4 3 1 1 1 1 */
[x] CF603D
[] CF773E
假设 $forall i,A_i>0$ ,保证 $A$ 排序,且初始时 $F=0$ ,我们如何计算最后的 $F$ 。
设 $F_i$ 表示通过 $i$ 这段区间的 $F$ ,$cnt_i$ 表示 $A$ 序列中有多少个 $i$ 。
则 $F_i=min{F_{i-1}+cnt_i,i}$。 等价于 $F_i=min{F_{i-1},i-cnt_i}+cnt_i$ 。
推导可得 $F_n=sum_{i=0}^n cnt_i+min_i{i-sum_{j=0}^i cnt_j}$ 。
考虑一开始 $F_0$ 如果是负数,那么答案可以写成 $F_n=sum_{i=0}^n cnt_i+min{min_i{i-sum_{j=0}^i cnt_j},F_0}$ 。
这个算法为啥要保证 $A_i>0$ ,其实我们只要保证 $Fleq A_1$ 即可,即无 $3$ 操作的情况。
考虑有 $3$ 操作,即为 $exists i,A_i<0$ 的情况,那么我们肯定能找到一个阀值使得后面均无 $3$ 操作。
最后一个 $3$ 操作出现的地方一定是满足单调性的,即找到 $A_igeq -i$ ,然后二分即可。然后套用上述做法即可。
时间复杂度 $O(nlog^2 n)$ 。
#include<iostream> #include<cstring> #include<cstdio> #include<climits> #include<algorithm> #include<queue> #include<vector> #define pii pair<int,int> #define mp make_pair #define pb push_back #define fi first #define se second using namespace std; inline int read(){ int f=1,ans=0; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=1e6+11; const int L=-5e5; const int R=5e5; struct Segment1{ int Minn[MAXN<<2],tag[MAXN<<2]; void build(int k,int l,int r){ if(l==r){Minn[k]=l;return;} int mid=(l+r)>>1; build(k<<1,l,mid),build(k<<1|1,mid+1,r); Minn[k]=min(Minn[k<<1],Minn[k<<1|1]);return; } void pushdown(int k){ if(!tag[k]) return; Minn[k<<1]+=tag[k],Minn[k<<1|1]+=tag[k]; tag[k<<1]+=tag[k],tag[k<<1|1]+=tag[k]; tag[k]=0;return; } void Modify(int k,int l,int r,int x,int y,int w){ if(x<=l&&r<=y){tag[k]+=w,Minn[k]+=w;return;} int mid=(l+r)>>1; pushdown(k); if(x<=mid) Modify(k<<1,l,mid,x,y,w); if(mid<y) Modify(k<<1|1,mid+1,r,x,y,w); Minn[k]=min(Minn[k<<1],Minn[k<<1|1]); return; } int Query(int k,int l,int r,int x,int y){ if(x<=l&&r<=y) return Minn[k]; int mid=(l+r)>>1,res=INT_MAX; pushdown(k); if(x<=mid) res=min(res,Query(k<<1,l,mid,x,y)); if(mid<y) res=min(res,Query(k<<1|1,mid+1,r,x,y)); Minn[k]=min(Minn[k<<1],Minn[k<<1|1]); return res; } }S; struct Segment2{ int Sum[MAXN<<2]; void Modify(int k,int l,int r,int ps,int w){ if(l==r){Sum[k]+=w;return;} int mid=(l+r)>>1; if(ps<=mid) Modify(k<<1,l,mid,ps,w); if(mid<ps) Modify(k<<1|1,mid+1,r,ps,w); Sum[k]=Sum[k<<1]+Sum[k<<1|1]; return; } int Query(int k,int l,int r,int x,int y){ if(x<=l&&r<=y) return Sum[k]; int res=0,mid=(l+r)>>1; if(x<=mid) res+=Query(k<<1,l,mid,x,y); if(mid<y) res+=Query(k<<1|1,mid+1,r,x,y); return res; } int Find(int k,int l,int r,int kth){ if(l==r) return l; int mid=(l+r)>>1; if(Sum[k<<1]<kth) return Find(k<<1|1,mid+1,r,kth-Sum[k<<1]); return Find(k<<1,l,mid,kth); } }T; int Q,all,Minn=INT_MAX; void ins(int x){S.Modify(1,L,R,x,R,-1);T.Modify(1,L,R,x,1);all++;Minn=min(Minn,x);return;} int Query(int ps,int F){ int cnt=T.Query(1,L,R,ps,R),Minn=min(F,S.Query(1,L,R,ps,R)+(all-cnt)); return cnt+Minn; } int Sta[MAXN]; int main(){ //freopen("maker.in","r",stdin); S.build(1,L,R); Q=read(); while(Q--){ int x=read(); ins(x); if(Minn>=0){printf("%d ",Query(0,0));continue;} int l=1,r=all,res=INT_MAX; while(l<=r){ int mid=(l+r)>>1; if(T.Find(1,L,R,mid)<=-mid) res=mid,l=mid+1; else r=mid-1; } if(res==INT_MAX||res==all){printf("%d ",-all);continue;} int Kth=T.Find(1,L,R,res+1); printf("%d ",Query(Kth,-res)); } } /* 3 0 2 -1 */