博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
XVIII Open Cup named after E.V. Pankratiev. Grand Prix of Khamovniki
阅读量:7086 次
发布时间:2019-06-28

本文共 14096 字,大约阅读时间需要 46 分钟。

A. Ability Draft

记忆化搜索。

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;void fre() { }#define MS(x, y) memset(x, y, sizeof(x))#define ls o<<1#define rs o<<1|1typedef long long LL;typedef unsigned long long UL;typedef unsigned int UI;template
inline void gmax(T1 &a, T2 b) { if (b > a)a = b; }template
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
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 11 2 2 125 327 21 22 1 1 2 2 144 8 8 926 7(ans = 2)2 11 3 4 2 2 4 3 161 4 4 8 9 11514 11 10 8 5【题意】【分析】【时间复杂度&&优化】*/

  

B. Short Random Problem

积分DP。

 

C. Block, Stock and Two Smoking Galaxy Notes

枚举领导者$S$,它需要满足度数至少为$\frac{n}{2}$。

枚举完领导后,将和$S$认识和不认识的分成两组二分图匹配即可。

时间复杂度$O(m^2)$。

#include
#include
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]]
=n/2-5)solve(i); puts("No");}/*5 41 22 33 44 5*/

  

D. Lunch Queue

用一棵平衡树$S$维护整个队列,再对每个队伍用一棵平衡树$T_i$维护。

那么对于新来的一个人,先在$S$中找出满足距离限制最紧的那个点$x$,再在$T_{c_i}$中查找$x$之后最靠前的点作为插队位置。

瓶颈在于最后一步比较两个人在$S$中的前后关系,将$S$用替罪羊树维护,同时维护动态标号即可$O(1)$比较。

时间复杂度$O(n\log n)$。

#include
#include
#include
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][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
>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

$ans=\lfloor\frac{n}{11}\rfloor+\lfloor\frac{n}{111}\rfloor+\lfloor\frac{n}{1111}\rfloor+...$。

令$N=9n$,则$ans=\lfloor\frac{N}{99}\rfloor+\lfloor\frac{N}{999}\rfloor+\lfloor\frac{N}{9999}\rfloor+...$。

对于整数部分FFT计算答案,对于小数部分用实数近似计算即可。

时间复杂度$O(|n|\log |n|)$。

 

F. Shuffle

对于置换中的每个循环,通过KMP求出所有可能的匹配位置,它们必然是一个等差数列。

那么对于所有循环分别列同余方程,然后扩展欧几里得求解即可。

时间复杂度$O(n)$。

#include
#include
#include
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();}/*aaababbbbbbabbbbabaabbabbababbbabbbbbabaaabbaabbaaaababa*/

  

G. Piecewise Linearity

按题意反解出每个函数即可,精度可以取模控制,不过使用__float128也可以通过全部数据。

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;void fre() { }#define MS(x, y) memset(x, y, sizeof(x))#define ls o<<1#define rs o<<1|1typedef long long LL;typedef unsigned long long UL;typedef unsigned int UI;template
inline void gmax(T1 &a, T2 b) { if (b > a)a = b; }template
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
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 21 02 13-3 -1-1 -11 14 13-3 1-2 00 11 1【题意】【分析】【时间复杂度&&优化】*/

  

H. Sketch

贪心将原序列复制下来,然后构造递减数列,注意特判原序列非法的情况。

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;void fre() { }#define MS(x, y) memset(x, y, sizeof(x))#define ls o<<1#define rs o<<1|1typedef long long LL;typedef unsigned long long UL;typedef unsigned int UI;template
inline void gmax(T1 &a, T2 b) { if (b > a)a = b; }template
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
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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;void fre() { }#define MS(x, y) memset(x, y, sizeof(x))#define ls o<<1#define rs o<<1|1typedef long long LL;typedef unsigned long long UL;typedef unsigned int UI;template
inline void gmax(T1 &a, T2 b) { if (b > a)a = b; }template
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
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
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
' && b[i] >= y){ rst[i] --; } } } } } return 0;}/*【trick&&吐槽】【题意】【分析】【时间复杂度&&优化】*/

  

J. Stairways

设$pre[i]=\max(a[1..i])$,则对于任意一个前缀$i$来说,两个子序列必有一个的最大值为$pre[i]$,设$f[i][j]$表示考虑前缀$i$,除了$pre[i]$之外另一个子序列最大值为$j$时的最小代价,考虑如何从$f[i-1][]$转移到$f[i][]$。

若$a[i]=pre[i]$,那么显然将它归到较大一侧最优,故$f[i][j]=f[i-1][j]+a[i]$,只需要把答案加上$a[i]$即可。

否则$a[i]<pre[i]$,那么假设它不作为最大值,则对于$[a[i],pre[i]]$的$j$,有$f[i][j]=f[i-1][j]+j$,对于$[0,a[i])$的$j$,有$f[i][j]=f[i-1][j]+pre[i]$。

而如果它作为最大值,则有$f[i][a[i]]=\min(f[i-1][0..a[i]])+a[i]$。

故需要一个数据结构维护$f[j]$,支持区间加上一个数,区间每个位置$j$加上$j$,以及区间查询最小值,单点修改。

将$f$分块,每块维护凸壳和标记即可,因为查询横坐标递增,故不断将凸壳的队首弹出即可均摊$O(\sqrt{n})$每次查询。

时间复杂度$O(n\sqrt{n})$。

#include
#include
using namespace std;typedef long long ll;const int N=100010,K=330;const ll inf=1000000000000010LL;int n,m,lim,i,a[N],b[N],pre[N],pos[N],st[K],en[K],tx[K],q[N],head[K],tail[K];ll base,f[N],tb[K];inline void up(ll&a,ll b){a>b?(a=b):0;}inline double slope(int x,int y){return 1.0*(f[x]-f[y])/(b[y]-b[x]);}inline void build(int x){ int&h=head[x],&t=tail[x],l=st[x],r=en[x]; h=l,t=h-1; for(int i=r;i>=l;q[++t]=i--)while(h
slope(q[t],i))t--;}inline void addx(int l){ int L=pos[l]; for(int i=l;i<=en[L];i++)f[i]+=b[i]; for(int i=pos[m];i>L;i--)tx[i]++;}inline void addf(int r,int p){ int R=pos[r]; for(int i=st[R];i<=r;i++)f[i]+=p; build(R); for(int i=0;i
cal(q[h+1],o))h++; return cal(q[h],o);}inline ll query(int r){ ll ret=inf; int R=pos[r]; for(int i=st[R];i<=r;i++)up(ret,f[i]+1LL*tx[R]*b[i]+tb[R]); for(int i=0;i

  

K. Hiding a Tree

将所有可以修改编号的点的编号随机设置,然后再挑选一个影响答案的点修正异或值。

若当前方案不合法,则在有解的情况下是小概率事件,多轮随机即可。

注意生成随机编号的时候要避免与之前编号相同,因为根据生日悖论,在$10^9$内取$10^5$个随机数中有重复元素的概率超过$50\%$,会导致这一轮随机作废。

#include
#include
#include
#include
#include
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
T;inline int ask(){ while(1){ int x=rand()%1000000000+1; if(x
1000000000)return; c[i]=b[i]; } sort(c+1,c+n+1); for(i=1;i

  

转载地址:http://uigml.baihongyu.com/

你可能感兴趣的文章
android学习笔记之十一数据存储(Shared Preferences、SQLite)
查看>>
nodejs 同步异步异常处理封装
查看>>
数据库中的左连接(left join)和右连接(right join)区别
查看>>
Restful 与 SOAP
查看>>
bigdecimal 保留小数位
查看>>
springCloud Finchley升级记录
查看>>
持续交付峰会 Call For Papers
查看>>
Markdown 11种基本语法(转载)
查看>>
一步一步带你安装史上最难安装的 vim 插件 —— YouCompleteMe
查看>>
cocoapods找不到第三方库Unable to find a pod with name, author, summary, or descriptionmatching...
查看>>
js dom元素获取节点
查看>>
Erlang if、case、guard和函数
查看>>
nfs的安装和配置
查看>>
SQL注入漏洞全接触--进阶篇
查看>>
android的Menu使用
查看>>
linux系统安装之后需要做的是事情
查看>>
Android利用ZXing进行二维码开发
查看>>
React-native项目中使用redux
查看>>
Swift中的反射
查看>>
Java NIO使用及原理分析 (四)
查看>>