UOJ Test Round #1

  T1:数字比大小的本质是按(长度,字典序)比大小。

  T2:首先发现单调性,二分答案,用堆模拟,$O(n\log^2 n)$。

    第二个log已经没有什么可优化的了,但是第一个可以做到线性。

    我们先将特殊题的p就当作是-1跑一边,设这个题的出现时间是tx,完成所需时间为sx,记录下每个题在[tx,T]上的出现时间。把所有题按优先级排序,可以发现如果找到了前i个满足出现时间之和为sx,那么这些时间区间正好可以被特殊题区间覆盖,找到这个i就确定了优先级,最后再模拟一遍即可。

    有个很容易忽视的问题,就是一定要保证得到的i是能让px尽量小的,这样才可以在总时间相同的情况下使特殊题最后做完,从而保证特殊题的完成时间就是T而不是T之前。

    当然按上述做法把优先级从低到高排序没有任何问题,但是如果从高到低排序最后相减就会出错了。

    为了这个问题调了一整个下午。

 #include<map>
#include<cstdio>
#include<queue>
#include<algorithm>
#define rep(i,l,r) for (int i=l; i<=r; i++)
typedef long long ll;
using namespace std; const int N=;
const ll inf=1000000000000000000ll;
int n,pp,pos,s[N],sm[N];
ll mx,S,T,Ed[N];
struct P{ int t,s,p,id; }a[N];
priority_queue<P>Q;
map<ll,bool>mp; bool operator <(const P &a,const P &b){ return a.p<b.p; }
bool cmp(const P &a,const P &b){ return (a.t==b.t) ? a.p>b.p : a.t<b.t; }
bool cmp1(int x,int y){ return a[x].p<a[y].p; }
void F(int id,ll l,ll r){
l=max(l,S); r=min(r,T);
if (r>l) sm[id]+=r-l;
} int main(){
scanf("%d",&n); mp[]=;
rep(i,,n){
scanf("%d%d%d",&a[i].t,&a[i].s,&a[i].p);
if (a[i].p==-) S=a[i].t,pp=a[i].s;
mp[a[i].p]=; a[i].id=i;
}
scanf("%lld",&T);
sort(a+,a+n+,cmp);
rep(i,,n) s[i]=i;
sort(s+,s+n+,cmp1);
for (int i=,j=; i<=n; i=j){
for (; a[j].t==a[i].t; j++) Q.push(a[j]);
ll tim=a[i].t,ed=a[j].t;
if (ed==) ed=inf;
while (!Q.empty()){
P x=Q.top(); Q.pop();
if (tim+x.s<=ed){
F(x.id,tim,tim+x.s); tim+=x.s; mx=max(mx,tim);
if (tim==ed) break;
}else { x.s-=ed-tim; F(x.id,tim,ed); mx=max(mx,ed); Q.push(x); break; }
}
}
ll res=,ans=;
while (mp.count(ans)) ans++;
rep(i,,n){
res+=sm[a[s[i]].id];
if (res==pp){
//printf("%lld %lld %d %lld\n",S,res,pp,T);
//printf("%d %d %d\n",a[s[i]].p,a[s[i+1]].p,a[s[i+2]].p);
ans=(~a[s[i]].p) ? a[s[i]].p : a[s[i-]].p;
while (mp.count(ans)) ans++;
break;
}
}
rep(i,,n) if (a[i].p==-) { a[i].p=ans; break; }
while (!Q.empty()) Q.pop();
for (int i=,j=; i<=n; i=j){
for (; a[j].t==a[i].t; j++) Q.push(a[j]);
ll tim=a[i].t,ed=a[j].t;
if (ed==) ed=inf;
while (!Q.empty()){
P x=Q.top(); Q.pop();
if (tim+x.s<=ed) Ed[x.id]=tim+x.s,tim+=x.s;
else { x.s-=ed-tim; Q.push(x); tim=ed; break; }
}
}
printf("%lld\n",ans);
rep(i,,n) printf("%lld ",Ed[i]);
return ;
}

UTR#2 T2

  T3:毒瘤类中心。

UOJ Easy Round #1

  T1:均值不等式或极大极小值定理直接出解。注意:

精度问题

有人可能会写:

ans_min = (long long)sqrt((double)g * l);

这样会被卡精度,因为double大概只有15位10进制有效数字。只能得到60分。

解决方法是:

ans_min = (long long)sqrt(l / g) * g;

当然有人可能直接long double保平安了……

  T2:Trie树上放些指针就好了。

  T3:实际上就是一个可持久化并查集,然后整个状态空间形成了一棵树,用树上倍增即可。

    考虑更简便的做法。

    首先如果只有Add操作,MST的形态是不会变的。

    如果没有Return操作。Delete直接用可撤销并查集即可(不要路径压缩,因为代价是均摊而不是严格)。

    有了Return之后的难点就在于前两种操作可以保证的均摊复杂度分析失效,我们就这一点处理:

      考虑一个Return操作,如果前面是Add,那这就是一个Delete 1。

      考虑一个Delete,可不可以做到,如果后面的操作不是Return,我们就“真删”,否则“假删”呢?

      思考如何“假删”,就是回答前k'条边形成的生成树的权值和,这个用维护一个数组即可。

UOJ Round #1

  T1:最小化sum{ a[i]%x+a[i]/x },变形成 sum{ a[i]-a[i]/x *(x-1) }。

    我们枚举x,问题就变成了对每个x求sum{ a[i]/x },这个设为z[x]。

    从a[]的角度思考,考虑每个a[i]对数组z的影响,由于a[i]/x的值只有$O(\sqrt{a_i})$个,总复杂度就可以做到根号级别了。

    从x的角度思考,枚举a[i]/x的所有值t,查询满足$tx\leq a_i < (t+1)x$的i的个数,然后给z[x]加上t和个数的积,查询开个桶用前缀和完成。

    这样根据调和级数就可以做到$O(X\log X)$了,感觉很巧妙。

    还有一种角度,就是每次加一个后缀和:http://uoj.ac/submission/241036

    启示:很多时候从a%b=a-(a/b)*b考虑很有用。以及根号优化和调和级数很有用。

  T2:又是一道好题,很像[PKUWC2018]随机算法。

    这两道题的共同点在于都是要求找到一个排列使解最优,并求出最优排列的个数,以及排列的每个元素是否会其作用根据排列而改变。

    首先发现a%b在b<=a时一定会其作用,在b>a时一定不会起作用。

    首先考虑如何求出最优解,由于上面的结论,我们可以将a[]排序然后DP,这样实际上枚举的是排列的哪些元素起了作用,显然这样可以保证包含了最优解。

    然后难点在于求出最优解的个数。

    我们用f[x]表示到当前为止值为x,只考虑a[i]<x的情况,的方案数。(显然a[i]<x的a[i]是不可能在之前出现的),枚举a[i],可以从f[x%a[i]]转移过来。

    这样,转移量就是大于a[i]而不大于x的那些元素的位置,乘上组合数即可。

    还有一种更为精妙的方法:http://uoj.ac/submission/243023

    延续起先的思路,将a[]排序,f[i][x]表示只考虑前i个中会起作用的元素并作用于原数之后,这个数会变成x的方案数,直接转移。

  T3:毒瘤仙人掌。

UOJ Round #2

  T1:构造题,不要求最优,只要m<=n即可。

    构造题还有一个套路,你可以人为固定最后变成什么样子,这样就好做多了。

    比如这题,我们规定最后是(((...((()))...)))这样的,前n个全是左括号。

    这个怎么实现呢,从左往右扫,扫到一个右括号时,找到它右边第一个左括号,然后把这一段翻转(由于这两个中间一定全是右括号,所以实际上相当于只交换了这两位)。

    拿一个指针指向要找的左括号,显然指针单调移动,故总复杂度为线性。

  T2:我不行啊。

  T3:一道非常好的题目,就是很难写。

    先总结一下主定理(Master Theorem):

      https://blog.csdn.net/lanchunhui/article/details/52451362

      https://www.cnblogs.com/SBSOI/p/5640663.html

      几个表示法:o小于,Θ等于,O小于等于,Ω大于等于。

      主要就是:如果f(n)是$n^{\log_{b}a}$的低阶,则结果就是$Θ(n^{\log_{b}a})$,如果同阶,则为$Θ(n^{\log_{b}a}\log n)$,如果更高阶,在一定情况下就是$Θ(f(n))$。

    先看随机树部分:满足树高期望为$O(\log n)$,所以每次开一个桶,统计的复杂度是子树高度的平方,总复杂度为$O(n \log^2 n)$

    然后是链的部分:根据这个式子可以方便求出不同链之间的答案(实际上记录一个后缀和也行):$(x_1+\cdots+x_k)^2=x_1^2+\cdots + x_k^2 +2\sum_{1\leq i < j\leq k}x_i x_j$

    然后是正解一:点分治。

      首先有一个巧妙的转化方式:对于与gcd有关的题目,可以先作莫比乌斯变换方便统计,最后再莫比乌斯反演回去。这部分的复杂度都是$O(n \log n)$的。
      找到树的中心c,考虑点对(u,v),如果都在c的子树中那么可以直接统计。

      对于u在c子树内,v在c的祖先的其它子树内的情况,我们可以这样做:

        先找到c一直到根的所有祖先,然后求出它们的其它子树的深度数组,然后和c的子树合并计数。

        这样就有一个问题,每次可能会询问c的子树内的所有距离c为d的倍数的点的个数。我们发现对于相同的d,不同的序列只会有d个。那么我们可以在$d\leq \sqrt{H}$时用一个数组记录答案,大于时直接统计,因为单次询问复杂度已经不会超过$O(\sqrt{H})$了。

      根据主定理,每层处理复杂度已经超过了$O(n^{\log_b a})$,所以总复杂度就是$f(n)=O(n\sqrt{n})$。

    正解二:启发式合并。

      同样是分块,对于$d\leq \sqrt{n}$的部分直接统计,大于的部分用vector记录答案并启发式合并,最后反演回去即可。

    总结:1.认真分析复杂度。2.Mobius反演的思想。 3.树上启发式合并和线段树合并。 4.分块与记忆化的思想。

 #include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define rep(i,l,r) for (int i=(l),_=(r); i<=_; i++)
#define For(i,x) for (int i=h[x],k; i; i=nxt[i])
typedef long long ll;
using namespace std; const int N=,M=,inf=;
int n,dd,nd,rt,z,sq,S,h[N],f[N],sz[N],d[N],dep[N];
int dp[M][M],ds[N],s1[N],fa[N],cnt[N],to[N<<],nxt[N<<];
bool vis[N];
ll ans[N],s2[N];
void add(int u,int v){ to[++nd]=v; nxt[nd]=h[u]; h[u]=nd; } void getrt(int x,int fa){
sz[x]=; f[x]=;
For(i,x) if ((k=to[i])!=fa && !vis[k])
getrt(k,x),sz[x]+=sz[k],f[x]=max(f[x],sz[k]);
f[x]=max(f[x],S-sz[x]);
if (f[x]<f[rt]) rt=x;
} void getdep(int x,int fa){
dep[x]=dep[fa]+;
if (dd<dep[x]) d[dd=dep[x]]=;
d[dep[x]]++;
For(i,x) if (!vis[k=to[i]] && k!=fa) getdep(k,x);
} ll ask(int x,int y){
ll res=;
if (x<=sq && dp[x][y]) return dp[x][y];
for (int i=y; i<=z; i+=x) res+=ds[i];//祖先的子树的深度数组(未做变换)
if (x<=sq) dp[x][y]=res;
return res;
} void work(int x){
dep[x]=; z=;
rep(i,,sz[x]+) s1[i]=s2[i]=ds[i]=;
For(i,x) if (!vis[k=to[i]] && k!=fa[x]){
dd=; getdep(k,x); z=max(z,dd);
rep(j,,dd) ds[j]+=d[j];
rep(j,,dd) for (int l=j+j; l<=dd; l+=j) d[j]+=d[l];//Mobius变换
rep(j,,dd) s1[j]+=d[j],s2[j]+=1ll*d[j]*d[j];
}
rep(i,,z) ans[i]+=(1ll*s1[i]*s1[i]-s2[i])>>;
sq=sqrt(z); memset(dp,,sizeof(dp[])*(sq+));
for (int i=x,y=; fa[i] && !vis[fa[i]]; i=fa[i],y++)//枚举祖先的其它子树
For(j,fa[i]) if (!vis[k=to[j]] && k!=i && k!=fa[fa[i]]){
dd=; dep[fa[i]]=; getdep(k,fa[i]);
rep(l,,dd) for (int p=l+l; p<=dd; p+=l) d[l]+=d[p];
rep(l,,dd) ans[l]+=1ll*d[l]*(ask(l,l-y%l)+(y%l==));//可能重心也是一个合法点
}
} void solve(int x){
vis[x]=;
For(i,x) if (sz[k=to[i]]>sz[x]) sz[k]=S-sz[x];
work(x);
For(i,x) if (!vis[k=to[i]]) S=sz[k],f[rt=]=inf,getrt(k,x),solve(rt);
} int main(){
freopen("gcd.in","r",stdin);
freopen("gcd.out","w",stdout);
scanf("%d",&n);
rep(i,,n) scanf("%d",&fa[i]),add(fa[i],i),add(i,fa[i]),cnt[dep[i]=dep[fa[i]]+]++;
for (int i=n-; i; i--) cnt[i]+=cnt[i+];
S=n; f[rt=]=inf; getrt(,); solve(rt);
for (int i=n-; i; i--) for (int j=i+i; j<n; j+=i) ans[i]-=ans[j];//Mobius反演
rep(i,,n-) printf("%lld\n",ans[i]+cnt[i]);//加上u==v的个数
return ;
}

解法一

 #include<cmath>
#include<cstdio>
#include<vector>
#include<algorithm>
#define rep(i,l,r) for (int i=l; i<=r; i++)
typedef long long ll;
using namespace std; const int N=;
int n,m,fa[N],dep[N],cnt[N],to[N],g[N],f[N];
ll ans[N];
vector<int>V[N]; int main(){
freopen("gcd.in","r",stdin);
freopen("gcd.out","w",stdout);
scanf("%d",&n); m=sqrt(n);
rep(i,,n) scanf("%d",&fa[i]),cnt[dep[i]=dep[fa[i]]+]++;
rep(i,,n) to[i]=i;
for (int i=n; i; i--) cnt[i]+=cnt[i+];
rep(d,,m){
for (int i=n; i>; i--){
f[i]++; g[to[i]]+=f[i];
ans[d]+=1ll*f[fa[i]]*g[i]; f[fa[i]]+=g[i];
}
rep(i,,n) to[i]=fa[to[i]],f[i]=g[i]=;
}
for (int i=n; i; i--){
V[i].push_back();
if (V[i].size()>V[fa[i]].size()) swap(V[i],V[fa[i]]);
int x=V[i].size(),y=V[fa[i]].size();
if (x>m) rep(d,m+,x){
int a=,b=;
for (int j=d; j<=x; j+=d) a+=V[i][x-j];
for (int j=d; j<=y; j+=d) b+=V[fa[i]][y-j];
ans[d]+=1ll*a*b;
}
rep(j,,x) V[fa[i]][y-j]+=V[i][x-j];
}
for (int i=n; i; i--) for (int j=i+i; j<=n; j+=i) ans[i]-=ans[j];
for (int i=; i<n; i++) printf("%lld\n",ans[i]+cnt[i]);
return ;
}

解法二

        

UOJ Rounds的更多相关文章

  1. PKUSC2018训练日程(4.18~5.30)

    (总计:共66题) 4.18~4.25:19题 4.26~5.2:17题 5.3~5.9: 6题 5.10~5.16: 6题 5.17~5.23: 9题 5.24~5.30: 9题 4.18 [BZO ...

  2. 虚拟机上装uoj

    前期准备: x64 ubuntu 镜像.vmware.ss账号 注意一定要有64位镜像! ss不是必须的,不过没有的话就等着下载一晚上吧... 首先先装好ubuntu,我装的是ubuntu-16.04 ...

  3. 【UOJ #35】后缀排序 后缀数组模板

    http://uoj.ac/problem/35 以前做后缀数组的题直接粘模板...现在重新写一下模板 注意用来基数排序的数组一定要开到N. #include<cstdio> #inclu ...

  4. 【UOJ #246】【UER #7】套路

    http://uoj.ac/contest/35/problem/246 神奇!我这辈子是想不出这样的算法了. 对区间长度分类讨论:题解很好的~ 我已经弱到爆了,看完题解后还想了一晚上. 题解中&qu ...

  5. 【UOJ #244】【UER #7】短路

    http://uoj.ac/contest/35/problem/244 对其他人来说好简单的一道题,我当时却不会做TWT 注定滚粗啊 题解很好的~ #include<cstdio> #i ...

  6. 【BZOJ 3051】【UOJ #57】【WC 2013】平面图

    http://www.lydsy.com/JudgeOnline/problem.php?id=3051 http://uoj.ac/problem/57 这道题需要平面图转对偶图,点定位,最小生成树 ...

  7. 【UOJ #13】【UER #1】跳蚤OS

    http://uoj.ac/problem/13 建立trie树,然后建立go指针, 和AC自动机里的fail指针差不多, 走到一个快捷方式就从go指针走. 注意在trie树上要保留字符'/',不能用 ...

  8. 【UOJ #14】【UER #1】DZY Loves Graph

    http://uoj.ac/problem/14 题解很好的~ 不带路径压缩的并查集能保留树的原本形态. 按秩合并并查集可以不用路径压缩,但是因为此题要删除,如果把深度当为秩的话不好更新秩的值,所以把 ...

  9. 【UOJ#228】基础数据结构练习题 线段树

    #228. 基础数据结构练习题 题目链接:http://uoj.ac/problem/228 Solution 这题由于有区间+操作,所以和花神还是不一样的. 花神那道题,我们可以考虑每个数最多开根几 ...

随机推荐

  1. 如何开始创建第一个基于Spring MVC的Controller

    万事开头难,良好的开端是成功的一半! 以下示例怎么开始创建我们的第一个Spring MVC控制器Controller 1.新建一个java类,命名为:MyFirstController,包含以下代码, ...

  2. 使用记事本创建Web服务(WebService)

    学习就要从最简单最直观的地方入手. 1)打开记事本,添加如下代码: <%@ WebService Language="C#" Class="myFirstWebSe ...

  3. oracle与mysql的group by语句

    之所以去纠那么细节的问题,是因为之前有过一个这样的场景: 有个同学,给了一条数据库的语句给我,问,为啥这样子的语句在oracle语句下执行不了. 1 select * from xx where xx ...

  4. Endnote 中文参考文献样式修改版

    http://blog.yuelong.info/post/endnote-gbt7714-2005.html 很多人不知道 EndNote 是自带中文参考文献引用样式的,即符合<文后参考文献著 ...

  5. 本地上jar命令

    1.上传到jd-release mvn deploy:deploy-file -DgroupId=com.jd.open.api -DartifactId=open-api-sdk -Dversion ...

  6. rsync安装使用详解

    rsync是类unix系统下的数据镜像备份工具,从软件的命名上就可以看出来了——remote sync.它的特性如下: 可以镜像保存整个目录树和文件系统. 可以很容易做到保持原来文件的权限.时间.软硬 ...

  7. PushState+Ajax 完美实现无刷新

    转载自:http://lazynight.me/1897.html 折腾一下PJAX,利用HTML5的新API,实现历史记录的完美导入. 不知道你用没用过Github,里边的目录跳转就是用html5的 ...

  8. BootStrap弹出框插件popover简单实例

    1.网上实例地址 http://www.runoob.com/bootstrap/bootstrap-popover-plugin.html 2.具体demo     $("#pieId&q ...

  9. 转:RBAC权限控制

    名词解释: RBAC:Role-Based Access Control,基于角色的访问控制   关键词: RBAC,Java Shiro,Spring Security,   一. RBAC 要解决 ...

  10. Idea IntelliJ远程调试教程

    总结 第一步:修改startup.sh 在倒第二行加上export JPDA_ADDRESS=8787 最后一行在start前面加上"   jpda   " 第二步:配置Idea, ...