XVIII Open Cup named after E.V. Pankratiev. Grand Prix of Siberia
1. GUI
按题意判断即可。
#include<stdio.h> #include<iostream> #include<string.h> #include<string> #include<ctype.h> #include<math.h> #include<set> #include<map> #include<vector> #include<queue> #include<bitset> #include<algorithm> #include<time.h> using namespace std; void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); } #define MS(x, y) memset(x, y, sizeof(x)) #define ls o<<1 #define rs o<<1|1 typedef long long LL; typedef unsigned long long UL; typedef unsigned int UI; template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b > a)a = b; } template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b < a)a = b; } const int N = 0, M = 0, Z = 1e9 + 7, inf = 0x3f3f3f3f; template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; } int casenum, casei; bool inclu(int L, int R, int l, int r) { return L <= l && r <= R; } int main() { scanf("%d", &casenum); for (casei = 1; casei <= casenum; ++casei) { int x1, x2, x3, x4; int y1, y2, y3, y4; scanf("%d%d%d%d",&x1,&y1,&x2,&y2); scanf("%d%d%d%d",&x3,&y3,&x4,&y4); if(inclu(x1,x2,x3,x4) && inclu(y1,y2,y3,y4)) { puts("B in A"); } else if(inclu(x3,x4,x1,x2) && inclu(y3,y4,y1,y2)) { puts("A in B"); } else if((x2 < x3 || x4 < x1) || (y2 < y3 || y4 < y1)) { puts("Separate"); } else puts("Intersect"); } return 0; } /* 【trick&&吐槽】 【题意】 【分析】 【时间复杂度&&优化】 */
2. Searching on the Cube
首先爬山找到一个极小点,然后不断前进找到另一个极小点,分析哪一个是答案即可。
#include<stdio.h> #include<iostream> #include<string.h> #include<string> #include<ctype.h> #include<math.h> #include<set> #include<map> #include<vector> #include<queue> #include<bitset> #include<algorithm> #include<time.h> using namespace std; #define MS(x, y) memset(x, y, sizeof(x)) #define ls o<<1 #define rs o<<1|1 typedef long long LL; typedef unsigned long long UL; typedef unsigned int UI; template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b > a)a = b; } template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b < a)a = b; } const int N = 0, M = 0, Z = 1e9 + 7, inf = 0x3f3f3f3f; template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; } int casenum, casei; int n; char rtn[100]; void fwd() { puts("forward"); fflush(stdout); scanf("%s", rtn); } void rgt() { puts("right"); fflush(stdout); } void lft() { puts("left"); fflush(stdout); } void dig() { puts("dig"); fflush(stdout); exit(0); } bool BEST() { fwd(); if(strcmp(rtn, "closer") == 0) { return 0; } lft(); lft(); fwd(); fwd(); if(strcmp(rtn, "closer") == 0) { return 0; } lft(); lft(); fwd(); lft(); fwd(); if(strcmp(rtn, "closer") == 0) { return 0; } lft(); lft(); fwd(); fwd(); if(strcmp(rtn, "closer") == 0) { return 0; } lft(); lft(); fwd(); return 1; } bool onlycheckBEST() { bool flag = 1; fwd(); if(strcmp(rtn, "closer") == 0) { flag = 0; } lft(); lft(); fwd(); lft(); lft(); if(!flag)return 0; lft(); fwd(); if(strcmp(rtn, "closer") == 0) { flag = 0; } lft(); lft(); fwd(); lft(); if(!flag)return 0; rgt(); fwd(); if(strcmp(rtn, "closer") == 0) { flag = 0; } lft(); lft(); fwd(); rgt(); if(!flag)return 0; lft(); lft(); fwd(); if(strcmp(rtn, "closer") == 0) { flag = 0; } lft(); lft(); fwd(); if(!flag)return 0; return 1; } int main() { while(!BEST()); int clo = 0; int far = 0; while(1) { fwd(); if(strcmp(rtn, "closer") == 0) { ++clo; } else if(strcmp(rtn, "farther") == 0) { ++far; } if(onlycheckBEST()) { if(clo >= far) { dig(); } else clo = far = 0; } } return 0; } /* 【trick&&吐槽】 【题意】 【分析】 【时间复杂度&&优化】 */
3. Mirrors
留坑。
4. Roads to cinematography
$1$和$n$的最优连法一定是$1$往下,$n$往左,中间必然存在一个点$i$满足$i$往左到$1$,$i+1$往下到$n$。
设$f[l][r]$表示$l$往下,$r$往左时,$[l,r]$的最优连法,枚举$i$转移即可。
时间复杂度$O(n^3)$。
#include<cstdio> #include<algorithm> using namespace std; const int N=505,inf=1000000000; int n,i,j,m,e[100010][4]; int f[N][N],g[N][N];bool v[N][N]; struct P{int x,y;}a[N]; inline void add(int x1,int y1,int x2,int y2){ if(x1<x2)swap(x1,x2),swap(y1,y2); if(y1<y2)swap(x1,x2),swap(y1,y2); if(x1==x2&&y1==y2)return; m++; e[m][0]=x1; e[m][1]=y1; e[m][2]=x2; e[m][3]=y2; } void dfs(int l,int r){ if(l+1>=r)return; if(v[l][r])return; v[l][r]=1; f[l][r]=inf; for(int i=l;i<r;i++){ //i left,i+1 down dfs(l,i); dfs(i+1,r); int t=f[l][i]+f[i+1][r]+a[i].x-a[l].x+a[i+1].y-a[r].y; if(t<f[l][r]){ f[l][r]=t; g[l][r]=i; } } } void go(int l,int r){ if(l+1>=r)return; int i=g[l][r]; go(l,i); go(i+1,r); add(a[l].x,a[i].y,a[i].x,a[i].y); add(a[i+1].x,a[r].y,a[i+1].x,a[i+1].y); } int main(){ scanf("%d",&n); for(i=1;i<=n;i++)scanf("%d%d",&a[i].x,&a[i].y); dfs(1,n); f[1][n]+=a[1].x+a[1].y+a[n].x-a[1].x; add(0,0,a[1].x,0); add(a[1].x,0,a[1].x,a[1].y); add(a[1].x,a[n].y,a[n].x,a[n].y); go(1,n); printf("%d %d\n",m,f[1][n]); for(i=1;i<=m;i++){for(j=0;j<4;j++)printf("%d ",e[i][j]);puts("");} }
5. Geometric solver
按题意模拟。
#include<stdio.h> #include<iostream> #include<string.h> #include<string> #include<ctype.h> #include<math.h> #include<set> #include<map> #include<vector> #include<queue> #include<bitset> #include<algorithm> #include<time.h> using namespace std; void fre() { } #define MS(x, y) memset(x, y, sizeof(x)) #define ls o<<1 #define rs o<<1|1 typedef long long LL; typedef unsigned long long UL; typedef unsigned int UI; template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b > a)a = b; } template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b < a)a = b; } const int N = 2e5 + 10, M = 0, Z = 1e9 + 7, inf = 0x3f3f3f3f; template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; } int casenum, casei; int n, m; int id[N]; int root[N]; int col[N]; bool edge[N]; vector<int>ans; bool FLAG; struct A { int y; int tp; int id; }; vector< A >a[N]; void dfs(int x) { for(auto it : a[x]) { int y = it.y; int tp = it.tp; int id = it.id; if(col[y] == -1) { col[y] = col[x] ^ tp; edge[id] = 1; dfs(y); } else { if(!edge[id]) { edge[id] = 1; ans.push_back(it.id); //printf("!edge[id]: %d\n", id); } if(col[y] != (col[x] ^ tp)) { FLAG = 0; } } } } int main() { while(~scanf("%d%d",&n, &m)) { MS(edge, 0); MS(root, -1); MS(id, -1); ans.clear(); for(int i = 1; i <= n; ++i) { a[i].clear(); } FLAG = 1; for(int i = 1; i <= m; ++i) { char op[100]; scanf("%s", op); if(op[0] == 'v') { int x; scanf("%d", &x); if(root[x] == 0) { ans.push_back(i); //printf("root[x] == 0: %d\n", i); } else if(root[x] == 1) { FLAG = 0; } else { root[x] = 0; id[x] = i; } } else if(op[0] == 'h') { int x; scanf("%d", &x); if(root[x] == 1) { ans.push_back(i); //printf("root[x] == 1: %d\n", i); } else if(root[x] == 0) { FLAG = 0; } else { root[x] = 1; id[x] = i; } } else { int x, y; scanf("%d%d", &x, &y); if(op[1] == 'e')//be horizontal { a[x].push_back({y, 1, i}); a[y].push_back({x, 1, i}); } else { a[x].push_back({y, 0, i}); a[y].push_back({x, 0, i}); } } } MS(col, -1); int useful = 0; for(int i = 1; i <= n; ++i) { if(root[i] != -1) { if(col[i] == -1) { col[i] = root[i]; dfs(i); ++useful; } else { if(col[i] != root[i]) { FLAG = 0; } ans.push_back(id[i]); //printf("same_con: %d\n", id[i]); } } } for(int i = 1; i <= n; ++i) { if(col[i] == -1) { col[i] = 0; dfs(i); } } if(!FLAG) { puts("inconsistent"); } else { puts("consistent"); printf("%d\n", ans.size()); int sz = ans.size(); for(int i = 0; i < sz; ++i) { printf("%d%c", ans[i], i == sz - 1 ? '\n' : ' '); } } } return 0; } /* 【trick&&吐槽】 【题意】 【分析】 【时间复杂度&&优化】 */
6. Monsters
设$f[i]=\max(f[i-1],s[i])$,则$ans=\prod\min(m[i],f[i])$。
线段树维护每个区间$[l,r]$内只考虑区间$[l,r]$时以下信息:
- $fl,fr$:$f[l]$和$f[r]$的值。
- $mul$:$\prod\min(m[i],f[i])$。
- $rmul$:将右儿子用左儿子的$f$修正后右儿子的贡献。
对于$mul$,有$mul=左儿子的mul\times ask(右儿子,左儿子的f[r])$。
其中$ask(x,pre)$表示考虑$x$的子树,之前$f$为$pre$时的乘积:
- 若$fl\geq pre$,则无需修正,直接返回$mul$即可。
- 若$x$为叶子,则暴力处理即可。
- 若左儿子的$fr\geq pre$,则右儿子只会被左儿子修正,返回$ask(左儿子,pre)\times rmul$即可。
- 否则左儿子将全部被修正为$pre$,故返回$ask(右儿子,pre)\times 左儿子全部修正为pre$的贡献即可。而贡献可以在每个节点套上一棵Treap来$O(\log n)$计算。
总时间复杂度$O(n\log^3n)$。
#include<cstdio> #include<algorithm> #include<cstdlib> using namespace std; const int N=100010,M=262150,P=1000000007; int fl[M],fr[M],mul[M],rmul[M]; int n,_,i,s[N],m[N]; int CNT,MUL,ni; struct node{ int val,cnt,sum,v,mul,p; node*l,*r; node(){ val=cnt=sum=p=0; v=mul=1; l=r=NULL; } void up(){ sum=cnt+l->sum+r->sum; mul=1LL*v*l->mul%P*r->mul%P; } }*blank=new(node),*T[M],pool[5000000],*cur=pool; inline void Rotl(node*&x){node*y=x->r;x->r=y->l;x->up();y->l=x;y->up();x=y;} inline void Rotr(node*&x){node*y=x->l;x->l=y->r;x->up();y->r=x;y->up();x=y;} void Ins(node*&x,int p){ if(x==blank){ x=cur++; x->val=p; x->l=x->r=blank; x->cnt=x->sum=1; x->v=x->mul=p; x->p=rand(); return; } x->sum++; x->mul=1LL*x->mul*p%P; if(p==x->val){ x->cnt++; x->v=1LL*x->v*p%P; return; } if(p<x->val){ Ins(x->l,p); if(x->l->p>x->p)Rotr(x); }else{ Ins(x->r,p); if(x->r->p>x->p)Rotl(x); } } void Del(node*&x,int p,int q){ x->sum--; x->mul=1LL*x->mul*q%P; if(p==x->val){ x->cnt--; x->v=1LL*x->v*q%P; return; } if(p<x->val){ Del(x->l,p,q); if(x->l->p>x->p)Rotr(x); }else{ Del(x->r,p,q); if(x->r->p>x->p)Rotl(x); } } void Ask(node*&x,int p){//<p if(x==blank)return; if(p==x->val){ CNT+=x->l->sum; MUL=1LL*MUL*x->l->mul%P; return; } if(p<x->val){ Ask(x->l,p); return; } Ask(x->r,p); CNT+=x->l->sum+x->cnt; MUL=1LL*MUL*x->l->mul%P*x->v%P; } inline int po(int a,int b){ int t=1; for(;b;b>>=1,a=1LL*a*a%P)if(b&1)t=1LL*t*a%P; return t; } void build(int x,int a,int b){ if(a<b){ int mid=(a+b)>>1; build(x<<1,a,mid); build(x<<1|1,mid+1,b); } int i; fl[x]=fr[x]=s[a]; mul[x]=1; T[x]=blank; for(i=a;i<=b;i++){ fr[x]=max(fr[x],s[i]); mul[x]=1LL*mul[x]*min(fr[x],m[i])%P; Ins(T[x],m[i]); } if(a<b)rmul[x]=1LL*mul[x]*po(mul[x<<1],P-2)%P; } inline int query(int x,int a,int b,int pre){ CNT=0; MUL=1; Ask(T[x],pre); return 1LL*MUL*po(pre,b-a+1-CNT)%P; //int t=1; //for(int i=a;i<=b;i++)t=1LL*t*min(m[i],pre)%P; //return t; } int ask(int x,int a,int b,int pre){ if(fl[x]>=pre)return mul[x]; if(a==b)return min(m[a],pre); int mid=(a+b)>>1; if(fr[x<<1]>=pre)return 1LL*ask(x<<1,a,mid,pre)*rmul[x]%P; return 1LL*query(x<<1,a,mid,pre)*ask(x<<1|1,mid+1,b,pre)%P; } inline void up(int x,int a,int b){ fl[x]=fl[x<<1]; fr[x]=max(fr[x<<1],fr[x<<1|1]); int mid=(a+b)>>1; mul[x]=1LL*mul[x<<1]*ask(x<<1|1,mid+1,b,fr[x<<1])%P; rmul[x]=1LL*mul[x]*po(mul[x<<1],P-2)%P; } void change(int x,int a,int b,int c){ if(a==b){ fl[x]=fr[x]=s[a]; mul[x]=min(s[a],m[a]); return; } int mid=(a+b)>>1; if(c<=mid)change(x<<1,a,mid,c);else change(x<<1|1,mid+1,b,c); up(x,a,b); } void changem(int x,int a,int b,int c,int A,int B){ Ins(T[x],B); Del(T[x],A,ni); if(a==b)return; int mid=(a+b)>>1; if(c<=mid)changem(x<<1,a,mid,c,A,B);else changem(x<<1|1,mid+1,b,c,A,B); } int main(){ blank->l=blank->r=blank; scanf("%d%d%d",&n,&_,&s[1]); for(i=1;i<=n;i++)scanf("%d",&m[i]); for(i=2;i<=n+1;i++)scanf("%d",&s[i]); build(1,1,n); //shutaoshu printf("%d\n",mul[1]); while(_--){ int op,x,y; scanf("%d%d%d",&op,&x,&y); if(op==0){ ni=po(m[x],P-2); changem(1,1,n,x,m[x],y); m[x]=y; change(1,1,n,x); }else{ x++; if(x<=n){ s[x]=y; change(1,1,n,x); } } printf("%d\n",mul[1]); } }
7. Regular expressions
注意到$((a(c|(g|t)))*)$可以匹配所有字符串,所以答案不超过$16$。
爆搜出所有短正则表达式后,建立DFA,最小化DFA判断即可。
8. WSO-2017 soccer team
假设所有人都选择$2a[i]$,那么需要选出$k=\frac{r-l+1}{3}$个人增加$2(d[i]-a[i])$,还需要再选出$k$个人增加$d[i]-a[i]$,故可持久化线段树维护区间$k$小值即可。
时间复杂度$O(n\log n)$。
#include<cstdio> #include<algorithm> using namespace std; typedef long long ll; const int N=129000,M=N*21; int n,m,i,x,y; ll a[N],d[N],sa[N],c[N]; int T[N],tot,l[M],r[M],cnt[M];ll sum[M]; int ins(int x,int a,int b,int c,ll p){ int y=++tot; cnt[y]=cnt[x]+1; sum[y]=sum[x]+p; if(a==b)return y; int mid=(a+b)>>1; if(c<=mid)l[y]=ins(l[x],a,mid,c,p),r[y]=r[x]; else l[y]=l[x],r[y]=ins(r[x],mid+1,b,c,p); return y; } inline ll kth(int x,int y,int k){ if(!k)return 0; int a=1,b=n,mid;ll ans=0; while(a<b){ mid=(a+b)>>1; int t=cnt[l[x]]-cnt[l[y]]; if(t>=k){ b=mid; x=l[x]; y=l[y]; }else{ k-=t; ans+=sum[l[x]]-sum[l[y]]; a=mid+1; x=r[x]; y=r[y]; } } if(k)ans+=(sum[x]-sum[y])/(cnt[x]-cnt[y])*k; return ans; } inline ll ask(int x,int y,int l,int r){return kth(x,y,r)-kth(x,y,l-1);} int main(){ scanf("%d",&n); for(i=1;i<=n;i++)scanf("%lld",&a[i]),sa[i]=sa[i-1]+a[i]; for(i=1;i<=n;i++)scanf("%lld",&d[i]); for(i=1;i<=n;i++)c[i]=d[i]-a[i]; sort(c+1,c+n+1); for(i=1;i<=n;i++)T[i]=ins(T[i-1],1,n,lower_bound(c+1,c+n+1,d[i]-a[i])-c,d[i]-a[i]); scanf("%d",&m); while(m--){ scanf("%d%d",&x,&y); ll ans=(sa[y]-sa[x-1])*2; int len=(y-x+1)/3; ans+=ask(T[y],T[x-1],len*2+1,len*3)*2; ans+=ask(T[y],T[x-1],len+1,len*2); printf("%lld.%lld\n",ans/2,ans%2*5); } }
9. Primitive divisors
枚举所有的质数$p$,满足$q^n\bmod p=1$的$p$不会很多,对于每个将$\varphi(p)$分解质因数找到最小循环节判断即可。
#include<cstdio> const int N=10000000; int n,q,tot,i,j,p[N],ans[N],fin,v[N]; inline int po(int a,int b,int P){ int t=1; for(;b;b>>=1,a=1LL*a*a%P)if(b&1)t=1LL*t*a%P; return t; } inline bool check(int x){ if(x<=n)return 0; if(po(q,n,x)!=1)return 0; int per=x-1; int t=per; while(t>1){ int o=v[t]; if(po(q,per/o,x)==1)per/=o; if(n>per)return 0; t/=o; } return 1; } int main(){ scanf("%d%d",&q,&n); for(i=2;i<N;i++){ if(!v[i]){ p[tot++]=i; v[i]=i; } for(j=0;j<tot&&1LL*i*p[j]<N;j++){ v[i*p[j]]=p[j]; if(i%p[j]==0)break; } } for(i=0;i<tot;i++)if(check(p[i]))ans[++fin]=p[i]; printf("%d\n",fin); for(i=1;i<=fin;i++)printf("%d ",ans[i]); }
10. Tickets
按题意模拟。
#include<stdio.h> #include<iostream> #include<string.h> #include<string> #include<ctype.h> #include<math.h> #include<set> #include<map> #include<vector> #include<queue> #include<bitset> #include<algorithm> #include<time.h> using namespace std; void fre() { } #define MS(x, y) memset(x, y, sizeof(x)) #define ls o<<1 #define rs o<<1|1 typedef long long LL; typedef unsigned long long UL; typedef unsigned int UI; template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b > a)a = b; } template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b < a)a = b; } const int N = 1e6 + 10, M = 0, Z = 1e9 + 7, inf = 0x3f3f3f3f; template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; } int casenum, casei; int n; char a[N], b[N]; int main() { while(~scanf("%s%s",a,b)) { int n = strlen(a); int A = 0, B = 0; for(int i = 0; i < n; ++i) { if(a[i] < b[i])++A; else if(a[i] > b[i])++B; } printf("%d\n%d\n",A,B); } return 0; } /* 【trick&&吐槽】 【题意】 【分析】 【时间复杂度&&优化】 */
11. Logarithm smoothing
二分答案$mid$,将$f(x)=c\ln x$的图像往上下分别平移$mid$。
从$(a,f(a)+mid)$开始往右沿着切线走,判断走$n$步是否可以到达$b$之后的位置即可。
#include<cstdio> #include<cmath> #include<algorithm> using namespace std; typedef long double ld; const int T=50; int n;ld C,A,B,OFFSET; void read(ld&x){ double t; scanf("%lf",&t); x=t; } void write(ld x){ printf("%.15f",(double)x); } bool down(ld A,ld B,int n); bool up(ld A,ld B,int n){ //printf("up %d\n",n); if(A>=B)return 1; if(n<1)return 0; ld y=C*log(A)+OFFSET; //f'(x)=C/x ld L=A,R=B+10; for(int i=0;i<T;i++){ ld mid=(L+R)/2; if((C*log(mid)-OFFSET-y)/(mid-A)<C/mid)L=mid;else R=mid; } return down(L,B,n); } bool down(ld A,ld B,int n){ //printf("down %d\n",n); if(A>=B)return 1; if(n<1)return 0; ld y=C*log(A)-OFFSET; //f'(x)=C/x ld k=C/A; ld L=A,R=B+10; for(int i=0;i<T;i++){ ld mid=(L+R)/2; if(y+(mid-A)*k<C*log(mid)+OFFSET)L=mid;else R=mid; } return up(L,B,n-1); } bool check(ld _mid){ OFFSET=_mid; if(down(A,B,n))return 1; if(up(A,B,n))return 1; return 0; } int main(){ int Case; scanf("%d",&Case); while(Case--){ scanf("%d",&n); read(C); read(A); read(B); ld l=0,r=C*(log(B)-log(A))/2; for(int i=0;i<100;i++){ ld mid=(l+r)/2; if(check(mid))r=mid;else l=mid; } write(l); puts(""); } }
12. Outer space signals
KMP求出两个串最左和最右出现位置然后判断即可。
时间复杂度$O(n)$。
#include<cstdio> #include<cstring> const int N=1000010; int Case,na,nb,nc,i,bl,br,cl,cr;char a[N],b[N],c[N]; int nxt[N]; void solve(int n,char*a,int m,char*b,int&l,int&r){ int i,j; for(nxt[1]=j=0,i=2;i<=n;nxt[i++]=j){ while(j&&a[j+1]!=a[i])j=nxt[j]; if(a[j+1]==a[i])j++; } l=r=0; for(i=1,j=0;i<=m;i++){ while(j&&a[j+1]!=b[i])j=nxt[j]; if(a[j+1]==b[i])j++; if(j==n){ if(!l)l=i; r=i; j=nxt[j]; } } } bool check(){ if(!bl||!cl)return 0; if(cr-nc+1>bl)return 1; if(br-nb+1>cl)return 1; return 0; } int main(){ scanf("%d",&Case); while(Case--){ scanf("%s%s%s",a+1,b+1,c+1); na=strlen(a+1); nb=strlen(b+1); nc=strlen(c+1); solve(nb,b,na,a,bl,br); solve(nc,c,na,a,cl,cr); puts(check()?"YES":"NO"); } }