ZROI2019 提高十连测
额 掰手指头一数 特么又是第三年十连测了= =
2017一场没打 那时候好像一场比赛也就100人左右
2018前几场还都好好补了 后来开始放飞自我了 这时候一场有150人还多了
2019想让今年的Noip不留遗憾 好像一场有200人多 现在好的能冲进前十不好也就三四十
主要是把题解里可以借鉴的思路和代码罗列一下
一。
A.(ZROI954)
考虑对这玩意dp pi=1等价于右括号 pi=2等价于左括号 pi=3等价于左右任选括号 我们需要让其括号匹配的代价尽量小 然后排序顺序按照能力值大小,相等时按照231来排就可以了 分析复杂度 发现k是sqrt(n)级别的 所以总复杂度是O(nsqrt(n)) 但是要注意dp数组不能memset。。不然会让复杂度分析凉掉。。(这个代码是后来手机改的所以格式有点奇怪。。。
//Love and Freedom. #include<cstdio> #include<cmath> #include<algorithm> #include<cstring> #define ll long long #define inf 20021225 #define N 500 using namespace std; int read() { int s=0,t=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') t=-1; ch=getchar();} while(ch>='0' && ch<='9') s=s*10+ch-'0',ch=getchar(); return s*t; } struct node{int type,w,s;}a[100010]; bool cmp(node x,node y) { if(x.w!=y.w) return x.w<y.w; return x.type<y.type; } ll f[2][N+2][N+2]; int main() { //freopen("ex_group3.in","r",stdin); int n=read(),k=read(); for(int i=1;i<=n;i++) { a[i].w=read(),a[i].s=read(),a[i].type=read(); if(a[i].type==2) a[i].type=1; else if(a[i].type==3) a[i].type=2; else a[i].type=3; } if(n<2*k){printf("-1\n"); return 0;} sort(a+1,a+n+1,cmp); memset(f,48,sizeof(f)); f[0][0][0]=0; int p=0; for(int i=1;i<=n;i++) { p^=1;// memset(f[p],48,sizeof(f[p])); for(int j=0;j<=k;j++) for(int l=0;l+j<=k;l++) { f[p][j][l]=f[p][N][N]; } for(int j=0;j<=k;j++) for(int l=0;l+j<=k;l++) { f[p][j][l]=min(f[p][j][l],f[p^1][j][l]); if(a[i].type==1 || a[i].type==2) { if(l) f[p][j][l]=min(f[p][j][l],f[p^1][j][l-1]+a[i].s); } if(a[i].type==2 || a[i].type==3) { if(j) f[p][j][l]=min(f[p][j][l],f[p^1][j-1][l+1]+a[i].s); } //printf("%d %d %d %lld\n",p,j,l,f[p][j][l]); } } p^=1; memset(f[p],48,sizeof(f[p])); if(f[p^1][k][0]==f[p][0][0]) printf("-1"); else printf("%lld\n",f[p^1][k][0]); return 0; }
B.(ZROI955)
先考虑一维的做法 我们发现折起来其实相当于把短的一边砍掉 然后发现对于一个答案区间[l,r]合法相当于[1,r]&&[l,n]都合法(脑补一下
最后的话就是分别统计计数就好了 继续考虑二维的做法 我们发现行列之间互不影响 最后答案是行列乘起来就好了
对于单纯行来说 我们把列直接哈希掉就可以了 我单哈也过了(@lky 快点砸ZR
//Love and Freedom. #include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #include<string> #include<vector> #define ll long long #define inf 20021225 #define md 20040423 #define bs 37 #define str string #define pb push_back #define N 1000010 using namespace std; int read() { int s=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1; ch=getchar();} while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar(); return f*s; } vector<str> vec; str ch; vector<int> pos; int hs1[N],hs2[N],cnt; int a[N<<1],f[N<<1]; ll calc(int cnt,int n) { pos.clear(); int len,mx=0,mr=0,p=0,ans=1; pos.pb(1); for(int i=1;i<=cnt;i++) { if(mr>=i) f[i]=min(f[(p<<1)-i],mr-i); while(i+f[i]<=cnt&&i-f[i]>=1&&a[i+f[i]]==a[i-f[i]]) f[i]++; if(i+f[i]>mr) mr=i+f[i],p=i; mx=max(mx,f[i]-1); if(!(i&1) && i!=2) { int id=i>>1; if(f[i-1]>=2*(id-ans)+1) ans=id,pos.pb(id); } } ans=n; int tmp=pos.size()-1; ll cur=0; for(int i=cnt;i;i--) { if(!(i&1)) { int id=i>>1;// printf("%d %d %d %d\n",i,id,f[i+1],2*(ans-id)+1); if(f[i+1]>=2*(ans-id)+1) { while(~tmp && pos[tmp]>id) tmp--; cur+=tmp+1; ans=id; } } } return cur; } int main() { int n=read(),m=read(); for(int i=0;i<n;i++) cin>>ch,vec.pb(ch); for(int i=0;i<n;i++) { int hs=0; for(int j=0;j<m;j++) hs=(1ll*hs*bs%md+vec[i][j]-'a'+1)%md; hs1[i]=hs; } cnt=0; a[++cnt]=0; for(int i=0;i<n;i++) a[++cnt]=hs1[i],a[++cnt]=0; ll ans=calc(cnt,n);// printf("%d\n",ans); for(int i=0;i<m;i++) { int hs=0; for(int j=0;j<n;j++) hs=(1ll*hs*bs%md+vec[j][i]-'a'+1)%md; hs2[i]=hs;// printf("%d\n",hs); } cnt=0; a[++cnt]=0;// printf("%d\n",ans); for(int i=0;i<m;i++) a[++cnt]=hs2[i],a[++cnt]=0; printf("%lld\n",ans*calc(cnt,m)); return 0; }
C.(ZROI956)
神仙题ovo!0/1操作已经把trie暗示的很明显了。我们发现+1会对后缀连续一段1进行翻转。所以我们反过来建trie。然后异或可以打整体标记。好像就做完了ovo。
//Love and Freedom. #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #include<vector> #define ll long long #define inf 20021225 #define N 300010 #define ls(x) t[x].son[0] #define rs(x) t[x].son[1] using namespace std; int read() { int s=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1; ch=getchar();} while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar(); return f*s; } struct node{int son[2],tag,ed;}t[N*31]; int poi,k; void insert(int s) { int pos=0; for(int i=0;i<30;i++) { int ch=(s>>i&1)^(k>>i&1); if(!t[pos].son[ch]) t[pos].son[ch]=++poi; pos=t[pos].son[ch]; } t[pos].ed++; } void del(int x) { int pos=0; for(int i=0;i<30;i++) { int ch=(x>>i&1)^(k>>i&1); if(!t[pos].son[ch]) t[pos].son[ch]=++poi; pos=t[pos].son[ch]; } t[pos].ed--;// printf("GG"); } void add() { int pos=0; for(int i=0;i<30;i++) { int ch=(k>>i&1); swap(ls(pos),rs(pos)); if(!t[pos].son[ch]) break; pos=t[pos].son[ch]; } } vector<int> ans; void dfs(int pos,int val,int d) { for(int i=1;i<=t[pos].ed;i++) ans.push_back(val); if(d==30) return; int ch=k>>d&1; if(t[pos].son[ch]) dfs(t[pos].son[ch],val,d+1); if(t[pos].son[ch^1]) dfs(t[pos].son[ch^1],val^(1<<d),d+1); } int main() { int n=read(),q=read(),x; for(int i=1;i<=n;i++) x=read(),insert(x); while(q--) { int opt=read(); if(opt!=3) x=read(); if(opt==1) insert(x); else if(opt==2) del(x); else if(opt==3) add(); else k^=x; } dfs(0,0,0); sort(ans.begin(),ans.end()); for(int i=0;i<ans.size();i++) printf("%d ",ans[i]); return 0; }
二。
A.(ZROI957)
先求出部分解再调整得到整体解的思路值得借鉴。首先可以想到贪心一定可以求出一组解(前提:解存在)
然后我们考虑倒着调整这组解,我们发现对于S中i和i+1两个位置如果不一样的话 那么这两个中间一定要有一个被匹配(题目要求)然后我们发现每次选最往后的调整一定是不劣的,所以直接暴力扫一遍就可以了。
//Love and Freedom. #include<cstdio> #include<cmath> #include<algorithm> #include<cstring> #define ll long long #define inf 20021225 #define N 300010 using namespace std; int read() { int s=0,t=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') t=-1; ch=getchar();} while(ch>='0' && ch<='9') s=s*10+ch-'0',ch=getchar(); return s*t; } char s[N],t[N]; int n,m,pos[N]; bool vis[N]; int main() { n=read(),m=read(); scanf("%s%s",s+1,t+1); int i,p; for(i=1,p=1;i<=n && p<=m;i++) if(s[i]==t[p]) pos[p++]=i; if(p<=m) return printf("-1\n"),0; for(i=n-1,p=m;i&&pos[p]<i&&p;i--) if(s[i]!=s[i+1] && !vis[i+1]) pos[p]=s[i]==t[p]?i:i+1,vis[pos[p]]=1,p--; if(!p) for(;i;i--) if(s[i]!=s[i+1] && !vis[i+1]) return printf("-1\n"),0; for(int i=1;i<=m;i++) printf("%d ",pos[i]); return 0; }
B.(ZROI958)
我们考虑二分k,显然可以倍增建图O(nlg^2n)但是常数较大容易wei掉。我们考虑用并查集替换这个过程,因为图是一棵基环树,所以用并查集维护bfs直接把环断掉就OK了。好神仙啊。
//Love and Freedom. #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #include<queue> #define ll long long #define inf 20021225 #define N 300010 #define pa pair<int,int> #define fs first #define se second #define mp make_pair using namespace std; int read() { int s=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();} while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar(); return f*s; } int n,to[N][2]; struct bcj { int fa[N],dis[N]; bool vis[N]; void init() { for(int i=1;i<=n;i++) vis[i]=dis[i]=0,fa[i]=i; } int find(int x) { if(x==fa[x]) return x; int fx=find(fa[x]); dis[x]+=dis[fa[x]]; return fa[x]=fx; } }c[2]; queue<pa> q; bool check(int k) { c[0].init(); c[1].init(); while(!q.empty()) q.pop(); q.push(mp(1,0)); q.push(mp(1,1)); c[0].vis[1]=c[1].vis[1]=1; while(!q.empty()) { pa tmp=q.front(); q.pop(); int x=tmp.fs,s=tmp.se; x=to[x][s]; while(inf) { int fx=c[s].find(x); if(c[s^1].vis[fx]) break; if(c[s].dis[x]>=k) break; if(fx==n) return 1; c[s^1].vis[fx]=1; q.push(mp(fx,s^1)); if(c[s].find(to[fx][s])==fx) break; c[s].fa[fx]=to[fx][s]; ++c[s].dis[fx]; } } return 0; } int main() { n=read(); for(int i=1;i<=n;i++) to[i][0]=read(),to[i][1]=read(); int l=1,r=n,ans=-1; while(l<=r) { int mid=l+r>>1; if(check(mid)) ans=mid,r=mid-1; else l=mid+1; } printf("%d\n",ans); return 0; }
C.(ZROI959)
常规套路好题。考虑每个数的质因数不超过7个,所以我们可以直接对于每个质因数暴力建图(其实也不需要建出来 直接在搜索的时候判一下边权就可以了)然后我们需要找到对于每个质因数过u,v的最长链,如果有一个最长链和原树中最长链一样的话答案就是n+1不然的话就是所有长度取max+1 一个很好的写法就是dfs(x,fa,p)用fa直接ban掉一个方向的路径。最后复杂度是O(nlgnlgw)。
//Love and Freedom. #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #define ll long long #define inf 20021225 #define N 200010 #define P 1000000 #define pa pair<int,int> #define mp make_pair #define fs first #define se second using namespace std; int read() { int s=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();} while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar(); return f*s; } int gcd(int x,int y){return y?gcd(y,x%y):x;} struct edge{int to,lt,v;}e[N<<1]; int F[N][19],dep[N],G[N][19]; bool vis[P]; int f[N][2],g[N],in[N],cnt; void add(int x,int y,int v) { e[++cnt].to=y; e[cnt].lt=in[x]; e[cnt].v=v; in[x]=cnt; e[++cnt].to=x; e[cnt].lt=in[y]; e[cnt].v=v; in[y]=cnt; } void getfa(int x) { for(int i=1;i<19;i++) F[x][i]=F[F[x][i-1]][i-1],G[x][i]=gcd(G[x][i-1],G[F[x][i-1]][i-1]); for(int i=in[x];i;i=e[i].lt) { int y=e[i].to; if(y==F[x][0]) continue; F[y][0]=x; G[y][0]=e[i].v; dep[y]=dep[x]+1; getfa(y); } } pa jump(int x,int l) { int g=0; for(int i=0;i<19;i++) if(l>>i&1) g=gcd(G[x][i],g),x=F[x][i]; return mp(x,g); } pa LCA(int x,int y) { if(dep[x]<dep[y]) swap(x,y); pa tmp=jump(x,dep[x]-dep[y]); x=tmp.fs; if(x==y) return tmp; int g=tmp.se; for(int i=18;~i;i--) if(F[x][i]!=F[y][i]) g=gcd(gcd(G[y][i],G[x][i]),g),x=F[x][i],y=F[y][i]; return mp(F[x][0],gcd(gcd(G[x][0],G[y][0]),g)); } int solve(int x,int fa,int p) { int ans=0; for(int i=in[x];i;i=e[i].lt) { if(e[i].v%p) continue; int y=e[i].to; if(y==fa) continue; ans=max(ans,solve(y,x,p)+1); } return ans; } bool np[P+2]; int pri[N],tot,lt[P+2]; void sieve() { for(int i=2;i<=P;i++) { if(!np[i]) pri[++tot]=i,lt[i]=i; for(int j=1;j<=tot&&i*pri[j]<=P;j++) { np[i*pri[j]]=1; lt[i*pri[j]]=pri[j]; if(i%pri[j]==0) break; } } } int main() { sieve(); int n=read(),q=read(),x,y,v; for(int i=1;i<n;i++) x=read(),y=read(),v=read(),add(x,y,v); getfa(1); while(q--) { x=read(),y=read(); pa tmp=LCA(x,y); if(tmp.se==1){printf("%d\n",dep[x]+dep[y]-2*dep[tmp.fs]); continue;} int fx=tmp.fs==x?jump(y,dep[y]-dep[x]-1).fs:F[x][0]; int fy=tmp.fs==y?jump(x,dep[x]-dep[y]-1).fs:F[y][0]; int mx=solve(x,fx,1)+solve(y,fy,1); int ans=0; while(tmp.se!=1) { int g=lt[tmp.se]; while(tmp.se%g==0) tmp.se/=g; ans=max(ans,solve(x,fx,g)+solve(y,fy,g)); if(ans==mx) break; } if(ans==mx) ans=-1; else ans+=dep[x]+dep[y]-2*dep[tmp.fs]+1; printf("%d\n",ans); } return 0; }
三。
A.(ZROI960)
斯波题。枚举答案,然后用lower_bound查找即可。复杂度分析用到调和级数:O(nlg^2n) 话说这题暴力跑的比lg^2快咋整啊。
//Love and Freedom. #include<cstdio> #include<cmath> #include<algorithm> #include<cstring> #define ll long long #define inf 20021225 #define N 100010 using namespace std; int read() { int s=0,t=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') t=-1; ch=getchar();} while(ch>='0' && ch<='9') s=s*10+ch-'0',ch=getchar(); return s*t; } int pre[2][N],n; char ch[N]; int calc(int s) { int pos=0,ans=0,flag=0; while(pos<=n) { pos=lower_bound(pre[0]+pos+1,pre[0]+n+1,pre[0][pos]+s)-pre[0]; if(pos>n) return flag>2?ans-1:0; ans+=s; flag++; pos=lower_bound(pre[1]+pos+1,pre[1]+n+1,pre[1][pos]+1)-pre[1]; if(pos>n) return flag>2?ans:0; flag++; ans++; } } int main() { scanf("%s",ch+1); n=strlen(ch+1); int ans=0; for(int i=1;i<=n;i++) pre[0][i]+=(ch[i]=='0'),pre[1][i]+=(ch[i]=='1'); for(int i=1;i<=n;i++) pre[0][i]+=pre[0][i-1],pre[1][i]+=pre[1][i-1]; for(int s=0;s<n;s++) ans=max(ans,calc(s)); printf("%d\n",pre[1][n]?ans:0); return 0; }
B.(ZROI961)
神仙题。sun切了。场上做法做的是bitset优化转移。正解的话,可以发现如果我们有三个数x,y,z 且满足$\frac{1}{1.1}z <= x <= y <=z$的话,y是没有必要存下来的,而$log_{1.1} 2e15$只有370,所以我们可以暴力转移保存。如果使用sort的话时间复杂度是lglg,所以使用归并排序即可。
//Love and Freedom. #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #include<vector> #include<queue> #define ll long long #define inf 20021225 #define N 200010 #define it vector<ll>::iterator #define vec vector<ll> #define pb push_back using namespace std; int read() { int s=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();} while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar(); return f*s; } struct edge{int to,lt; ll v;}e[N]; int in[N],cnt,n,m; vector<ll> len[N],tmp; queue<int> q; int d[N],a[N],top; void add(int x,int y,ll v) { e[++cnt].to=y; e[cnt].lt=in[x]; e[cnt].v=v; in[x]=cnt; d[y]++; } void pushback(ll val) { if(tmp.size()>=2 && tmp[tmp.size()-2]*11/10>=val) tmp.pop_back(); tmp.pb(val); } void merge(vec a,vec b,ll va,ll vb,vec &c) { it i=a.begin(),j=b.begin(); tmp.clear(); ll val; while(i<a.end() && j<b.end()) { if((*i)+va==(*j)+vb) val=(*i)+va,i++,j++; else if((*i)+va<(*j)+vb) val=(*i)+va,i++; else val=(*j)+vb,j++; pushback(val); } while(i<a.end()) val=(*i)+va,i++,pushback(val); while(j<b.end()) val=(*j)+vb,j++,pushback(val); c=tmp; } void ins2(int x,int y,ll v) { merge(len[x],len[y],v,0,len[y]); } void ins(int x) { for(int i=in[x];i;i=e[i].lt) ins2(x,e[i].to,e[i].v); } int main() { n=read(),m=read(); int Q=read(),x,y; ll v; for(int i=1;i<=m;i++) x=read(),y=read(),scanf("%lld",&v),add(x,y,v); for(int i=1;i<=n;i++) if(!d[i]) q.push(i); while(!q.empty()) { int x=q.front(); q.pop(); a[++top]=x;// printf("%d\n",x); for(int i=in[x];i;i=e[i].lt) if(!(--d[e[i].to])) q.push(e[i].to); } len[1].pb(0); for(int i=1;i<=top;i++) ins(a[i]); while(Q--) { int x=read(); scanf("%lld",&v); it i=lower_bound(len[x].begin(),len[x].end(),v); if(i!=len[x].end() && (*i)<=v*11/10) printf("YES\n"); else printf("NO\n"); } return 0; }
C.(ZROI962)
大数据结构题(雾。最后0.5h突然就会了,然后写了个暴力还写挂了(MMP。 发现我们只需要线段树分别维护行列极值,然后分三种情况讨论一下就可以了。其中一种情况需要线段树上二分,我好像写丑了常数有点大...不过这种题写起来是真的神清气爽。
//Love and Freedom. #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #define ll long long #define inf 20021225 #define N 100010 #define ls (x<<1) #define rs (x<<1|1) #define mid (l+r>>1) using namespace std; int read() { int s=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();} while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar(); return f*s; } struct SGT { int mn[N<<2],mx[N<<2],n; void pushup(int x) { mn[x]=min(mn[ls],mn[rs]); mx[x]=max(mx[ls],mx[rs]); } void modify(int x,int l,int r,int d,int v) { if(l==r){mn[x]=mx[x]=v; return;} if(d<=mid) modify(ls,l,mid,d,v); else modify(rs,mid+1,r,d,v); pushup(x); } int query(int x,int l,int r,int LL,int RR,int f) { if(LL<=l&&r<=RR){return f?mx[x]:mn[x];} int ans=f?0:inf; if(LL<=mid) ans=f?max(ans,query(ls,l,mid,LL,RR,f)):min(ans,query(ls,l,mid,LL,RR,f)); if(RR>mid) ans=f?max(ans,query(rs,mid+1,r,LL,RR,f)):min(ans,query(rs,mid+1,r,LL,RR,f)); return ans; } int pquery(int x,int l,int r,int LL,int RR,int v) { if(l==r) return mx[x]>=v?l:-1; if(LL<=l&&r<=RR) { if(mx[rs]>=v) return pquery(rs,mid+1,r,LL,RR,v); if(mx[ls]>=v) return pquery(ls,l,mid,LL,RR,v); return -1; } int ans=-1; if(LL<=mid) ans=max(ans,pquery(ls,l,mid,LL,RR,v)); if(RR>mid) ans=max(ans,pquery(rs,mid+1,r,LL,RR,v)); return ans; } int squery(int x,int l,int r,int LL,int RR,int v) { if(l==r) return mx[x]>=v?l:inf; if(LL<=l&&r<=RR) { if(mx[ls]>=v) return squery(ls,l,mid,LL,RR,v); if(mx[rs]>=v) return squery(rs,mid+1,r,LL,RR,v); return inf; } int ans=inf; if(LL<=mid) ans=min(ans,squery(ls,l,mid,LL,RR,v)); if(RR>mid) ans=min(ans,squery(rs,mid+1,r,LL,RR,v)); return ans; } int nqry(int l,int r,int v) { int tmp1=pquery(1,1,n,1,l,v); int tmp2=squery(1,1,n,r,n,v); if(tmp1==-1) return tmp2==inf?-1:tmp2-r; return tmp2==inf?l-tmp1:min(l-tmp1,tmp2-r); } int qry(int l,int r,int f){return query(1,1,n,l,r,f);} void mdf(int d,int v){modify(1,1,n,d,v);} }t[2]; void modify(int x,int f,int v){t[f].mdf(x,v);} int query(int a,int b,int c,int d,int v) { int mht=abs(b-d)+abs(c-a); if(t[0].qry(a,a,1)>=v && t[1].qry(d,d,1)>=v) return mht; if(t[1].qry(b,b,1)>=v && t[0].qry(c,c,1)>=v) return mht; if(a>c) swap(a,c); if(b>d) swap(b,d); if(t[0].qry(a,c,0)>=v || t[1].qry(b,d,0)>=v) return mht; int ans=inf; if(t[0].qry(a,a,1)>=v && t[0].qry(c,c,1)>=v) { if(t[1].qry(b,d,1)>=v) return mht; int tmp=t[1].nqry(b,d,v); if(~tmp) ans=min(ans,tmp); } if(t[1].qry(b,b,1)>=v && t[1].qry(d,d,1)>=v) { if(t[0].qry(a,c,1)>=v) return mht; int tmp=t[0].nqry(a,c,v); if(~tmp) ans=min(ans,tmp); } return ans==inf?-1:mht+(ans<<1); } int main() { int n=read(),m=read(),q=read(),x; t[0].n=n; t[1].n=m; for(int i=1;i<=q;i++) { int opt=read(); if(opt==3) { int a=read(),b=read(),c=read(),d=read(),v=read(); printf("%d\n",a==c&&b==d?0:query(a,b,c,d,i-v)); } else x=read(),modify(x,opt-1,i); } return 0; }
四。
A.(ZROI963)
疯狂分类讨论即可...
大概长这样...最后一个也需要考虑字典序所以光荣的获得了90分的好成绩...
//Love and Freedom. #include<cstdio> #include<cmath> #include<algorithm> #include<cstring> #define ll long long #define inf 20021225 #define N 500100 using namespace std; int read() { int s=0,t=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') t=-1; ch=getchar();} while(ch>='0' && ch<='9') s=s*10+ch-'0',ch=getchar(); return s*t; } char s1[N],s2[N]; int n1,n2; void sub1() { for(int i=1;i<=n1;i++) printf("%c",s1[i]==s2[i]?s1[i]:'?'); printf("\n"); } int pre[N],suf[N]; void sub2() { if(n1>n2) swap(n1,n2),swap(s1,s2); pre[0]=suf[n1+1]=0; int ans=0,pos=0; for(int i=1;i<=n1;i++) pre[i]=pre[i-1]+(s1[i]==s2[i] && s1[i]!='?'); for(int i=0;i<n1;i++) suf[n1-i]=suf[n1-i+1]+(s1[n1-i]==s2[n2-i] && s1[n1-i]!='?'); for(int i=0;i<=n1;i++) if(pre[i]+suf[i+1]>ans){ans=pre[i]+suf[i+1]; pos=i;} for(int i=1;i<=pos;i++) printf("%c",s1[i]==s2[i]?s1[i]:'?'); printf("*"); for(int i=pos+1;i<=n1;i++) printf("%c",s1[i]==s2[n2-n1+i]?s1[i]:'?'); printf("\n"); } char qwq[N]; void sub3() { memset(pre,0,sizeof(pre)); memset(suf,0,sizeof(suf)); if(n1>n2) swap(n1,n2),swap(s1,s2); int d=0,pos=n1+1; int c1=0,c2=0,k=0; for(int i=1;i<=n1;i++) c1+=s1[i]!='*'; for(int i=1;i<=n2;i++) c2+=s2[i]!='*'; c1=min(c1,c2); for(int i=n1;i;i--) if(s1[i]=='*' || s2[n2-n1+i]=='*') break; else if(s1[i]==s2[n2-n1+i]&&s1[i]!='?') d++,pos=i; int m=d; for(int i=1;i<=n1;i++) { if(s1[i]=='*'||s2[i]=='*') break; if(i>=pos&&s2[n2-n1+i]==s1[i]) d--; if(s1[i]==s2[i]&&s1[i]!='?') d++; if(d>m) m=d,k=i; } for(int i=1;i<=k;i++) printf("%c",s1[i]==s2[i]?s1[i]:'?'); printf("*"); if(k>=pos) { for(int i=n2-n1+k+1,j=k+1;j<=n1;i++,j++) printf("%c",s1[j]==s2[i]?s1[j]:'?'); } else { for(int i=1;i<=c1-k-n1+pos-1;i++) printf("?"); for(int i=n2-n1+pos,j=pos;j<=n1;i++,j++) printf("%c",s1[j]==s2[i]?s1[j]:'?'); } printf("\n"); } int main() { int T=read(); while(T--) { scanf("%s%s",s1+1,s2+1); int flag=0; n1=strlen(s1+1),n2=strlen(s2+1); for(int i=1;i<=n1;i++) flag|=(s1[i]=='*'); for(int i=1;i<=n2;i++) flag|=(s2[i]=='*'); if(flag) { sub3(); } else { if(n1==n2) sub1(); else sub2(); } } return 0; } /** ab*cd a*c */
B.(ZROI964)
二分以后需要和n无关的判断 发现柿子长这样(pi-pj+1)>=2*(i-j+1)
移项以后可以通过单调性维护
//Love and Freedom. #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #include<vector> #define ll long long #define inf 20021225 #define P 1000010 #define N 500010 using namespace std; int read() { int s=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();} while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar(); return f*s; } struct node{int i,p;}; vector<node> f[P]; bool cmp(node a,node b){return a.p-2*a.i<b.p-2*b.i;} int pri[N],cnt,a[N],lt[P]; bool np[P]; void prime(int n) { for(int i=2;i<=n;i++) { if(!np[i]) pri[++cnt]=i,lt[i]=i; for(int j=1;j<=cnt && i*pri[j]<=n;j++) { np[i*pri[j]]=1,lt[i*pri[j]]=pri[j]; if(i%pri[j]==0) break; } } } int mx[N],mp[N],n; pair<int,int> work(vector<node> t) { sort(t.begin(),t.end(),cmp); mx[0]=t[0].i; mp[0]=t[0].p; for(int i=1;i<t.size();i++) { if(t[i].i>mx[i-1]) mx[i]=t[i].i,mp[i]=t[i].p; else mx[i]=mx[i-1],mp[i]=mp[i-1]; } int l=0,ans=0,al=0; for(int i=0;i<t.size();i++) { while(l<t.size()-1 && t[l+1].p-2*t[l+1].i <= t[i].p-2*t[i].i+1) l++; int len=0;// printf("%d %d %d %d %d\n",i,l,t[i].i,t[i].p,mn[l]); if(t[l].p-2*t[l].i<=t[i].p-2*t[i].i+1) len=2*(mx[l]-t[i].i+1); //printf("%d %d\n",mp[l],len); if(len>ans) ans=len,al=max(1,mp[l]-len+1); else if(len==ans) al=min(al,max(1,mp[l]-len+1)); } //printf("%d %d\n",ans,al); return make_pair(ans,al); } int main() { prime(1e6); n=read(); for(int i=1;i<=n;i++) a[i]=read(); for(int i=1;i<=n;i++) while(a[i]!=1) { int g=lt[a[i]]; while(a[i]%g==0) a[i]/=g; f[g].push_back(node{f[g].size()+1,i}); } pair<int,int> fin=make_pair(0,0),tmp; for(int i=1;i<=cnt;i++) { if(f[pri[i]].size()<2) continue; tmp=work(f[pri[i]]);// printf("%d %d\n",tmp.first,tmp.second); if(tmp.first>fin.first) fin=tmp; else if(tmp.first==fin.first) fin.second=min(tmp.second,fin.second); } printf("%d %d\n",fin.second,max(0,min(n,fin.second+fin.first-1))); return 0; } /** 10 43 11 65 53 54 51 91 17 19 31 */
C.(ZROI965)
考虑y爆零阶段的x 和 以后的x 它们如果不存在有0的 那么他们的和应该=x
所以对于一个i有效的状态只有O(x)个 可以通过把状态压起来来做到O(1)转移
//Love and Freedom. #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #include<vector> #include<queue> #define ll long long #define inf 20021225 #define M 4010 #define N 2010 using namespace std; int read() { int s=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();} while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar(); return f*s; } struct node{int a,b,id;}v[N]; bool operator<(node a,node b){return a.a>b.a;} int n,x,y,pre[N],w,f[N][M*2],pp[N][M*2],pq[N][M*2]; bool kd[N][M*2]; queue<int> ze,nz; void decode(int s,int i,int &p,int &q) { if(s>=M){p=0; q=s-M;} else{p=s; q=max(0,x-pre[i]-p);} } int encode(int p,int q) { if(p) return p; else return q+M; } void upd(int i,int p,int q,int fp,int fq,int val,int t) { int s=encode(p,q); if(f[i][s]<val) { f[i][s]=val; pp[i][s]=fp; pq[i][s]=fq; kd[i][s]=t; } } void getper(int x,int p,int q) { if(!x) return; int s=encode(p,q); getper(x-1,pp[x][s],pq[x][s]); if(kd[x][s]) nz.push(v[x].id); else ze.push(v[x].id); } int main() { x=read(),n=read(); for(int i=1;i<=n;i++) v[i].a=read(); for(int i=1;i<=n;i++) v[i].b=read(),v[i].id=i; sort(v+1,v+n+1); memset(f,-48,sizeof(f)); for(int i=1;i<=n;i++) pre[i]=pre[i-1]+v[i].a; for(int i=0;i<=x;i++) f[0][encode(i,x-i)]=0; for(int i=1;i<=n;i++) for(int j=0;j<M*2;j++) if(f[i-1][j]>=0) { int p,q; decode(j,i-1,p,q); upd(i,max(p-v[i].a,0),q,p,q,f[i-1][j]+v[i].b+min(v[i].a-p,0),1); upd(i,p,max(q-v[i].a,0),p,q,f[i-1][j],0); } int ans=-1,pos=0; for(int i=0;i<M*2;i++) if(f[n][i]>ans) { int p,q; decode(i,n,p,q); if(!q) ans=f[n][i],pos=p; } getper(n,pos,0); printf("%d\n",ans); while(!ze.empty()) printf("%d ",ze.front()),ze.pop(); while(!nz.empty()) printf("%d ",nz.front()),nz.pop(); return 0; }
五。
A.(ZROI966)
我可能是一种奇特的推法...
考虑贪心匹配的话一定是尽量往前匹配...所以每个位置如果不匹配下一位的话是(c-1)种方案,否则是匹配,然后考虑容斥,选出i个位置(i<m)是匹配位,这样的话用总方案-算出来的就是答案了。
//Love and Freedom. #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #define ll long long #define inf 20021225 #define mdn 1000000007 using namespace std; int read() { int s=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();} while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar(); return f*s; } int ksm(int bs,int mi) { int ans=1; while(mi) { if(mi&1) ans=1ll*ans*bs%mdn; bs=1ll*bs*bs%mdn; mi>>=1; } return ans; } int main() { int n=read(),m=read(),c=read(); for(int i=1;i<=m;i++) int x=read(); int ans=ksm(c,n),fn=1,fm=1; ans=(ans-ksm(c-1,n)%mdn+mdn)%mdn; for(int i=1;i<m;i++) { fn=1ll*fn*(n-i+1)%mdn, fm=1ll*fm*i%mdn; ans=(ans-1ll*fn*ksm(fm,mdn-2)%mdn*ksm(c-1,n-i)%mdn+mdn)%mdn; } printf("%d\n",(ans+mdn)%mdn); return 0; }
B.(ZROI967)
神仙题...
发现是翻硬币游戏模型,打个表可以发现sg[i]=2^lowbit(i) 然后用双射f[i]=2^(lowbit(i)+1)-1来表示(这样依然显然可以保证sg异或的性质)
接下来我们只需要维护每一位有多少1就可以了,最后的答案就是sum的最高位1的个数(因为在r的最高位为1的话前面的一定可以选出区间来保证凑出任何最高位为1的数 可以感性理解一下也可以通过异或基来证明)
//Love and Freedom. #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #define ll long long #define inf 20021225 #define lowbit(x) ((x)&(-x)) #define N 1<<20 using namespace std; int read() { int s=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();} while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar(); return f*s; } int f[N],a[N],pre[20]; char ch[N]; int main() { int n=read(),sum=0; for(int i=1;i<=n;i++) f[i]=(lowbit(i)<<1)-1; scanf("%s",ch+1); reverse(ch+1,ch+n+1); for(int i=1;i<=n;i++) if(ch[i]=='1') { sum^=f[i]; a[i]=1; for(int j=0;j<20;j++) if(i>>j&1) pre[j]++; } int m=read(); while(m--) { int x=read(); x=n-x+1; a[x]^=1; sum^=f[x]; if(a[x]) { for(int j=0;j<20;j++) if(x>>j&1) pre[j]++; } else { for(int j=0;j<20;j++) if(x>>j&1) pre[j]--; } if(!sum) puts("0"); else { for(int j=19;~j;j--) if(sum>>j&1) { printf("%d\n",pre[j]); break; } } } return 0; }
C.(ZROI968)
我在学习了...别催了...《Re:从零开始的群论生活》
学完了(。
我有一个绝妙的解释,但这里放不下。(逃
一些小地方的理解:
首先奇质数可以直接做的原因是因为奇质数有原根,那么它相当于直接的循环群。
而2^a不好做的原因就是因为它没有原根,但是2^a的群可以分解成一个循环群和一个二阶群的直积。
具体证明详见《抽象代数》(逃
话说为什么我的跑的这么慢啊...
//Love and Freedom. #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #define ll long long #define inf 1000000001 #define mdn 1000000007 #define N 10000001 using namespace std; int read() { int s=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();} while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar(); return f*s; } int ksm(int bs,int mi) { int ans=1; while(mi) { if(mi&1) ans=1ll*ans*bs%mdn; bs=1ll*bs*bs%mdn; mi>>=1; } return ans; } int inv(int x){return ksm(x,mdn-2);} int S(int p,int l,int r,int k){return 1ll*(ksm(p,r+k)-ksm(p,l)+mdn)%mdn*inv(ksm(p,k)-1)%mdn;} int a[N],b[N],pri[N],cnt,mp[N]; bool np[N]; void sieve() { for(int i=2;i<N;i++) { if(!np[i]) pri[++cnt]=i,mp[i]=i; for(int j=1;j<=cnt&&i*pri[j]<N;j++) { np[pri[j]*i]=1; mp[pri[j]*i]=pri[j]; if(pri[j]%i==0) break; } } } int fac[51],top; int get(int x) { int ans=1; top=0; while(x!=1){fac[++top]=mp[x]; x/=mp[x];} for(int i=1,s=1;i<=top;i++,s++) if(i==top||fac[i]!=fac[i+1]) { while(s>b[fac[i]]) ans*=fac[i],s--; s=0; } return ans; } void upd(int &x,int y){x+=x+y>=mdn?y-mdn:y;} int solve_pri(int p,int k) { int ans=0; int l=(a[p]-1)%k+1,r=a[p],m=b[p]; //printf("%d %d %d\n",r,k,ans); if(r>k) upd(ans,S(p,l-m+k-1,r-m-1,k)); // a-ik-1>0 upd(ans,ksm(p,max(0,l-m-1))); // last return (1ll*get(p-1)*ans%mdn+1)%mdn; } int solve_2(int k) { if(!b[2]) return solve_pri(2,k); int ans=0; int l=(a[2]-1)%k+1,r=a[2],m=b[2]; if(r>k) upd(ans,S(2,l-m+k-2,r-m-2,k)); upd(ans,ksm(2,max(0,l-m-min(l,2)))); return (ans+1)%mdn; } int main() { sieve(); int n=read(); for(int i=1;i<=n;i++) { int x=read(),y=read(); a[x]=y; } int m=read(); for(int i=1;i<=m;i++) { int x=read(),y=read(); b[x]=y; } int k=1,ans=0; for(int i=1;i<=cnt&&k<inf;i++) for(int j=1;j<=b[pri[i]]&&k<inf;j++) k=min(1ll*inf,1ll*k*pri[i]); ans=a[2]?solve_2(k):1; //printf("%d\n",k); for(int i=2;i<=cnt;i++) if(a[pri[i]]) ans=1ll*ans*solve_pri(pri[i],k)%mdn; printf("%d\n",ans); return 0; }
六。
A.(ZROI969)
真·NOIPCSP T1
考虑两者作差就可以证明只要整除N-1就可以了 最后就是N-1的约数个数。
//Love and Freedom. #include<cstdio> #include<cmath> #include<algorithm> #include<cstring> #define ll long long #define inf 20021225 using namespace std; int read() { int s=0,t=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') t=-1; ch=getchar();} while(ch>='0' && ch<='9') s=s*10+ch-'0',ch=getchar(); return s*t; } int main() { int P=read(),ans=0; P--; for(int i=1;i<=P;i++) if(P%i==0) ans++; printf("%d\n",ans); return 0; }
B.(ZROI970)
按照套路gcd==i都可以转化为gcd|i来做,然后最后直接容斥
那么发现gcd|i直接通过统计因数计算贡献就可以了。
本来以为这个复杂度是NK后来写着写着发现md这就是n*d(n)= =
//Love and Freedom. #include<cstdio> #include<cmath> #include<algorithm> #include<cstring> #define ll long long #define inf 20021225 #define N 400010 #define mdn 998244353 #define rg register #define il inline #include<ctime> using namespace std; il int read() { int s=0,t=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') t=-1; ch=getchar();} while(ch>='0' && ch<='9') s=s*10+ch-'0',ch=getchar(); return s*t; } int a[N],d[N],n,m,k,fac[N],inv[N],f[N]; il int ksm(int bs,int mi) { int ans=1; while(mi) { if(mi&1) ans=1ll*ans*bs%mdn; bs=1ll*bs*bs%mdn; mi>>=1; } return ans; } il int C(int n,int m) { if(n<m) return 0; return 1ll*fac[n]*inv[m]%mdn*inv[n-m]%mdn; } //void upd(int &x,int y){x+=x+y>=mdn?y-mdn:y;} #define upd(x,y) (x+=x+y>=mdn?y-mdn:y) // 洪蛤吨是大毒瘤!!!卡我nsqrtn!!! // wdnmd 我再用upd我就是憨憨(卡常去世选手 int pri[N],cnt,mn[N]; bool np[N]; il void sieve(int n) { for(int i=2;i<=n;i++) { if(!np[i]) pri[++cnt]=i,mn[i]=i; for(int j=1;j<=cnt&&pri[j]*i<=n;j++) { np[i*pri[j]]=pri[j]; mn[i*pri[j]]=pri[j]; if(i%pri[j]==0) break; } } } int stk[30],top,tms[30]; il void dfs(int x,int v) { if(x>top){d[v]++; return;} int dv=v; for(int i=0;i<=tms[x];i++) dfs(x+1,dv),dv*=stk[x]; } il void takedown(int x) { top=0;// printf("%d\n",x); while(x!=1) { stk[++top]=mn[x]; tms[top]=0; int gg=mn[x]; while(x%gg==0) tms[top]++,x/=gg; } //printf("%d %d %d %d\n",tms[1],tms[2],stk[1],stk[2]); dfs(1,1); } int main() { //freopen("960.in","r",stdin); n=read(),m=read(),k=read(); //sieve(m); //printf("%lld ",clock()); for(rg int i=1;i<=n;i++) { a[i]=read(); d[a[i]]++; //takedown(a[i]); } for(rg int i=1;i<=m;i++) for(rg int j=i+i;j<=m;j+=i) d[i]+=d[j]; //printf("%lld ",clock()); //for(int i=1;i<=m;i++) printf("%d\n",d[i]); fac[0]=1; //int tot=0; //for(int i=1;i<=m;i++) tot+=d[i]; //printf("%d\n",tot); for(rg int i=1;i<=n;i++) fac[i]=1ll*fac[i-1]*i%mdn; inv[n]=ksm(fac[n],mdn-2); for(rg int i=n;i;i--) inv[i-1]=1ll*inv[i]*i%mdn; for(rg int i=1;i<=m;i++) { int ans=0,qwq=m/i,tmp=1; ans=ksm(qwq,d[i]); int qaq=0; int j; for(j=n-d[i];j<k-3;j+=4) { qaq=(qaq+1ll*tmp*C(d[i],j-n+d[i])%mdn)%mdn,tmp=1ll*tmp*(qwq-1)%mdn; qaq=(qaq+1ll*tmp*C(d[i],j-n+1+d[i])%mdn)%mdn,tmp=1ll*tmp*(qwq-1)%mdn; qaq=(qaq+1ll*tmp*C(d[i],j-n+2+d[i])%mdn)%mdn,tmp=1ll*tmp*(qwq-1)%mdn; qaq=(qaq+1ll*tmp*C(d[i],j-n+3+d[i])%mdn)%mdn,tmp=1ll*tmp*(qwq-1)%mdn; } for(;j<k;j++) upd(qaq,1ll*tmp*C(d[i],j-n+d[i])%mdn),tmp=1ll*tmp*(qwq-1)%mdn; ans=(ans-qaq+mdn)%mdn; f[i]=1ll*ans*ksm(qwq,n-d[i])%mdn; } //printf("%lld ",clock()); for(rg int i=m;i;i--) for(rg int j=i+i;j<=m;j+=i) f[i]=(f[i]-f[j]+mdn)%mdn; for(rg int i=1;i<=m;i++) printf("%d ",f[i]); return 0; }
C.(ZROI971)
考场上写50由于先写了20的暴力就差一点改完= =(不过主要的锅应该在T2卡常上 。
感觉这个题挺套路的= =
首先子序列计数一定是要尽量往前选 然后考虑到ai相同不能冲突所以就是从last[a[i]]~i-1转移过来 那么显然这个就是二维前缀和优化了
第二个套路没想到 有点菜
//Love and Freedom. #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #define ll long long #define inf 20021225 #define N 5001 #define mdn 998244353 using namespace std; int read() { int s=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();} while(ch>='0'&&ch<='9') s=(s<<3)+(s<<1)+ch-'0',ch=getchar(); return f*s; } int f[3][N][N],pr[2][N][N],pa[N],pb[N],a[N],b[N],n,m,sa[N],sb[N]; // 0 f 1 g 2 h int query(int s,int xl,int xr,int yl,int yr){return ((ll)pr[s][xr][yr]-(xl?pr[s][xl-1][yr]:0)-(yl?pr[s][xr][yl-1]:0)+((xl&&yl)?pr[s][xl-1][yl-1]:0)+mdn+mdn)%mdn;} void upd(int s,int x,int y){pr[s][x][y]=((ll)pr[s][x][y-1]+pr[s][x-1][y]+f[s][x][y]-pr[s][x-1][y-1]+mdn)%mdn;} void upd(int &x,int y){x+=x+y>=mdn?y-mdn:y;} int main() { n=read(),m=read(); for(int i=1;i<=n;i++) a[i]=read(),pa[i]=sa[a[i]],sa[a[i]]=i; for(int i=1;i<=m;i++) b[i]=read(),pb[i]=sb[b[i]],sb[b[i]]=i; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) f[0][i][j]=query(0,pa[i],i-1,j-1,j-1),upd(f[0][i][j],(j==1&&!pa[i])),upd(0,i,j); for(int i=1;i<=m;i++) for(int j=1;j<=m;j++) f[1][i][j]=query(1,pb[i],i-1,j-1,j-1),upd(f[1][i][j],(j==1&&!pb[i])),upd(1,i,j); int ans=0; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) upd(ans,1ll*f[0][i][j]*pr[1][m][min(m,j-1)]%mdn); memset(f[0],0,sizeof(f[0])); memset(f[1],0,sizeof(f[1])); memset(pr[0],0,sizeof(pr[0])); memset(pr[1],0,sizeof(pr[1])); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { if(a[i]==b[j]) f[0][i][j]=query(0,pa[i],i-1,pb[j],j-1),upd(f[0][i][j],(!pa[i]&&!pb[j])); if(a[i]>b[j]) f[2][i][j]=query(0,pa[i],i-1,pb[j],j-1),upd(f[2][i][j],(!pa[i]&&!pb[j])); upd(0,i,j); } reverse(a+1,a+n+1); reverse(b+1,b+m+1); memset(sa,0,sizeof(sa)); memset(sb,0,sizeof(sb)); for(int i=1;i<=n;i++) pa[i]=sa[a[i]],sa[a[i]]=i; for(int i=1;i<=m;i++) pb[i]=sb[b[i]],sb[b[i]]=i; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) f[1][i][j]=query(1,pa[i],i-1,pb[j],j-1),upd(f[1][i][j],(!pa[i]&&!pb[j])),upd(1,i,j); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) upd(ans,1ll*f[2][i][j]*(pr[1][n-i][m-j]+1)%mdn); printf("%d\n",ans); return 0; }
七。
A.(ZROI972)
一眼题(?
考虑到一些区间被扔到另一边不能有交的话那么就是说每一段被覆盖的次数不超过2。
连续的一段对应着两种划分,所以直接拿堆维护右端点就好了。
//Love and Freedom. #include<cstdio> #include<cmath> #include<algorithm> #include<cstring> #include<queue> #define ll long long #define inf 20021225 #define mdn 998244353 #define N 1001000 using namespace std; int read() { int s=0,t=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') t=-1; ch=getchar();} while(ch>='0' && ch<='9') s=s*10+ch-'0',ch=getchar(); return s*t; } struct seg{int l,r;}t[N]; bool operator<(seg a,seg b){return a.l==b.l?a.r<b.r:a.l<b.l;} priority_queue< int , vector<int> , greater <int> > q; int main() { int n=read(); for(int i=1;i<=n;i++) t[i].l=read(),t[i].r=read(); sort(t+1,t+n+1); int ans=1,tmp=0; for(int i=1;i<=n;i++) { while(!q.empty()&&q.top()<=t[i].l) q.pop(),tmp--; if(q.empty()) ans=(ans<<1)%mdn; q.push(t[i].r); tmp++; if(tmp>2){ans=0; break;} } printf("%d\n",ans); return 0; }
B.(ZROI973)
咳咳场上写了一堆锅还好拍出来修掉了。
考虑直接dp $f(x) = min(f(\lceil \frac{x}{k+1} \rceil) + ak + b)$
显然可以直接把b减去a这样的话统一k和k+1比较方便。
然后一个显然的转化我们要$min(a \sum k_i + mb)$ 其中 $\lceil \frac{n}{\prod k_i} \rceil = 1$
显然每次除以n^(1/x)是最优的,当然有些地方要调整+1
这个只有log级别,所以最后复杂度是O(Tlg^2n)?
//Love and Freedom. #include<cstdio> #include<cmath> #include<algorithm> #include<cstring> #define ll long long #define inf 20021225 using namespace std; int read() { int s=0,t=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') t=-1; ch=getchar();} while(ch>='0' && ch<='9') s=s*10+ch-'0',ch=getchar(); return s*t; } int n,a,b; bool check(ll bs,int mi) { ll ans=1; while(mi) { if(mi&1) { ans=ans*bs; if(ans>=n) return 1; } bs=bs*bs; if(bs>=n && mi>1) return 1; mi>>=1; } return 0; } int get(int l,int r,int mi) { int ans=0; while(l<=r) { int mid=l+r>>1; if(check(mid,mi)) r=mid-1,ans=mid; else l=mid+1; } return ans; } ll solve() { b=b-a; ll tmp=2,ans=b+1ll*a*n; int l=1,r=n; for(int i=1;tmp<=n*2;i++,tmp*=2) { r=get(l,r,i); ll pw=1;// printf("%d %d\n",r,i); for(int j=0;j<i;j++) pw*=r; if(pw==n) ans=min(ans,1ll*a*r*i+1ll*b*i); else { ll k=r*i; for(int j=0;j<i&&pw>=n;j++) ans=min(ans,1ll*b*i+1ll*a*k),k--,pw=pw/r*(r-1); } } return ans; } int main() { int T=read(); while(T--) { n=read(),b=read(),a=read(); if(n==1){puts("0"); continue;} printf("%lld\n",solve()); } return 0; }
C.(ZROI974)
有点神仙的题,首先可以想到转化,一定是欧式距离再加一段反复横跳。
但是可能反复横跳的位置不在矩形内,所以需要讨论。接着考虑把它写成斜率形式(具体看题解吧)
然后维护凸包/李超树就,我写的凸包形式,最后在凸包上二分找到答案就好了。(跑的奇慢无比= =)
//Love and Freedom. #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #define ll long long #define inf 20021225 #define N 100010 #define LG 18 #define bint __int128 using namespace std; int read() { int s=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();} while(ch>='0'&&ch<='9') s=(s<<3)+(s<<1)+ch-'0',ch=getchar(); return f*s; } struct ST { ll f[N][LG]; int lg[N]; void init(ll *a,int n) { for(int i=1;i<=n;i++) f[i][0]=a[i],lg[i]=(i==1)?0:lg[i>>1]+1; for(int i=1;i<LG;i++) for(int j=1;j+(1<<i)<=n;j++) f[j][i]=min(f[j][i-1],f[j+(1<<i-1)][i-1]); } ll query(int l,int r) { int k=lg[r-l+1]; return min(f[l][k],f[r-(1<<k)+1][k]); } }sx,sy; struct poi { ll x,y; poi(){} poi(ll _x,ll _y){x=_x,y=_y;} }; bool operator<(poi a,poi b){return a.x==b.x?a.y<b.y:a.x<b.x;} poi operator-(poi a,poi b){return poi(a.x-b.x,a.y-b.y);} bint cross(poi a,poi b){return (bint)a.x*b.y-(bint)a.y*b.x;} ll calc(poi a,ll x){return x*a.x+a.y;} #define vec vector<poi> #define ls x<<1 #define rs x<<1|1 #define pb push_back #define bp pop_back struct SGT { vec t[N<<2],tmp; int n; void pushup(int x,int l,int r,ll *a,ll *pre,int id) { tmp.clear(); for(int i=l;i<=r;i++) if(id) tmp.pb(poi(a[i],2ll*pre[i-1]-2ll*i*a[i])); else tmp.pb(poi(a[i],-2ll*pre[i]+2ll*i*a[i])); sort(tmp.begin(),tmp.end()); for(int i=0;i<tmp.size();i++) { while(t[x].size()>1 && cross(tmp[i]-t[x][t[x].size()-2],t[x][t[x].size()-1]-t[x][t[x].size()-2])>=0) t[x].bp(); t[x].pb(tmp[i]); } } void build(int x,int l,int r,ll *a,ll *pre,int id) { pushup(x,l,r,a,pre,id); if(l==r) return; int mid=l+r>>1; build(ls,l,mid,a,pre,id); build(rs,mid+1,r,a,pre,id); } ll get(vec v,ll x) { int l=0,r=v.size(); ll ans=1e18; r--; while(l<=r) { if(r-l<=5){for(int i=l;i<=r;i++) ans=min(ans,calc(v[i],x)); break;} int mid=l+r>>1; ll qwq=calc(v[mid],x),qaq=calc(v[mid+1],x); if (qwq<qaq) r=mid; else l=mid+1; } return ans; } ll query(int x,int l,int r,int LL,int RR,ll pos) { if(RR<LL) return 1e18; if(LL<=l&&RR>=r) return get(t[x],pos); ll ans=1e18; int mid=l+r>>1; if(LL<=mid) ans=min(ans,query(ls,l,mid,LL,RR,pos)); if(RR>mid) ans=min(ans,query(rs,mid+1,r,LL,RR,pos)); return ans; } }pa,pb,sa,sb; ll prea[N],preb[N],a[N],b[N],rema[N],remb[N]; int n,m,q; int main() { n=read(),m=read(),q=read(); for(int i=1;i<n;i++) a[i]=read(),prea[i]=prea[i-1]+a[i]; for(int i=1;i<m;i++) b[i]=read(),preb[i]=preb[i-1]+b[i]; sx.init(a,n); sy.init(b,m); pa.build(1,1,n,a,prea,1);pb.build(1,1,m,b,preb,1); sa.build(1,1,n,a,prea,0);sb.build(1,1,m,b,preb,0); while(q--) { int x1=read(),y1=read(),x2=read(),y2=read(); ll ans=1e18; if(x1>x2) swap(x1,x2); if(y1>y2) swap(y1,y2); if(abs(abs(x2-x1)-abs(y2-y1))<=1) ans=prea[x2-1]-prea[x1-1]+preb[y2-1]-preb[y1-1]; else if(x2-x1>y2-y1) { ll len=prea[x2-1]-prea[x1-1]+preb[y2-1]-preb[y1-1]; int t=((x2-x1)-(y2-y1))>>1; if(y1!=y2) ans=len+2ll*t*sy.query(y1,y2-1); ans=min(ans,pb.query(1,1,m,y2,min(m-1,t+y2),2*(t+y2))+(-2*preb[y2-1]+len)); ans=min(ans,sb.query(1,1,m,max(1,y1-t-1),y1-1,2*(t-y1+1))+(len+2*preb[y1-1])); } else { ll len=prea[x2-1]-prea[x1-1]+preb[y2-1]-preb[y1-1]; int t=((y2-y1)-(x2-x1))>>1; if(x1!=x2) ans=len+2ll*t*sx.query(x1,x2-1); ans=min(ans,pa.query(1,1,n,x2,min(n-1,t+x2),2*(t+x2))+(-2*prea[x2-1]+len)); ans=min(ans,sa.query(1,1,n,max(1,x1-t-1),x1-1,2*(t-x1+1))+(len+2*prea[x1-1])); } printf("%lld\n",ans); } return 0; }
八。
第一场掉分呜呜呜。
无脑莽T3不打暴力太亏了,以后还是好好暴力吧(哭
A.(ZROI975)
一眼题,欧拉公式V+F-E=2即可。也可以直接维护qwq
//Love and Freedom. #include<cstdio> #include<cmath> #include<algorithm> #include<cstring> #include<map> #define ll long long #define inf 20021225 #define pa pair<int,int> #define ppa pair<pa,pa> #define mp make_pair #define N 500100 #define fs first #define se second using namespace std; int read() { int s=0,t=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') t=-1; ch=getchar();} while(ch>='0' && ch<='9') s=s*10+ch-'0',ch=getchar(); return s*t; } map<pa,bool> poi; map<ppa,bool> edg; char ch[N]; int main() { scanf("%s",ch+1); pa pos=mp(0,0); poi[pos]=1; int n=strlen(ch+1); int ans=0; for(int i=1;i<=n;i++) { pa tmp=pos; if(ch[i]=='L') tmp.fs--; else if(ch[i]=='R') tmp.fs++; else if(ch[i]=='U') tmp.se++; else if(ch[i]=='D') tmp.se--; if(poi[tmp]&&!edg[mp(pos,tmp)]) ans++; poi[tmp]=1; edg[mp(pos,tmp)]=edg[mp(tmp,pos)]=1; pos=tmp; } printf("%d\n",ans+1); return 0; }
B.(ZROI976)
我的计数dp水平太菜了呜呜呜。
考虑f[i][s]其中s是压位表示这个串中以s的第i位结尾是否ok,然后预处理转移即可。
发现随机数据,所以有效状态不多,直接压进map就做完了= =
//Love and Freedom. #include<cmath> #include<cstdio> #include<cstring> #include<algorithm> #include<map> #define ll long long #define inf 20021225 #define N 101 #define mdn 1000000007 #define IT map<ll,int>::iterator using namespace std; int read() { int s=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();} while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar(); return f*s; } map<ll,int> f[N]; ll g[2][N][2]; char s[N],t[N]; int sn,tn; void upd(int &x,int y){x+=x+y>=mdn?y-mdn:y;} int main() { scanf("%s%s",s,t); sn=strlen(s),tn=strlen(t); for(int i=0;i<sn+tn;i++) for(int b=0;b<2;b++) { for(int j=0;j<=tn;j++) if(i-j>=0&&i-j<sn&&s[i-j]-'0'==b) g[0][i][b]|=1ll<<j; for(int j=0;j<tn;j++) if(t[j]-'0'==b) g[1][i][b]|=1ll<<j; } f[0][1]=1; for(int i=0;i<sn+tn;i++) { IT it=f[i].begin(); while(it!=f[i].end()) { ll st=(*it).first; int v=(*it).second; for(int b=0;b<2;b++) upd(f[i+1][(g[0][i][b]&st)|((g[1][i][b]&st)<<1)],v); it++; } } printf("%d\n",f[sn+tn][1ll<<tn]); return 0; }
C.(ZROI977)
无脑莽了1h(别问我中间干嘛去了,太累了拖肥去了。。)
做法很科学,就是多了一个鸽笼原理,如果有多于2n个点,所有点都可以了。
//Love and Freedom. #include<cstdio> #include<cmath> #include<algorithm> #include<cstring> #include<vector> #define ll long long #define inf 20021225 #define N 2100 #define mp make_pair #define fs first #define se second #define pa pair<int,int> #define pb push_back using namespace std; int read() { int s=0,t=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-') t=-1; ch=getchar();} while(ch>='0' && ch<='9') s=s*10+ch-'0',ch=getchar(); return s*t; } int tagh[N][N],tags[N][N],tagl[N][N],tagr[N][N]; int tagld[N][N],taglu[N][N],tagru[N][N],tagrd[N][N],s[N][N]; bool a[N][N],ok[N][N]; vector<pa> poi; int n; int main() { n=read(); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) { a[i][j]=read(); if(a[i][j]) poi.pb(mp(i,j)); } if(poi.size()>(n<<1)) { for(int i=1;i<=n;i++,puts("")) for(int j=1;j<=n;j++) printf("Y "); return 0; } for(int i=0;i<poi.size();i++) for(int j=i+1;j<poi.size();j++) { pa p1=min(poi[i],poi[j]),p2=max(poi[i],poi[j]); if(p1.fs==p2.fs) { int det=p2.se-p1.se,d=det>>1; if(det&1) continue; tags[1][p1.se+d]++; } else if(p1.se==p2.se) { int det=p2.fs-p1.fs,d=det>>1; if(det&1) continue; tagh[p1.fs+d][1]++; } else if(p1.se<p2.se) { int det=p2.se-p1.se+p2.fs-p1.fs; if(det&1) continue; int d=det>>1,w=p1.fs+p1.se+d; if(p1.fs-p2.fs==p1.se-p2.se) tagl[p1.fs][w-p1.fs]++,tagld[p2.fs][p1.se]=1,tagru[p1.fs][p2.se]=1; else { if(p2.se-p1.se>d) { int y=w-p1.fs,_y=w-p2.fs-1; tagl[p1.fs][y]++; tagl[p2.fs+1][_y]--; tags[1][y]++; tags[p1.fs+1][y]--; tags[p2.fs][_y+1]++; } else { int x=w-p1.se,_x=w-p2.se-1; tagl[_x+1][p2.se]++; tagl[x][p1.se]--; tagh[x][1]++; tagh[x][p1.se+1]--; tagh[_x+1][p2.se]++; } } } else { int det=p1.se-p2.se+p2.fs-p1.fs; if(det&1) continue; int d=det>>1,w=p2.fs-p2.se-d; if(p1.fs+p1.se==p2.fs+p2.se) tagr[p1.fs][p1.fs-w]++,taglu[p1.fs][p2.se]=1,tagrd[p2.fs][p1.se]=1; else { if(p1.se-p2.se>d) { int y=p1.fs-w,_y=p2.fs+1-w; tagr[p1.fs][y]++; tagr[p2.fs+1][_y]--; tags[1][y]++; tags[p1.fs+1][y]--; tags[p2.fs][_y-1]++; } else { int x=p2.se+w,_x=p1.se+1+w; tagr[x][p2.se]++; tagr[_x-1][p1.se]--; tagh[x][1]++; tagh[x][p2.se+1]--; tagh[_x-1][p1.se]++; } } } } for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) tagrd[i][j]|=tagrd[i-1][j]|tagrd[i][j-1], ok[i][j]|=tagrd[i][j]; for(int i=1;i<=n;i++) for(int j=n;j;j--) tagld[i][j]|=tagld[i-1][j]|tagld[i][j+1], ok[i][j]|=tagld[i][j]; for(int i=n;i;i--) for(int j=1;j<=n;j++) tagru[i][j]|=tagru[i+1][j]|tagru[i][j-1], ok[i][j]|=tagru[i][j]; for(int i=n;i;i--) for(int j=n;j;j--) taglu[i][j]|=taglu[i+1][j]|taglu[i][j+1], ok[i][j]|=taglu[i][j]; for(int i=1;i<=n;i++) for(int j=1,tmp=0;j<=n;j++) tmp+=tagh[i][j],ok[i][j]|=(bool)(tmp); for(int j=1;j<=n;j++) for(int i=1,tmp=0;i<=n;i++) tmp+=tags[i][j],ok[i][j]|=(bool)(tmp); for(int i=1;i<=n;i++) for(int j=n;j;j--) s[i][j]=s[i-1][j+1]+tagl[i][j],ok[i][j]|=s[i][j]; memset(s,0,sizeof(s)); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) s[i][j]=s[i-1][j-1]+tagr[i][j],ok[i][j]|=s[i][j]; memset(s,0,sizeof(s)); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) s[i][j]=s[i-1][j]+tags[i][j],ok[i][j]|=s[i][j]; memset(s,0,sizeof(s)); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) s[i][j]=s[i][j-1]+tagh[i][j],ok[i][j]|=s[i][j]; for(int i=1;i<=n;i++,puts("")) for(int j=1;j<=n;j++) printf("%c ",(ok[i][j])?'Y':'N'); return 0; }