UNR #1 题解

虽然题解讲的很清楚...但还是再写一遍骗一点访问量QAQ

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=998244353;
#define SZ 2333333
int n; ll hh[SZ];
#define gc getchar()
int gi_()
{
    int s,c;
    while(c=gc,c<48||c>57);
    s=c-48;
    while(c=gc,c>=48&&c<=57) s=s*10+c-48;
    return s;
}
#define gi gi_()
int L[SZ],R[SZ],ss[SZ],sn=0;
ll q1[SZ],q2[SZ];
void add(ll& a,ll b)
{
    a+=b; a%=MOD;
    if(a<0) a+=MOD;
}
int main()
{
    n=gi;
    for(int i=1;i<=n;i++) hh[i]=gi;
    for(int i=1;i<=n;i++)
    {
        while(sn&&hh[ss[sn]]<hh[i]) --sn;
        if(sn) L[i]=ss[sn]; ss[++sn]=i;
    }
    sn=0;
    for(int i=n;i>=1;i--)
    {
        while(sn&&hh[ss[sn]]<=hh[i]) --sn;
        if(sn) R[i]=ss[sn]; else R[i]=n+1;
        ss[++sn]=i;
    }
    for(int i=1;i<=n;i++) hh[i]%=MOD;
    for(int i=1;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[1],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=0;
    for(int i=1;i<=n;i++)
    {
        add(q1[i],q1[i-1]); add(q2[i],q2[i-1]);
        ll orz=((q1[i]*i%MOD+q2[i]%MOD)%MOD+MOD)%MOD;
        ans^=orz;
    }
    printf("%d\n",ans);
}

B. 合唱队形

待施工...等看懂题解再来补(题解写的是什么卵啊

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=0;
map<ll,bool> qd;
//O(1)
ll gminus(ll a,ll b)
{
    if(!b) return a;
    if(a==b) return 0;
    if(qd[a-b]) return a-b;
    qd[a-b]=1;
    ++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[233333];
//O(log)
//不需要b在集合中
ll gmul(ll a,ll b)
{
    if(qd[a*b]) return a*b;
    ll tg=a*b;
    ll cur=A,sn=0;
    while(b)
    {
        if(b&1)
        {
            for(int i=1;i<=sn;i++) gadd(ss[i],ss[i]); sn=0;
            cur=gminus(cur,a);
            if(qd[tg-(A-cur)]) return gadd(tg-(A-cur),gminus(A,cur));
        }
        ss[++sn]=a; a<<=1; b>>=1;
    }
    return gminus(A,cur);
}
//O(log)
ll gmod(ll a,ll b)
{
    if(a%b==0) return 0;
    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]=1;
    if(A<B) swap(A,B);
    if(C%gcd(A,B)!=0||C>A) {puts("-1"); return 0;}
    ll gcdd=gcd(A,B);
    ggcd(A,B);
    gmul(gcdd,C/gcdd);
    cout<<ls<<"\n";
    for(int i=1;i<=ls;i++) cout<<la[i]<<" "<<lb[i]<<"\n";
}

B. 题解下午补

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=0,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 0;
    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=524288,M2=M+M;
void tagit(int x,int vid)
{
    if(!x||x>M2) return;
    sum[x]=(rs[x]-ls[x]+1)*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+1,tag[x]);
    tag[x]=0;
}
void upd(int x)
{
    //pd(x+x); pd(x+x+1);
    sum[x]=sum[x+x]+sum[x+x+1];
}
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=1;i<=M;i++) ls[i+M]=rs[i+M]=i;
    for(int i=M-1;i>=1;i--) ls[i]=ls[i+i], rs[i]=rs[i+i+1];
}
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]>>1;
    push(x+x,l,min(r,m),ns);
    push(x+x+1,max(m+1,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+1,p); upd(x);
}
int query(int x,int l,int r)
{
    if(l>r||!x||x>M2) return 0;
    if(ls[x]==l&&rs[x]==r) return sum[x];
    pd(x);
    int m=ls[x]+rs[x]>>1,ans=0;
    ans+=query(x+x,l,min(r,m));
    ans+=query(x+x+1,max(m+1,l),r);
    upd(x);
    return ans;
}
int main()
{
    build();
    int n,m,ty,lans=0;
    scanf("%d%d%d",&n,&m,&ty);
    while(m--)
    {
        int tp,a,b,c;
        scanf("%d",&tp);
        if(tp!=2)
        {
            scanf("%d%d",&a,&b);
            a=(a+lans*ty)%n+1; b=(b+lans*ty)%n+1;
            if(a>b) swap(a,b);
        }
        else
        {
            scanf("%d",&a);
            a=(a+lans*ty)%n+1;
        }
        if(tp==1) printf("%d\n",lans=query(1,a,b));
        else if(tp==2) pop(1,a);
        else scanf("%d",&c), push(1,a,b,newn(c));
        //if(m&127);else cerr<<m<<"\n";
    }
}

最后似乎是rank40卡线银牌?反正涨了很多rating还是很高兴的(因为之前rating太低辣)

时间: 2024-10-16 02:09:33

UNR #1 题解的相关文章

【UOJ#386】【UNR#3】鸽子固定器(贪心)

[UOJ#386][UNR#3]鸽子固定器(贪心) 题面 UOJ 题解 一个不难想到的暴力做法是把东西按照\(s\)排序,这样子我们枚举极大值和极小值,那么我们选择的一定是这一段之间\(v\)最大的那\(m\)个东西. 考虑优化这个过程,我们枚举右端点,左端点向左移动,每次插入一个元素,用堆来维护选择的过程.这样子复杂度可以做到\(O(n^2logn)\). 考虑继续优化这个过程,首先如果右端点一旦被弹出堆这个过程就可以终止了,这个很显然. 通过这个过程,我们也可以明白如果选择的个数不超过\(m

【UOJ#308】【UNR#2】UOJ拯救计划

[UOJ#308][UNR#2]UOJ拯救计划 题面 UOJ 题解 如果模数很奇怪,我们可以插值一下,设\(f[i]\)表示用了\(i\)种颜色的方案数. 然而模\(6\)这个东西很有意思,\(6=2*3\),所以我们只需要考虑其模\(2\)和模\(3\)的结果了. 而最终答案的贡献是\(\sum_{i=1}^k A_{k}^i f[i]\),当\(i\ge 3\)的时候\(6|A_k^i\),所以我们只需要知道\(f[0],f[1],f[2]\)的值. \(f[0]\)的值?当然是\(0\)啊

洛谷 P1079 Vigen&#232;re 密码 题解

此文为博主原创题解,转载时请通知博主,并把原文链接放在正文醒目位置. 题目链接:https://www.luogu.org/problem/show?pid=1079 题目描述 16 世纪法国外交家 Blaise de Vigenère 设计了一种多表密码加密算法――Vigenère 密 码.Vigenère 密码的加密解密算法简单易用,且破译难度比较高,曾在美国南北战争中为 南军所广泛使用. 在密码学中,我们称需要加密的信息为明文,用 M 表示:称加密后的信息为密文,用 C 表示:而密钥是一种

8.8联考题解

今天的T1让我怀疑我是不是在做奥赛题--这考的是什么知识点啊这个,会不会用绝对值函数? Evensgn 的债务 时间限制: 1 Sec  内存限制: 128 MB 题目描述 Evensgn 有一群好朋友,他们经常互相借钱.假如说有三个好朋友A,B,C.A 欠 B 20 元,B 欠 C 20 元,总债务规模为 20+20=40 元.Evensgn 是个追求简约的人,他觉得这样的债务太繁杂了.他认为,上面的债务可以完全等价为 A 欠C20 元,B 既不欠别人,别人也不欠他.这样总债务规模就压缩到了 

POJ 2533 - Longest Ordered Subsequence(最长上升子序列) 题解

此文为博主原创题解,转载时请通知博主,并把原文链接放在正文醒目位置. 题目链接:http://poj.org/problem?id=2533 Description A numeric sequence of ai is ordered if a1 < a2 < ... < aN. Let the subsequence of the given numeric sequence (a1, a2, ..., aN) be any sequence (ai1, ai2, ..., aiK)

(leetcode题解)Pascal&#39;s Triangle

Pascal's Triangle  Given numRows, generate the first numRows of Pascal's triangle. For example, given numRows = 5,Return [ [1], [1,1], [1,2,1], [1,3,3,1], [1,4,6,4,1] ] 题意实现一个杨辉三角. 这道题只要注意了边界条件应该很好实现出来,C++实现如下 vector<vector<int>> generate(int

2017ZZUACM省赛选拔试题部分题解----谨以纪念我这卡线滚粗的美好经历

写在前面: 其实心里有些小小的不爽又有点小小的舒畅,为啥捏?不爽当然是因为没被选拔上啦,舒畅捏则是因为没被选拔上反而让自己警醒,学长也提点很多很多."沉下去,然后一战成名"学长如是对我说,我很开心.其实这完全算不算是题解,只是我个人的一些小想法而已.而且到现在还有一题不会...让自己长点记性吧. 题目 A :聪明的田鼠 Time Limit: 1 Sec Memory Limit: 128 MB Description 田鼠MIUMIU来到了一片农田,农田可以看成是一个M*N个方格的矩

LeetCode-001题解

此题目摘自LeetCode001 Given an array of integers, find two numbers such that they add up to a specific target number. The function twoSum should return indices of the two numbers such that they add up to the target, where index1 must be less than index2.

leetcode题解: Next Permutation

最近还一直在刷leetcode,当然,更多时候只是将题解写在自己的电脑上,没有分享出来.偶尔想起来的时候,就写出来. public class Solution { public void nextPermutation(int[] nums) { if(nums==null||nums.length<=1) return; nextPermutationHelp( nums,0,nums.length-1); } public void nextPermutationHelp(int []nu