XVIII Open Cup named after E.V. Pankratiev. Grand Prix of Khamovniki
A. Ability Draft
記憶化搜索。
#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 = 1e5 + 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, S, g; int f[100][1<<10]; int p[100]; int s[100][10]; int sm, bg; int smp, bgp; int smk[100]; int bgk[100]; int cal(int o, int sta) { if(o > g)return 0; if(~f[o][sta])return f[o][sta]; int x = p[o]; int y = p[o + 1]; f[o][sta] = -2e9; int canbig = ((sta >> x & 1) == 0); int cansml = S - (s[o - 1][x] - (canbig == 0)); //canbig == 0 means we picked ulti int sgn = (x / n == y / n) ? 1 : -1; if(canbig) { int add = bgk[++bgp]; f[o][sta] = max(sgn * cal(o + 1, sta | 1 << x) + add, f[o][sta]); --bgp; } if(cansml) { int add = smk[++smp]; f[o][sta] = max(sgn * cal(o + 1, sta) + add, f[o][sta]); --smp; } return f[o][sta]; } int main() { while(~scanf("%d%d",&n, &S)) { m = n * 2; g = m * (S + 1); for(int i = 1; i <= g; ++i) { scanf("%d", &p[i]); --p[i]; for(int j = 0; j < m; ++j) { s[i][j] = s[i - 1][j] + (j == p[i]); } } scanf("%d", &sm); for(int i = 1; i <= sm; ++i)scanf("%d", &smk[i]); sort(smk + 1, smk + sm + 1); reverse(smk + 1, smk + sm + 1); scanf("%d", &bg); for(int i = 1; i <= bg; ++i)scanf("%d", &bgk[i]); sort(bgk + 1, bgk + bg + 1); reverse(bgk + 1, bgk + bg + 1); smp = bgp = 0; MS(f, -1); printf("%d\n", cal(1, 0)*((p[1]/n==0)?1:-1)); } return 0; } /* 【trick&&吐槽】 1 1 1 2 2 1 2 5 3 2 7 2 1 2 2 1 1 2 2 1 4 4 8 8 9 2 6 7 (ans = 2) 2 1 1 3 4 2 2 4 3 1 6 1 4 4 8 9 11 5 14 11 10 8 5 【題意】 【分析】 【時間復雜度&&優化】 */
B. Short Random Problem
留坑。
C. Block, Stock and Two Smoking Galaxy Notes
枚舉領導者$S$,它需要滿足度數至少為$\frac{n}{2}$。
枚舉完領導後,將和$S$認識和不認識的分成兩組二分圖匹配即可。
時間復雜度$O(m^2)$。
#include<cstdio> #include<cstdlib> using namespace std; const int N=1010,M=10010; int n,m,i,e[M][2],g[N],v[M],nxt[M],ed,f[N],b[N],T,right[N],deg[N]; inline void add(int x,int y){v[++ed]=y;nxt[ed]=g[x];g[x]=ed;} bool find(int x){ for(int i=g[x];i;i=nxt[i])if(b[v[i]]<T){ b[v[i]]=T; if(!f[v[i]]||find(f[v[i]]))return f[v[i]]=x,1; } return 0; } void solve(int S){ int i; for(i=1;i<=n;i++)right[i]=g[i]=0; ed=0; for(i=1;i<=m;i++){ if(e[i][0]==S)right[e[i][1]]=1; if(e[i][1]==S)right[e[i][0]]=1; } for(i=1;i<=m;i++){ if(e[i][0]!=S&&!right[e[i][0]]&&right[e[i][1]])add(e[i][0],e[i][1]); if(e[i][1]!=S&&!right[e[i][1]]&&right[e[i][0]])add(e[i][1],e[i][0]); } for(i=1;i<=n;i++)f[i]=b[i]=0; T=0; for(i=1;i<=n;i++){ if(right[i]||i==S)continue; T++; if(!find(i))return; } puts("Yes"); int cnt=0; for(i=1;i<=n;i++)if(right[i])cnt++; printf("%d %d\n",S,cnt); for(i=1;i<=n;i++)if(right[i]){ printf("%d ",i); if(!f[i])f[i]=-1; printf("%d\n",f[i]); } exit(0); } int main(){ scanf("%d%d",&n,&m); for(i=1;i<=m;i++)scanf("%d%d",&e[i][0],&e[i][1]),deg[e[i][0]]++,deg[e[i][1]]++; for(i=1;i<=n;i++)if(deg[i]>=n/2-5)solve(i); puts("No"); } /* 5 4 1 2 2 3 3 4 4 5 */
D. Lunch Queue
用一棵平衡樹$S$維護整個隊列,再對每個隊伍用一棵平衡樹$T_i$維護。
那麽對於新來的一個人,先在$S$中找出滿足距離限制最緊的那個點$x$,再在$T_{c_i}$中查找$x$之後最靠前的點作為插隊位置。
瓶頸在於最後一步比較兩個人在$S$中的前後關系,將$S$用替罪羊樹維護,同時維護動態標號即可$O(1)$比較。
時間復雜度$O(n\log n)$。
#include<cstdio> #include<cmath> #include<algorithm> using namespace std; const int N=400010; typedef unsigned long long ll; const ll inf=1ULL<<63; const double A=0.8; ll tl[N],tr[N],tm[N]; int size[N],son[N][2],f[N],tot,root; int id[N],cnt; int n,i,a[N]; void dfs(int x){ if(son[x][0])dfs(son[x][0]); id[++cnt]=x; if(son[x][1])dfs(son[x][1]); } int build(int fa,int l,int r,ll a,ll b){ int mid=(l+r)>>1,x=id[mid]; f[x]=fa;son[x][0]=son[x][1]=0;size[x]=1;tl[x]=a;tr[x]=b;tm[x]=(a+b)>>1; if(l==r)return x; if(l<mid)size[x]+=size[son[x][0]=build(x,l,mid-1,a,tm[x])]; if(r>mid)size[x]+=size[son[x][1]=build(x,mid+1,r,tm[x],b)]; return x; } inline int kth(int k){ if(k<1)return 0; int x=root,rank; while(1){ rank=size[son[x][0]]+1; if(k==rank)return x; if(k<rank)x=son[x][0];else k-=rank,x=son[x][1]; } } inline int rebuild(int x){ cnt=0; dfs(x); return build(f[x],1,cnt,tl[x],tr[x]); } inline void fix(int x){ int deep=1,z=x; size[x]++; while(f[z])size[z=f[z]]++,deep++; if(deep<log(tot)/log(1/A))return; while((double)size[son[x][0]]<A*size[x]&&(double)size[son[x][1]]<A*size[x])x=f[x]; if(!x)return; if(x==root){root=rebuild(x);return;} int y=f[x],b=son[y][1]==x,now=rebuild(x); son[y][b]=now; } inline void ins(int A,int B,int x){ if(!root){ root=size[1]=1; tr[1]=inf; tm[1]=inf>>1; return; } while(1){ if(!son[A][B]){ son[A][B]=x; f[x]=A; if(!B){ tl[x]=tl[A]; tr[x]=tm[A]; }else{ tl[x]=tm[A]; tr[x]=tr[A]; } tm[x]=(tl[x]+tr[x])>>1; break; } A=son[A][B]; } fix(x); } inline void insd(int A,int B,int x){ if(!son[A][B]){ son[A][B]=x; f[x]=A; if(!B){ tl[x]=tl[A]; tr[x]=tm[A]; }else{ tl[x]=tm[A]; tr[x]=tr[A]; } tm[x]=(tl[x]+tr[x])>>1; fix(x); return; } ins(son[A][B],B^1,x); } void show(int x){ if(son[x][0])show(son[x][0]); printf("%d ",x); if(son[x][1])show(son[x][1]); } namespace DS{ int root[N],son[N][2],f[N]; inline void rotate(int x){ int y=f[x],w=son[y][1]==x; son[y][w]=son[x][w^1]; if(son[x][w^1])f[son[x][w^1]]=y; if(f[y]){ int z=f[y]; if(son[z][0]==y)son[z][0]=x; else if(son[z][1]==y)son[z][1]=x; } f[x]=f[y];son[x][w^1]=y;f[y]=x; } inline void splay(int x){ while(f[x]){ int y=f[x]; if(f[y]){if((son[f[y]][0]==y)^(son[y][0]==x))rotate(x);else rotate(y);} rotate(x); } } inline void insert(int&x,int y){ if(!x){x=y;return;} int z=x; while(1){ int b=tm[y]>tm[z]; if(son[z][b])z=son[z][b]; else{ son[z][b]=y; f[y]=z; break; } } splay(x=y); } int ask(int&o,int y){ int t=0,z=0,x=o; while(x){ z=x; if(tm[x]>tm[y]){ t=x; x=son[x][0]; }else{ x=son[x][1]; } } if(z)splay(o=z); return t; } } int main(){ scanf("%d",&n); for(i=1;i<=n;i++){ int x,dis; scanf("%d%d",&x,&dis); a[i]=x; if(i==1)ins(0,0,i); else{ int y=kth(i-dis-1); if(a[y]==x)insd(y,1,i); else{ int z=DS::ask(DS::root[x],y); if(z)insd(z,0,i); else ins(root,1,i); } } DS::insert(DS::root[x],i); } show(root); }
E. Oneness
留坑。
F. Shuffle
對於置換中的每個循環,通過KMP求出所有可能的匹配位置,它們必然是一個等差數列。
那麽對於所有循環分別列同余方程,然後擴展歐幾裏得求解即可。
時間復雜度$O(n)$。
#include<cstdio> #include<cstring> #include<cstdlib> using namespace std; typedef int ll; const int N=1000010; int n,i,j,p[N],vis[N],q[N]; char a[N],b[N],c[N],S[N],T[N]; int nxt[N],w[N*4]; int ans=1; int _a[N],_b[N],tot; namespace NT{ int flag=1; ll k=1,m,a,r,d,x,y; ll exgcd(ll a,ll b,ll&x,ll&y){ if(!b)return x=1,y=0,a; ll d=exgcd(b,a%b,x,y),t=x; return x=y,y=t-a/b*y,d; } inline void add(ll a,ll r){ if(r>=a)while(1); r%=a; if(!flag)return; d=exgcd(k,a,x,y); if((r-m)%d){flag=0;return;} x=(x*(r-m)/d+a/d)%(a/d),y=k/d*a,m=((x*k+m)%y)%y; if(m<0)m+=y; k=y; } void write(ll x){ if(x>=10)write(x/10); int t=x%10; printf("%d",t); } void show(){ if(flag)write(m); else puts("-1"); } } inline void solve(int len){ int i,j,k,cnt=0; for(i=1;i<=len;i++)T[i]=b[q[i]],S[i]=a[q[i]]; //printf("len=%d\n",len); //for(i=1;i<=len;i++)putchar(T[i]);puts(""); //for(i=1;i<=len;i++)putchar(S[i]);puts(""); for(nxt[1]=j=0,i=2;i<=len;nxt[i++]=j){ while(j&&S[j+1]!=S[i])j=nxt[j]; if(S[j+1]==S[i])j++; } //for(i=1;i<=len;i++)printf("nxt[%d]=%d\n",i,nxt[i]); for(i=1,j=0,k=1;i<=len*4;i++){ while(j&&S[j+1]!=T[k])j=nxt[j]; if(S[j+1]==T[k]){ j++; if(j==len){ w[++cnt]=i-len; j=nxt[j]; //printf("find %d\n",i); } } k++; if(k>len)k=1; } if(!cnt){ puts("-1"); exit(0); } if(cnt<2)while(1); for(i=2;i<=cnt;i++)if(w[i]-w[i-1]!=w[2]-w[1])while(1); //printf("per=%d occ=%d\n",w[2]-w[1],w[1]); NT::add(w[2]-w[1],w[1]); } int gcd(int a,int b){return b?gcd(b,a%b):a;} int main(){ scanf("%s%s",a+1,b+1); n=strlen(a+1); for(i=1;i<=n;i++){ int x; if(i<=n/2)x=i*2-1; else x=i*2-n; //printf("! %d %d\n",i,x); p[x]=i; } for(i=1;i<=n;i++)if(!vis[i]){ int cnt=0; for(j=i;!vis[j];j=p[j])vis[j]=1,q[++cnt]=j; //for(j=1;j<=cnt;j++)printf("%d ",q[j]);puts(""); solve(cnt); } NT::show(); } /* aaababbbbbbabbbbab aabbabbababbbabbbb babaaabbaa bbaaaababa */
G. Piecewise Linearity
按題意反解出每個函數即可,精度可以取模控制,不過使用__float128也可以通過全部數據。
#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 = 1e5 + 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; double xx[N], yy[N]; __float128 x[N], y[N], v[N], k[N]; const double EPS = 1e-9; __float128 fabs(__float128 x) { return x >= 0 ? x : -x; } bool same(__float128 x, __float128 y) { x -= y; return x <= EPS && x >= -EPS; } int main() { while(~scanf("%d",&n)) { for(int i = 1; i <= n + 1; ++i) { scanf("%lf%lf", &xx[i], &yy[i]); x[i] = xx[i]; y[i] = yy[i]; } __float128 sum = 0; __float128 yy = 0; for(int i = 1; i <= n; ++i) { k[i] = (y[i + 1] - y[i]) / (x[i + 1] - x[i]); if(i >= 2) { v[i] = (k[i] - k[i - 1]) / 2; sum += v[i]; yy += v[i] * fabs(x[1] - x[i]); } } if(!same(sum, k[n]) || !same(-sum, k[1]) || !same(yy, y[1])) { puts("No"); } else { puts("Yes"); } } return 0; } /* 【trick&&吐槽】 2 -1 2 1 0 2 1 3 -3 -1 -1 -1 1 1 4 1 3 -3 1 -2 0 0 1 1 1 【題意】 【分析】 【時間復雜度&&優化】 */
H. Sketch
貪心將原序列復制下來,然後構造遞減數列,註意特判原序列非法的情況。
#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 = 3e5 + 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, k; int a[N], b[N]; int main() { while(~scanf("%d%d%d", &k, &n, &m)){ a[0] = 1; for(int i = 1; i <= k; i ++) { scanf("%d", &a[i]); if(a[i] == -1){ a[i] = a[i - 1]; } } int flag = 1; for(int i = 2; i <= k; i ++){ if(a[i] < a[i - 1]) flag = 0; } if(k > n || !flag){ puts("-1"); continue; } int rst = n - k, o = 0; for(int i = 1; i <= k; i ++){ int x = m; while(rst > 0){ if(x == a[i]) break; b[++ o] = x --; rst --; } b[++ o] = a[i]; } /* if(rst){ int x = b[o] - 1; while(rst > 0){ if(x == 0) break; b[++ o] = x --; rst --; } } */ if(rst){ puts("-1"); } else{ for(int i = 1; i <= n; i ++) printf("%d ", b[i]); puts(""); } } return 0; } /* 【trick&&吐槽】 【題意】 【分析】 【時間復雜度&&優化】 */
I. $\leq$ or $\geq$
最優策略:將元素個數為$k$的棧的棧頂元素的權重設置為$2^{k-1}$,然後取加權中位數。
#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 = 1e5 + 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, k, x; int a[N], b[N]; char s[10]; int rst[N]; int w[20]; typedef pair<int,int>P; P q[N]; LL pre[N][2],suf[N][2]; int main() { w[1]=1; w[2]=2; w[3]=4; w[4]=8; w[5]=16; w[6]=32; w[7]=64; w[8]=128; w[9]=256; w[10]=512; while(~scanf("%d%d",&n, &k)) { for(int i = 1; i <= n; i ++) rst[i] = k; while(1){ int num = 0; int mx=0; for(int i=1;i<=n;i++)mx=max(mx,rst[i]); for(int i = 1; i <= n; i ++){ scanf("%d", &b[i]); if(b[i])q[++num]=P(b[i],w[rst[i]]); } sort(q + 1, q + num + 1); LL best=~0ULL>>1; int pos=1; for(int i=1;i<=num;i++){ pre[i][0]=pre[i-1][0]+1LL*q[i].first*q[i].second; pre[i][1]=pre[i-1][1]+q[i].second; } suf[num+1][0]=suf[num+1][1]=0; for(int i=num;i;i--){ suf[i][0]=suf[i+1][0]+1LL*q[i].first*q[i].second; suf[i][1]=suf[i+1][1]+q[i].second; } for(int i=1;i<=num;i++){ LL now=1LL*q[i].first*pre[i][1]-pre[i][0]; now+=suf[i][0]-1LL*q[i].first*suf[i][1]; if(now<best)best=now,pos=i; } int y=q[pos].first; printf("%d\n", y); fflush(stdout); scanf("%s", s); if(s[0] == ‘E‘){ return 0; } else{ for(int i = 1; i <= n; i ++){ if(s[0] == ‘<‘ && b[i] <= y){ rst[i] --; } else if(s[0] == ‘>‘ && b[i] >= y){ rst[i] --; } } } } } return 0; } /* 【trick&&吐槽】 【題意】 【分析】 【時間復雜度&&優化】 */
J. Stairways
留坑。
K. Hiding a Tree
將所有可以修改編號的點的編號隨機設置,然後再挑選一個影響答案的點修正異或值。
若當前方案不合法,則在有解的情況下是小概率事件,多輪隨機即可。
註意生成隨機編號的時候要避免與之前編號相同,因為根據生日悖論,在$10^9$內取$10^5$個隨機數中有重復元素的概率超過$50%$,會導致這一輪隨機作廢。
#include<cstdio> #include<ctime> #include<cstdlib> #include<algorithm> #include<set> using namespace std; const int N=100010; int n,i,a[N],e[N][2],b[N],c[N],q[N],m,v[N]; int used[N]; set<int>T; inline int ask(){ while(1){ int x=rand()%1000000000+1; if(x<N&&used[x])continue; if(T.find(x)!=T.end())continue; T.insert(x); return x; } } void solve(){ T.clear(); for(i=1;i<=n;i++){ if(a[i])b[i]=ask(); else b[i]=i; } int ret=n; for(i=1;i<=n;i++)if(v[i])ret^=b[i]; if(ret){ if(!m)return; int x=rand()%m+1; b[q[x]]^=ret; } for(i=1;i<=n;i++){ if(b[i]<1||b[i]>1000000000)return; c[i]=b[i]; } sort(c+1,c+n+1); for(i=1;i<n;i++)if(c[i]==c[i+1])return; printf("%d\n",n); for(i=1;i<n;i++)printf("%d %d\n",b[e[i][0]],b[e[i][1]]); exit(0); } int main(){ srand(time(NULL)); scanf("%d",&n); for(i=1;i<=n;i++){ scanf("%d",&a[i]); if(!a[i])used[i]=1; } for(i=1;i<n;i++){ scanf("%d%d",&e[i][0],&e[i][1]); v[e[i][0]]^=1; v[e[i][1]]^=1; } for(i=1;i<=n;i++)if(a[i]&&v[i])q[++m]=i; for(int _=100;_;_--)solve(); puts("-1"); }
XVIII Open Cup named after E.V. Pankratiev. Grand Prix of Khamovniki