UOJ Rounds
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:毒瘤类中心。
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'条边形成的生成树的权值和,这个用维护一个数组即可。
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:毒瘤仙人掌。
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的更多相关文章
- 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 ...
- 虚拟机上装uoj
前期准备: x64 ubuntu 镜像.vmware.ss账号 注意一定要有64位镜像! ss不是必须的,不过没有的话就等着下载一晚上吧... 首先先装好ubuntu,我装的是ubuntu-16.04 ...
- 【UOJ #35】后缀排序 后缀数组模板
http://uoj.ac/problem/35 以前做后缀数组的题直接粘模板...现在重新写一下模板 注意用来基数排序的数组一定要开到N. #include<cstdio> #inclu ...
- 【UOJ #246】【UER #7】套路
http://uoj.ac/contest/35/problem/246 神奇!我这辈子是想不出这样的算法了. 对区间长度分类讨论:题解很好的~ 我已经弱到爆了,看完题解后还想了一晚上. 题解中&qu ...
- 【UOJ #244】【UER #7】短路
http://uoj.ac/contest/35/problem/244 对其他人来说好简单的一道题,我当时却不会做TWT 注定滚粗啊 题解很好的~ #include<cstdio> #i ...
- 【BZOJ 3051】【UOJ #57】【WC 2013】平面图
http://www.lydsy.com/JudgeOnline/problem.php?id=3051 http://uoj.ac/problem/57 这道题需要平面图转对偶图,点定位,最小生成树 ...
- 【UOJ #13】【UER #1】跳蚤OS
http://uoj.ac/problem/13 建立trie树,然后建立go指针, 和AC自动机里的fail指针差不多, 走到一个快捷方式就从go指针走. 注意在trie树上要保留字符'/',不能用 ...
- 【UOJ #14】【UER #1】DZY Loves Graph
http://uoj.ac/problem/14 题解很好的~ 不带路径压缩的并查集能保留树的原本形态. 按秩合并并查集可以不用路径压缩,但是因为此题要删除,如果把深度当为秩的话不好更新秩的值,所以把 ...
- 【UOJ#228】基础数据结构练习题 线段树
#228. 基础数据结构练习题 题目链接:http://uoj.ac/problem/228 Solution 这题由于有区间+操作,所以和花神还是不一样的. 花神那道题,我们可以考虑每个数最多开根几 ...
随机推荐
- BZOJ1009: [HNOI2008]GT考试 矩阵快速幂+kmp+dp
这个题你发现打暴力的话可以记忆化搜素加剪枝,那么意味着可以递推,我们搜的话就是1010^9我们就往下匹配遇到匹配成功就return,那么我们可以想一下什么决定了状态,我们考虑kmp的过程,对于我们目前 ...
- RTL2832U+R820T电视棒windows下安装sdr# 以及搭建ADS-B使用VirtualRadar看飞机的教程
本文中提到的软件随后我会打包给出下载地址.这篇文章是我根据网上的教程和自己的经验修改的详细版本,为了方便入门新手.先来说说RTL2832U+R820T在windows下安装sdr#的方法.首先科普下s ...
- ZOJ3261:Connections in Galaxy War(逆向并查集)
Connections in Galaxy War Time Limit: 3 Seconds Memory Limit: 32768 KB 题目链接:http://acm.zju.edu. ...
- 安卓的progress
https://www.cnblogs.com/wolipengbo/archive/2013/10/23/3383667.html
- bzoj 5092 [Lydsy1711月赛]分割序列 贪心高维前缀和
[Lydsy1711月赛]分割序列 Time Limit: 5 Sec Memory Limit: 256 MBSubmit: 213 Solved: 97[Submit][Status][Dis ...
- powercmd注册码
推荐一个很方便的软件:powercmd 用户名:nzone 注册码:PCMDA-86128-PCMDA-70594 . 下载地址网上很多: http://soft.hao123.com/soft/a ...
- Topcoder SRM 608 div1 题解
Easy(300pts): 题目大意:有n个盒子,一共有S个苹果,每个盒子有多少个苹果不知道,但是知道每个盒子的苹果下限和上限.现在要至少选择X个苹果,问如果要保证无论如何都能获得至少X个苹果,至少需 ...
- vivo面试经验4(linux基本操作,最基本,必须得会!!)
操作linux通过xshell进行连接: 基本操作介绍 1.shutdown -h 关机 shutdown -r 重启 2.mkdir aaa 新建目录aaa rmdir aaa 删除目录aaa 3. ...
- 【uva11732-"strcmp()" Anyone?】Trie
http://acm.hust.edu.cn/vjudge/problem/28438 题意:给定n个字符串,问用strcmp函数比较这些字符串共用多少次比较. 题解: 插入一个‘#’作为字符串的结束 ...
- 我喜欢的4个VS扩展吧
原文发布时间为:2011-06-09 -- 来源于本人的百度文章 [由搬家工具导入]