zoukankan      html  css  js  c++  java
  • HZNU ACM一日游 2019.3.17 【2,4,6-三硝基甲苯(TNT)】

    Travel Diary

    早上8:00到HG,听说hjc20032003在等我。

    然后他竟然鸽我...最后还是勉强在8:30坐上去偏僻的HZNU的地铁。

    到文新,然后带上fjl,打滴滴,一行人来到了HZNU。

    早上模拟赛,先疯狂打期望概率为$frac{1}{10}$的T1,然后26发以后过了。

    后面爆推T2两圆面积交式子,然后少考虑特判情况WA了几发,后面没时间了就滚去吃饭了。

    话说T3真的毒瘤,主要是英语阅读比较难(整整两页纸!!!)。

    下午迟到1分钟开始模拟赛,某队伍在0:07就A了第一题?我们错过了什么。。。

    先hjc上A,我想B,然后没想法,就滚去看D了,然后发现D是一个非常裸的set题,就随手码掉了。

    这个时候2A,然后fjl说他想上个题,然后拿了J题,写个公式交了一发WA!,然后我检查一发,发现没开long long

    于是补上,Accepted!

    45分钟,作为菜鸡队的我们,成功拿下送分题ADJ。

    然后我在1小时发现H题可做,就是特判1,2,3的三种情况,然后直接排序统计即可,上手20分钟1A H题。

    然后hjc说他B题知道怎么做了。。就是最劣情况斐波那契数列,然后其他暴力搞,由hjc写B然后我继续翻看题面。

    也没什么想法,发现K过的人比较多,但是很多次提交,一定是坑题。写K,发现K并不难想出,直接$ O(n^3+q log_2 n^3) $的复杂度就正确了。

    hjc打B题比较迅速,对拍、检查,交了一发,WA!仔细再拍也没有发现什么问题,hjc认为他的结论是正确的然后开大空间,开long long,交了1发。

    还是WA!此时没办法,打印代码,让我写K题,迅速的写好K题,迅速过样例,交了一发,WA!然后发现实数比较大小有精度误差,手写二分再交WA!

    然后只能考虑整数二分,二分上端点的时候使用向上取整,二分下端点的时候向下取整,这样二分里面就可以做到整数比较了,然后再交还是WA!

    无奈,打印代码,弃坑。

    hjc说他想好G题单调队列dp,打了一发,秒过样例,交一发WA!,然后打对拍没拍出问题,后来发现没开srand,后来又发现

    打错一个字母,迅速改,拍,发现暴力错了23333,交!Accepted!

    此时考试结束还有1小时,我们手里有5题,然后B,K写了没过,在看B的时候我喵一眼M,发现是个博弈最后猜出sg函数,WA 1发由于筛质数筛小了。在比赛快结束的时候,我们考虑一定是B数据出锅了然后疯狂特判,最后8发,终于A了。。

    还有15分钟,我看了I了,写了一发过了样例,卡时交,可惜Wa了,后来发现需要考虑反映射,其实I是个简单题。

    最终带点小遗憾拿了ABDGHJM 7题,hjc 3题,ljc 4题,fjl 1题。hjc比较巨!

    在参赛的266队伍中拿了rank 38-45 (按罚时是44名),获得7个气球!!!

    Tips:反正我们还是太菜(主要是HGOI某队少了rym巨佬,他们只打了5题)

    Problem & Solution(按照预计题目难度)

    A. Little Sub and Applese

    读入字符串,把末尾的句号改成感叹号。多组数据。

    Sol : 考察选手会不会使用DEV C++,考察选手能否正确使用评测OJ。

    复杂度$ O(T imes length) $

    注意考虑多组数据还有space,那么使用getline(cin,s)读入字符串,注意该函数和scanf()一样有返回值。

    # include <cstdio>
    # include <iostream>
    # include <cstring>
    using namespace std;
    string s;
    int main()
    {
        while (getline(cin,s)) {    
            int len=s.length();
            if (s[len-1]=='.') s[len-1]='!';
            cout<<s<<'
    ';
        }
        return 0;
    }
    A.cpp

    D. Little Sub and Balloons

    求序列中有几个不同的数字。

    Sol :考察选手会不会使用STL set,本人想不出更简单的算法。

    复杂度$ O(n log_2 n) $

    # include<iostream>
    # include<cstdio>
    # include<set>
    using namespace std;
    set<int>s;
    int main()
    {
        int n; scanf("%d",&n);
        for (int i=1;i<=n;i++){
            int t; scanf("%d",&t);
            s.insert(t);
        }
        cout<<s.size()<<'
    ';
        return 0;
     } 
    D.cpp

    J. Little Sub and Apples

    给出$n,k,t$ 表示$n$个苹果,吃$k$天,每天吃$ min{ t , rest } $个苹果,询问剩余几个苹果。

    Sol: 比较$t imes k $和$n$的大小,若$t imes k leq n$输出$n-t imes k$否则输出$0$

            记得脑子清楚点开long long,不开long long WA一发!

       复杂度 $ O(1) $

    # include <cstdio>
    # include <cstring>
    # include <iostream>
    using namespace std;
    # define int long long
    signed main()
    {
        int n,k,t; cin>>n>>k>>t;
        cout<<((k*t<n)?(n-k*t):(0))<<'
    ';
        return 0;
    }
    J.cpp

     B. Little Sub and Triples

    (a,b,c)记为三元组,给定一个函数$ f(a,b,c)= frac{e^a imes e^b}{e^c} $,给出一个序列$ A $和$ m $个询问$l , r$,即求$i,j,k in [l,r]且i eq j eq k$是否存在三元组($A_i,A_j,A_k$)

    满足$A_i leq  A_j leq A_k$且使得$f(A_i,B_i,C_i) > 1$成立。

    对于每一个询问输出"YES"表示有,"NO"表示没有。

    对于100%的数据满足 $n leq 2 imes 10^5 , A_i leq 2^{32}-1$

    Sol :一道好题,考场上hjc想出正解。

    题意就是求出一序列$[l,r]$是否存在三个元素,可以组成三角形。

    我们知道,组成三角形的条件是" 两个较小边之和大于最大边 ",

    考虑缩放,放宽三角形组成条件为" 两个较小边之和大于等于最大边 ",

    在此基础上,取最优情况是" 两个较小边之和等于最大边 ",满足斐波那契数列。

    考虑斐波那契数列第多少项大于等于$2^{32}-1$,打表可知是56项。

    那么对于一个区间满足$r-l+1 > 56$就可以知道无法构造一个"NO"的数据了。

    我们知道对于前两个数,必然大于等于$1,1$如果每次恰好不能组成三角形,为了构造"NO"的数据,那么就必须按照斐波那契数列走下去。
    如果前两个数更大或者每次不能组成三角形,那么事实上会比斐波那契数列走下去走的更少。

    若考虑最差情况区间前两个数位$1,1$,每次恰好不能组成三角形,按照斐波那契数列走下去,不出56项就会和题目$A_i$的范围大。

    剩下的若干个数,由鸽巢原理可知,必然可以和前面两个数都可以组成一个三角形,自然是"YES"得证。

    接下来,考虑区间长度小于$56$情况,不满足上述性质,直接暴力排序,顺序扫,

    若$exists A_i + A_{i+1} > A_{i+2} , 1 leq i leq n-2$那么三元组($A_i , A_{i+1} , A_{i+2} $)符合条件,就是"YES"

    否则就是"NO"。

    Tips:做加法的时候会爆int,所以采用做减法(就不开long long气不气?)

    复杂度 $ O(kn) ,k=56$

    # include <cstdio>
    # include <cstring>
    # include <iostream>
    # include <algorithm>
    using namespace std;
    const int N=2e5+10;
    int t[65],a[N],n,m;
    int main()
    {
        scanf("%d%d",&n,&m);
        for (int i=1;i<=n;i++) scanf("%d",&a[i]);
        while (m--) {
            int l,r; scanf("%d%d",&l,&r);
            if (r-l+1>56) { puts("YES"); continue;}
            int cnt=0; bool flag=false;
            for (int i=l;i<=r;i++) t[++cnt]=a[i];
            sort(t+1,t+1+cnt);
            for (int i=1;i<=cnt-2;i++)
             if (t[i]>t[i+2]-t[i+1]) {
                 flag=true;break;
             }
            puts(flag?"YES":"NO");
        } 
        return 0;
    }
    B.cpp

     I. Little Sub and Enigma

    这个题目一定要看原文(我除了扯淡两句话看的懂以外剩下就是看样例猜题面了)

    26个小写字母有一组双射,求从读入的数据中能观察或推导出的关系。

    Sol : 考场完全不知道是双射,然后...时间又来不及白白放弃简单的I题...

    直接开map模拟正反映射,考虑双向映射合法的充要条件是1不能对多。

    当正反映射都是25个成立的时候,那么第26组映射是唯一确定的。(由于只有小写字母)

    复杂度 $ O(n log_2 n) $

    # include <cstdio>
    # include <cstring>
    # include <iostream>
    # include <algorithm>
    # include <map>
    using namespace std;
    string s,t;
    map<char,char>mp1,mp2;
    int bo1['z'+1],bo2['z'+1];
    int main()
    {
        getline(cin,s);int lens=s.length(); 
        getline(cin,t);int lent=t.length();
        int cnt1=0,cnt2=0;
        for (int i=0;i<lens;i++) {
            char ss=s[i],tt=t[i];
            if (mp1.count(ss)!=0) {
                if (mp1[ss]!=tt) {
                    puts("Impossible"); return 0;
                }
            } else {
                  mp1[ss]=tt;
                  bo1[ss]=1; cnt1++;
              }      
            if (mp2.count(tt)!=0) {
                if (mp2[tt]!=ss) {
                    puts("Impossible"); return 0;
                }
            } else {
                mp2[tt]=ss;
                bo2[tt]=1; cnt2++;
            }
        }
        if (cnt1==25&&cnt2==25) {
            char op1,op2;
            for (char i='a';i<='z';i++)  
             if (!bo1[i]) { op1=i; break;}
            for (char i='a';i<='z';i++)
             if (!bo2[i]) { op2=i; break;} 
            mp1[op1]=op2; 
        }
        for (int i='a';i<='z';i++)
         if (mp1.count(i)) printf("%c->%c
    ",i,mp1[i]);
        return 0;
    }
    I.cpp

     H. Little Sub and Counting

    给出一序列$A$,对于每个$A_i$,求有多少个$j$,满足${A_i} ^ {A_j} > {A_j} ^ {A_i}$。

    对于100%的数据 $n leq 10^5 , A_i leq 2^{32}-1$

    Sol : 这道题考场是我A的。挺简单的,没有想象那么难。

    考虑举一些例子,发现指数大的数字可能比指数小的数字都大,有没有反例呢。取1,2,3,一取一个准。

    后来我尝试证明$a^b > b^a$的条件,显然$ ln $降次$ln(a^b) > ln(b^a)$,即$bln a > a ln b$由于$a,b > 0$移项,

    得$frac{ln a}{a} > frac{ln b}{b} $考虑$ y=frac{ln x}{x} $的单调性。

    对函数$ y=frac{ln x}{x} $求导可知$y' = frac{frac{1}{x} imes x - lnx imes 1}{x^2} = frac{1-lnx}{x^2}$

    令$y'=0$得$x = e$ 那么 就以$e=2.7$作为基准,有3个特例$1,2,3$就打表特判贡献即可。

    复杂度 $ O(n log_2 n) $

    # include <cstdio>
    # include <cstring>
    # include <iostream>
    # include <algorithm>
    # include <map>
    # define int long long
    using namespace std;
    const int N=1e5+10;
    int a[N],t[N],cnt,n,ans[N];
    int find(int x)
    {
        int l=1,r=cnt,ans=-1;
        while (l<=r) {
            int mid=(l+r)/2;
            if (t[mid]>=x) r=mid-1,ans=mid;
            else l=mid+1;
        }
        if (ans==-1) return 0;
        return cnt-ans+1;
    }
    signed main()
    {
        scanf("%lld",&n);
        int cnt1,cnt2,cnt3;cnt1=cnt2=cnt3=0;
        for (int i=1;i<=n;i++) {
            scanf("%lld",&a[i]);
            if (a[i]==1) cnt1++;
            else if (a[i]==2) cnt2++;
            else if (a[i]==3) cnt3++;
            if (a[i]>3) t[++cnt]=a[i];
        }
        sort(t+1,t+1+cnt);
        for (int i=1;i<=n;i++) {
            if (a[i]==1) printf("0 "); 
            else if (a[i]==2) printf("%lld ",cnt1+find(5)); 
            else if (a[i]==3) printf("%lld ",cnt1+cnt2+find(4));
            else printf("%lld ",cnt1+find(a[i]+1));
        }
        puts("");
        return 0;
    }
    H.cpp

     K. Little Sub and Triangles

    给出n个整数坐标点,给出$m$个询问,求这些点中可以互相构成三角形或者点的面积在$ [l,r]$的有多少个。

    对于100%的数据$n leq 250 , mleq 10^5$

    Sol : 本题是我打的,使用海伦公式疯狂WA。由于求距离,三边求面积开2次根号,精度误差太大。

    尽管塞入容器二分的时候考虑上取整枚举上界,下取整枚举上界避免小数比较大小,但是还是由于比较强的数据WA了。

    没有想到皮克定律(个点三角形面积最小分度值为0.5,证明的话就弄个坐标矩形框三角形即可)。

    考虑怎么求格点三角形面积,使用向量外积即叉乘。可以推倒结论

    对于$ A(x_1,y_1), B(x_2,y_2), C(x_3,y_3)$围成面积$S = | frac{1}{2} imes {(x_1 y_2 - x_2 y_1)+(x_2 y_3 - x_3 y_2)+(x_3 y_1-x_1 y_3)} |$

    然后全部乘以2,就可以全整数运算了。

    复杂度 $ O(n^3 + m log_2 n^3) $

    # include <cstdio>
    # include <iostream>
    # include <cstring>
    # include <algorithm>
    # define int long long
    using namespace std;
    const int N=250+10;
    struct rec{
        int x,y;
    }a[N];
    int t[N*N*N/6],n,m,cnt;
    int Fun(int x)
    {
        if (x<0) return -x;
        else return x;
    }
    signed main()
    {
        scanf("%lld%lld",&n,&m);
        for (int i=1;i<=n;i++) 
         scanf("%lld%lld",&a[i].x,&a[i].y);
        for (int i=1;i<=n;i++)
         for (int j=i+1;j<=n;j++)
          for (int k=j+1;k<=n;k++)
           t[++cnt]=Fun((a[i].x*a[j].y-a[j].x*a[i].y)+(a[j].x*a[k].y-a[k].x*a[j].y)+(a[k].x*a[i].y-a[i].x*a[k].y));
        sort(t+1,t+1+cnt); 
        while (m--) {
            int l,r; scanf("%lld%lld",&l,&r);
            l<<=1; r<<=1;
            int L=lower_bound(t+1,t+1+cnt,l)-t;
            int R=upper_bound(t+1,t+1+cnt,r)-t-1;
            if (R<L) puts("0");
            else printf("%lld
    ",R-L+1); 
        }
        return 0;
    }
    K.cpp

     M. Little Sub and Johann

    石子游戏,要不取一堆,要不取x个且gcd(x,该堆石子个数)=1,问谁会赢。

    考虑打表求sg函数,若x是质数那么$ sg(x)=maxlimits_{i=0}^{x-1} { sg(i) } +1 $

    若x是合数,那么$sg(x)=sg(y),y为x最小质因数$

    所以一边筛法就可以求出sg函数了。下面会给出证明:

    #include <cstdio>
    #include <iostream>
    using namespace std;
    const int N=1000000;
    int t,n,sg[N+50],k,ans,x;
    void find_prime()  
    {
        sg[1]=k=1;
        for (int i=2;i<=N;i++)
        {
            if (sg[i]) continue;  
            sg[i]=++k; 
            for (int j=i;j<=N;j+=i)
             if (!sg[j]) sg[j]=k; 
        }
        return;
    }
    int main()
    {
        scanf("%d",&t);
        find_prime();
        while (t--)
        {
            scanf("%d",&n); ans=0;
            for (int i=1;i<=n;i++)  scanf("%d",&x),ans^=sg[x];  
            if (ans) printf("Subconscious is our king!
    ");
             else printf("Long live with King Johann!
    ");
        }
        return 0;
    }
    M.cpp

     G. Little Sub and Piggybank

    对于n个物品有价格w[i],快乐值v[i]。

    每天能往存钱罐加任意实数的钱,每天放的钱数需要小于等于前一天放的钱数。

    如果某 一天的钱数恰好等于那天的特价商品,则可以买,获得那个物品的快乐值,

    求最后的最大快乐值。

    对于100%的数据$ n leq 2000 $

    Sol :  本题考场是hjc 2A的,考场想了个单调队列+二分维护一个转移。 

      f[i][j]表示从i天开始(第i天rest 0块钱),到第j天买(买第j个商品),最大happiness值

      显然$maxlimits_{j=1}^{n} { f[1][j] }$就是答案。

      对于朴素转移,考虑i->j->k合法,那么有$ f[i][j]=max{f[j+1][k]}+v[i] $

      考虑i->j->k合法的条件,那么就是一个贪心,即为了买第j个物品,那么在[i+1,j]每天加入$frac{w[j]}{j-i}$块钱。

      同理,为了买第k个物品那么在[j+1,k]每天加入$frac{w[k]}{k-j}$块钱。

      所以转移的条件就是$frac{w[j]}{j-i} geq frac{w[k]}{k-j}$

      考虑每个确定的i,维护$ f[i][k] $一个单调栈,只在队尾插入,队尾弹出,且满足决策值$ f[i][k] $单调递减,k和i之间买的物品每天平摊价格$frac{w[k]}{k-i}$单调递减的单调栈。

      对于每次转移,只需要在单调栈里面二分出一个符合条件的每天平摊价格,从对应的决策值转移即可。

      时间复杂度$ O(n^2 log_2 n)$

      事实上,本题还有$O(n^2)$的dp做法,是使用后缀max维护的。

      令f[i][j]表示最后两个被买的物品为i号j号($i<j$),也可以为空但要特殊判断。

      考虑p->j->i转移合法。根据上面的分析,可以知道$pgeq left lceil j-frac{(i-j) imes w[j] }{w[i]} ight ceil$

      转移是$f[i][j]=maxlimits_{p=left lceil j-frac{(i-j) imes w[j] }{w[i]} ight ceil}^{i-1} { f[p][j] }+v[i] $

      考虑维护后缀和g[i][j]维护对于一个确定的j,f[i][j]的后缀最大值,即$ g[i][j]=maxlimits_{k=i}^{j-1} {f[k][j]} $

      那么上面的转移就可以转化为$f[j][i]=g[p][j]+v[i] , p= left lceil j-frac{(i-j) imes w[j] }{w[i]} ight ceil $

      注意一些细节,如果p对于全局合法(即$ pleq  0 $),那么令p=1,但会造成一些非法的转移,所以需要设初始值全局为-INF,排除一些奇怪的不合法情况。

      特判只有2个的初始情况,即仅取(j,i)时的初始值,即当$f[j][i]=v[j]+v[i] , (frac{w[j]}{j} geq frac{w[i]}{i-j})$

      调了好久的代码....

      复杂度$ O(n^2) $

    # include <cstdio>
    # include <cstring>
    # include <iostream>
    # include <cmath>
    # define int long long
    using namespace std;
    const int N=2e3+10;
    int w[N],v[N],n,ans;
    int f[N][N],g[N][N];
    signed main()
    {
        scanf("%lld",&n);
        for (int i=1;i<=n;i++) scanf("%lld",&w[i]);
        for (int i=1;i<=n;i++) scanf("%lld",&v[i]);
        memset(g,~0x3f,sizeof(g));
        memset(f,~0x3f,sizeof(f));
        for (int i=1;i<=n;i++)
         for (int j=i-1;j>=1;j--)
          {
              int p=max((int)ceil((double)j-(double)(i-j)*w[j]/(double)w[i]),1ll);
              if ((i-j)*w[j]>=w[i]*j) f[j][i]=max(f[j][i],v[i]+v[j]);
              f[j][i]=max(f[j][i],g[p][j]+v[i]);
              g[j][i]=max(g[j+1][i],f[j][i]);
              ans=max(ans,f[j][i]);
          }
        cout<<ans<<'
    ';
        return 0;
    }
    G.cpp
  • 相关阅读:
    URL重写,asp.net URL重写,URLRewriter.dll下载,URLRewriter,URLRewriter下载,URL重写学习
    C#制作图像旋转的程序范例

    去掉qq空间不能添加带“=”号的网络音乐地址限制
    《Javascript高级程序设计》读书笔记(二)
    NExcelAPI使用测试
    常用的开源组件
    [转]编程的首要原则(s)是什么?
    《Javascript高级程序设计》读书笔记(一)
    [转]SQL错误处理的脆弱性演示
  • 原文地址:https://www.cnblogs.com/ljc20020730/p/10553997.html
Copyright © 2011-2022 走看看