UNR #1 题解
A. 争夺圣杯
还是想说一下,这题是原题啊...想做的人可以戳codechef上的MTMXSUM(懒得贴链接了,套了个壳,不过正常人应该都能看得出来)
显然异或输出没什么奇怪的性质...
考虑一个元素a[x]在哪些区间中会成为最大值,我们可以用单调栈找出前面比这个元素大的第一个元素a[l],右边大的第一个元素a[r]。
考虑这个元素对每一长度的贡献,设p=x-l,q=r-x,那么对于区间[s,t],只有当l<s<=x,x<=t<r,只有这pq个区间最大值为a[x]。
那么考虑这些区间的长度,不妨设p<=q,那么可以根据区间长度跟p、q的关系来统计答案。
当1<=len<=p时,显然共有len个区间(因为x肯定要在区间内)。
当p<len<=q时,共有p个区间(因为左端点可以是l+1~x)
当q<len<=p+q-1时,共有p+q-len个([x-p+1,x-p+len]...[x+q-len,x+q-1])
好像是个区间加等差数列,随便前缀和维护一下。
具体地,例如[p,q]加1...q-p+1,这种事情我们用两个数组s1,s2来维护,s1[p...q]+=1,s2[p...q]-=p-1,这个前缀和搞搞。最后我们只要统计s1*i+s2就行了。
实现时l和r需要一边开一边闭(一边大于,一边大于等于),然后用单调栈维护即可。
- #include <bits/stdc++.h>
- using namespace std;
- typedef long long ll;
- ll MOD=;
- #define SZ 2333333
- int n; ll hh[SZ];
- #define gc getchar()
- int gi_()
- {
- int s,c;
- while(c=gc,c<||c>);
- s=c-;
- while(c=gc,c>=&&c<=) s=s*+c-;
- return s;
- }
- #define gi gi_()
- int L[SZ],R[SZ],ss[SZ],sn=;
- ll q1[SZ],q2[SZ];
- void add(ll& a,ll b)
- {
- a+=b; a%=MOD;
- if(a<) a+=MOD;
- }
- int main()
- {
- n=gi;
- for(int i=;i<=n;i++) hh[i]=gi;
- for(int i=;i<=n;i++)
- {
- while(sn&&hh[ss[sn]]<hh[i]) --sn;
- if(sn) L[i]=ss[sn]; ss[++sn]=i;
- }
- sn=;
- for(int i=n;i>=;i--)
- {
- while(sn&&hh[ss[sn]]<=hh[i]) --sn;
- if(sn) R[i]=ss[sn]; else R[i]=n+;
- ss[++sn]=i;
- }
- for(int i=;i<=n;i++) hh[i]%=MOD;
- for(int i=;i<=n;i++)
- {
- int l=L[i],r=R[i];
- //1,min(i-l,r-i),max(i-l,r-i),(r-l)
- add(q1[],hh[i]); add(q1[min(i-l,r-i)],-hh[i]);
- add(q2[min(i-l,r-i)],min(i-l,r-i)*(ll)hh[i]%MOD);
- add(q2[max(i-l,r-i)],(r-l)*(ll)hh[i]%MOD-min(i-l,r-i)*(ll)hh[i]%MOD);
- add(q2[r-l],-(r-l)*(ll)hh[i]%MOD);
- add(q1[max(i-l,r-i)],-hh[i]); add(q1[r-l],hh[i]);
- }
- int ans=;
- for(int i=;i<=n;i++)
- {
- add(q1[i],q1[i-]); add(q2[i],q2[i-]);
- ll orz=((q1[i]*i%MOD+q2[i]%MOD)%MOD+MOD)%MOD;
- ans^=orz;
- }
- printf("%d\n",ans);
- }
C. 果冻运输
好好的一道人类智慧提答(确实很好玩)硬生生搞成了暴搜题...
开始我写了个暴力,看看数据范围,心想:肯定搜不出来,就只写了个iddfs,然后把状态hash一下输出,目测找一找规律...
最后有几个点目测玩到了一些两三分的acceptable answer...其他点都搜出1分左右...旁边wwf大爷玩了5h提答,看起来过了十几个点,结果交上去都是两三分,结果总分还没我一堆1分高...惨啊
Q:没想到A*吗?
A:想啦,感觉估价函数非常蛋疼...谁知道设成同色联通块个数这种辣鸡玩意儿就行了...
Q:那也比傻逼暴搜好啊
A:惨啊
人类智慧做法可戳:http://dram.blog.uoj.ac/blog/1864(当然不是我写的
A*大法可戳:http://immortalco.blog.uoj.ac/blog/1854
有空去写写把...
A. Jakarta Skyscrapers
大意就是有一个集合,里面可以容纳正整数。开始里面只有a和b两个正整数,对于集合中两个数x和y,可以通过一次操作得到x-y并插入到集合中。(注意到集合中只能有正整数,所以必须x>=y)。求一些操作使得集合中包含正整数c。输出任意一种步数小于400的方案,如果不存在输出-1。
显然400步内不存在,那么肯定也不存在解了...
然后我们写个暴力,可以发现,A>=B时当且仅当C<=A且gcd(A,B)|C才有解,所以我们就可以判出-1。
(以下内容与题解一点关系都没有)
然后我们发现gcd不是这么写吗:
- ll gcd(ll a,ll b)
- {
- while(b)
- {
- ll t=a%b; a=b; b=t;
- }
- return a;
- }
那么假如我们自己实现了什么方法,能高效地在这个系统中实现取模和乘法,那么我们就这样做gcd,最后乘上C/gcd(A,B)就做完了。
接下来我们就说说怎么做吧。
减法:有啦 1次
加法:注意到A-(A-x-y)=x+y。 3次
乘法:注意到我们可以快速加 O(log)次
取模:被除数-商*除数 O(log)次
那么gcd的复杂度:
复杂度似乎挺科学?可是我这样写完只有70...看到一个点403次简直哭瞎。
后面用个map加了点记忆化就行啦。求hack
- #include <iostream>
- #include <stdio.h>
- #include <stdlib.h>
- #include <map>
- using namespace std;
- typedef long long ll;
- #define SZ 666666
- ll A,B,C,la[SZ],lb[SZ];
- int ls=;
- map<ll,bool> qd;
- //O(1)
- ll gminus(ll a,ll b)
- {
- if(!b) return a;
- if(a==b) return ;
- if(qd[a-b]) return a-b;
- qd[a-b]=;
- ++ls; la[ls]=a; lb[ls]=b;
- return a-b;
- }
- //O(3)
- ll gadd(ll a,ll b)
- {
- if(a+b>=A) return A;
- if(qd[a+b]) return a+b;
- gminus(A,a);
- gminus(A-a,b);
- gminus(A,A-a-b);
- return a+b;
- }
- ll ss[];
- //O(log)
- //不需要b在集合中
- ll gmul(ll a,ll b)
- {
- if(qd[a*b]) return a*b;
- ll tg=a*b;
- ll cur=A,sn=;
- while(b)
- {
- if(b&)
- {
- for(int i=;i<=sn;i++) gadd(ss[i],ss[i]); sn=;
- cur=gminus(cur,a);
- if(qd[tg-(A-cur)]) return gadd(tg-(A-cur),gminus(A,cur));
- }
- ss[++sn]=a; a<<=; b>>=;
- }
- return gminus(A,cur);
- }
- //O(log)
- ll gmod(ll a,ll b)
- {
- if(a%b==) return ;
- if(qd[a%b]) return a%b;
- return gminus(a,gmul(b,a/b));
- }
- ll gcd(ll a,ll b)
- {
- while(b)
- {
- ll t=a%b; a=b; b=t;
- }
- return a;
- }
- void ggcd(ll a,ll b)
- {
- while(b)
- {
- ll t=gmod(a,b); a=b; b=t;
- }
- }
- int main()
- {
- cin>>A>>B>>C; qd[A]=qd[B]=;
- if(A<B) swap(A,B);
- if(C%gcd(A,B)!=||C>A) {puts("-1"); return ;}
- ll gcdd=gcd(A,B);
- ggcd(A,B);
- gmul(gcdd,C/gcdd);
- cout<<ls<<"\n";
- for(int i=;i<=ls;i++) cout<<la[i]<<" "<<lb[i]<<"\n";
- }
C. 火车管理
建议不要用题解的做法,高级做法参见 http://wangyisong1996.blog.uoj.ac/blog/1866 (人傻看不懂官方题解
感觉说的十分清楚啊(虽然我也看不懂复杂度分析
两个傻逼错误一个调了一小时,一个调了两小时...大概就是标记到了叶子还往下pushdown标记就失踪了...
- #include <iostream>
- #include <stdio.h>
- #include <math.h>
- #include <string.h>
- #include <time.h>
- #include <stdlib.h>
- #include <string>
- #include <vector>
- #include <set>
- #include <map>
- #include <queue>
- #include <algorithm>
- #include <sstream>
- #include <stack>
- #include <iomanip>
- using namespace std;
- #define pb push_back
- #define inf 1001001001
- #define infll 1001001001001001001LL
- #define FOR0(i,n) for(int (i)=0;(i)<(n);++(i))
- #define FOR1(i,n) for(int (i)=1;(i)<=(n);++(i))
- #define mp make_pair
- #define pii pair<int,int>
- #define ll long long
- #define ld double
- #define vi vector<int>
- #define fi first
- #define se second
- #define SZ 1048588
- #define S2 SZ*65
- int an=,lc1[S2],rc1[S2],vs[S2];
- int newn(int x) {return vs[++an]=x, an;}
- int join(int a,int b)
- {
- //cout<<"JOIN"<<a<<","<<b<<"\n";
- if(a&&b);else return a^b;
- int s=newn(vs[a]);
- lc1[s]=a; rc1[s]=b;
- return s;
- }
- int delf(int x)
- {
- if(!x||!lc1[x]) return ;
- else if(lc1[lc1[x]])
- {
- int g=++an;
- lc1[g]=delf(lc1[x]);
- rc1[g]=rc1[x];
- vs[g]=vs[lc1[g]];
- return g;
- }
- else return rc1[x];
- }
- int ls[SZ],rs[SZ],sum[SZ],tag[SZ],M=,M2=M+M;
- void tagit(int x,int vid)
- {
- if(!x||x>M2) return;
- sum[x]=(rs[x]-ls[x]+)*vs[vid];
- tag[x]=join(vid,tag[x]); //md这一句调了我一个小时
- }
- void pd(int x)
- {
- if(!x||x>M2||!tag[x]||x+x>M2/*wtf*/) return;
- tagit(x+x,tag[x]);
- tagit(x+x+,tag[x]);
- tag[x]=;
- }
- void upd(int x)
- {
- //pd(x+x); pd(x+x+1);
- sum[x]=sum[x+x]+sum[x+x+];
- }
- void popt(int x)
- {
- if(!x||x>M2||!tag[x]) return;
- sum[x]-=vs[tag[x]];
- tag[x]=delf(tag[x]);
- if(tag[x]) sum[x]+=vs[tag[x]];
- }
- void build()
- {
- for(int i=;i<=M;i++) ls[i+M]=rs[i+M]=i;
- for(int i=M-;i>=;i--) ls[i]=ls[i+i], rs[i]=rs[i+i+];
- }
- void push(int x,int l,int r,int ns)
- {
- if(l>r||!x||x>M2) return;
- if(ls[x]==l&&rs[x]==r) {tagit(x,ns); return;}
- pd(x);
- int m=ls[x]+rs[x]>>;
- push(x+x,l,min(r,m),ns);
- push(x+x+,max(m+,l),r,ns);
- upd(x);
- }
- void pop(int x,int p)
- {
- if(!x||x>M2||p<ls[x]||p>rs[x]) return;
- if(ls[x]==rs[x]) {popt(x); return;}
- pd(x); pop(x+x,p); pop(x+x+,p); upd(x);
- }
- int query(int x,int l,int r)
- {
- if(l>r||!x||x>M2) return ;
- if(ls[x]==l&&rs[x]==r) return sum[x];
- pd(x);
- int m=ls[x]+rs[x]>>,ans=;
- ans+=query(x+x,l,min(r,m));
- ans+=query(x+x+,max(m+,l),r);
- upd(x);
- return ans;
- }
- int main()
- {
- build();
- int n,m,ty,lans=;
- scanf("%d%d%d",&n,&m,&ty);
- while(m--)
- {
- int tp,a,b,c;
- scanf("%d",&tp);
- if(tp!=)
- {
- scanf("%d%d",&a,&b);
- a=(a+lans*ty)%n+; b=(b+lans*ty)%n+;
- if(a>b) swap(a,b);
- }
- else
- {
- scanf("%d",&a);
- a=(a+lans*ty)%n+;
- }
- if(tp==) printf("%d\n",lans=query(,a,b));
- else if(tp==) pop(,a);
- else scanf("%d",&c), push(,a,b,newn(c));
- //if(m&127);else cerr<<m<<"\n";
- }
- }
最后似乎是rank40卡线银牌?反正涨了很多rating还是很高兴的(因为之前rating太低辣)
剩下的题解等看懂了再来补...
UNR #1 题解的更多相关文章
- #386. 【UNR #3】鸽子固定器
#386. [UNR #3]鸽子固定器 题目链接 官方题解 分析: 神奇的做法+链表. 首先按照大小排序. 对于小于选择小于m个物品的时候,这个m个物品一定是一段连续的区间.因为,如果中间空着一个物品 ...
- 【UOJ#310】【UNR#2】黎明前的巧克力(FWT)
[UOJ#310][UNR#2]黎明前的巧克力(FWT) 题面 UOJ 题解 把问题转化一下,变成有多少个异或和为\(0\)的集合,然后这个集合任意拆分就是答案,所以对于一个大小为\(s\)的集合,其 ...
- 【UOJ#308】【UNR#2】UOJ拯救计划
[UOJ#308][UNR#2]UOJ拯救计划 题面 UOJ 题解 如果模数很奇怪,我们可以插值一下,设\(f[i]\)表示用了\(i\)种颜色的方案数. 然而模\(6\)这个东西很有意思,\(6=2 ...
- 【UOJ#390】【UNR#3】百鸽笼(动态规划,容斥)
[UOJ#390][UNR#3]百鸽笼(动态规划,容斥) 题面 UOJ 题解 发现这就是题解里说的:"火山喷发概率问题"(大雾 考虑如果是暴力的话,你需要记录下当前每一个位置的鸽笼 ...
- 【UOJ#389】【UNR#3】白鸽(欧拉回路,费用流)
[UOJ#389][UNR#3]白鸽(欧拉回路,费用流) 题面 UOJ 题解 首先第一问就是判断是否存在一条合法的欧拉回路,这个拿度数和连通性判断一下就行了. 第二问判断转的圈数,显然我们只需要考虑顺 ...
- 【UOJ#388】【UNR#3】配对树(线段树,dsu on tree)
[UOJ#388][UNR#3]配对树(线段树,dsu on tree) 题面 UOJ 题解 考虑一个固定区间怎么计算答案,把这些点搞下来建树,然后\(dp\),不难发现一个点如果子树内能够匹配的话就 ...
- 【UOJ#386】【UNR#3】鸽子固定器(贪心)
[UOJ#386][UNR#3]鸽子固定器(贪心) 题面 UOJ 题解 一个不难想到的暴力做法是把东西按照\(s\)排序,这样子我们枚举极大值和极小值,那么我们选择的一定是这一段之间\(v\)最大的那 ...
- Educational Round 64 题解
前言: 这场太难了……我一个紫名只打出两题……(虽说感觉的确发挥不够好) 一群蓝绿名的dalao好像只打了两题都能升分的样子…… 庆幸的是最后A出锅然后unr了>///< 写一波题解纪念这 ...
- 「UNR#1」奇怪的线段树
「UNR#1」奇怪的线段树 一道好题,感觉解法非常自然. 首先我们只需要考虑一次染色最下面被包含的那些区间,因为把无解判掉以后只要染了一个节点,它的祖先也一定被染了.然后发现一次染色最下面的那些区间一 ...
随机推荐
- Facebook开源动画库 POP-POPBasicAnimation运用
动画在APP开发过程中还是经常出现,将花几天的时间对Facebook开源动画库 POP进行简单的学习:本文主要针对的是POPBasicAnimation运用:实例源代码已经上传至gitHub,地址:h ...
- objective-c系列-NSMutableString
********************************************** NSMutableString为NSString的子类,除了父类的方法,NSMutableStirng还有 ...
- iOS 从应用中跳转至系统设置页面里的多种设置页面
我们在开发app过程中很多时候会需要设置系统权限,这时就需要在应用中跳转至系统设置页面权限设置页面,以下是自己结合网上的资料总结的一些经验: 直接从应用中跳转至系统设置中这个应用的权限设置页面 NSU ...
- DB2LOOK命令提取数据库对象信息
提取复制数据库的DDL语句:db2look -d BCDLJS -e -o db2look.sql -a -a:导出所有用户的DDL-o: 定向结果到文件-d: 数据库名-e: 抽取复制数据库所需的 ...
- C#生成注册码
string t = DateTime.Now.Ticks.ToString(); t = DESKey.DESEncrypt(t, DESKey.DesKeyStr); string[] strid ...
- win7+iis7.5+asp.net下 CS0016: 未能写入输出文件“c:\Windows\Microsoft.NET\Framework\v2.0.50727\Temporary ASP.NET Files 解决方案
http://www.cnblogs.com/finesite/archive/2011/01/28/1946940.html 给C:\Windows\temp 文件夹赋予完全控制权限,如图:
- Percona XtraBackup User Manual 阅读笔记
XtraBackup XtraBackup 2 安装XtraBackup 2.1 安装XtraBackup binary版本 2.1.1 yum的安装方法: 2.1.2 直接下载rpm包安装 3 Xt ...
- EF深入系列--细节
1.在调试的时候,查看EF生成的SQL语句 在Context类的构造函数中添加以下代码,就可以在调试的时候在[输出]窗口中看到SQL语句 this.Database.Log = s => Sys ...
- 多年前写的DataTable与实体类的转换,已放github
本文版权归mephisto和博客园共有,欢迎转载,但须保留此段声明,并给出原文链接,谢谢合作. 文章是哥(mephisto)写的,SourceLink 阅读目录 介绍 起因 代码 UnitTest G ...
- Linux磁盘管理之逻辑结构主引导扇区02
一.主引导扇区 主引导扇区位于硬盘的0磁道0柱面1扇区,共占用了63个扇区,但实际上只使用了512字节,由三大部分组成: 1.主引导记录MBR(Master Boot Record):占446字节. ...