Good Bye 2018 (A~F, H)


Codeforces 1091

比賽連接git

爲何我Good Bye 2018的CD全是打表+oeis啊=-=(你改叫oeisforces好了)
必定是我打開方式不對 反正給差評→_→
(orz \(\mathbb{mjt}\) CD都是推出來的!)
話說E只要看出是Erdős–Gallai定理而後想到二分,就是裸題麼...?居然都沒怎麼看題面給的連接。。sad。好吧反正我也不會二分。
之後不能直接棄療啊。函數

A.New Year and the Christmas Ornament

#include <set>
#include <map>
#include <cstdio>
#include <cctype>
#include <vector>
#include <cstring>
#include <algorithm>
#define mp std::make_pair
#define pr std::pair<int,int>
#define gc() getchar()
typedef long long LL;
typedef unsigned long long ull;

inline int read()
{
    int now=0,f=1;
    register char c=gc();
    for(; !isdigit(c); c=='-'&&(f=-1),c=gc());
    for(; isdigit(c); now=now*10+c-'0',c=gc());
    return now*f;
}
int ans;

signed main()
{
    int A = read(), B = read(), C = read();
    for(int i = 1; i <= 100; i++)
    {
        if(i <= A && (i + 1 <= B) && (i + 2 <= C)) ans = i + i + 1 + i + 2;
    }
    printf("%d\n",ans);
    return 0;
}

B.New Year and the Treasure Geolocation

解法一:由於有\(\sum x_i+a_j=n*ans_x\),因此\(ans_x\)就是\(\frac{\sum x_i+a_j}{n}\)\(ans_y\)同理。
解法二:由於必定是最大的\(x_i\)和最小的\(a_j\)配對,因此其實輸出\(\frac{\max\{x_i\}+\min\{a_j\}}{2}\)便可。
解法三:枚舉\(x_1,y_1\)對應哪一個\(a_j,b_j\),而後就能夠\(O(n\log n)\)地判斷。複雜度\(O(n^2\log n)\)
我寫的最傻的這種==。ui

#include <set>
#include <map>
#include <cstdio>
#include <cctype>
#include <vector>
#include <cstring>
#include <algorithm>
#define mp std::make_pair
#define pr std::pair<int,int>
#define gc() getchar()
typedef long long LL;
typedef unsigned long long ull;
const int N=1005;

int X[N],Y[N],A[N],B[N];

inline int read()
{
    int now=0,f=1;register char c=gc();
    for(;!isdigit(c);c=='-'&&(f=-1),c=gc());
    for(;isdigit(c);now=now*10+c-'0',c=gc());
    return now*f;
}

int main()
{
    int n=read();
    for(int i=1; i<=n; ++i) X[i]=read(), Y[i]=read();
    for(int i=1; i<=n; ++i) A[i]=read(), B[i]=read();
    for(int i=1; i<=n; ++i)
    {
        bool f=1;
        int ansx=X[1]+A[i],ansy=Y[1]+B[i];
        std::map<int,int> vx,vy;
        for(int j=2; j<=n; ++j)
            ++vx[X[j]], ++vy[Y[j]];
        for(int j=1; j<=n; ++j)
        {
            if(i==j) continue;
            if(!vx[ansx-A[j]]||!vy[ansy-B[j]]) {f=0; break;}
            --vx[ansx-A[j]], --vy[ansy-B[j]];
        }
        if(f) return printf("%d %d\n",ansx,ansy),0;
    }
    return 0;
}

C.New Year and the Sphere Transmission(思路)

首先能夠猜到和是多少與\(\gcd(n,k)\)有關。由於能夠打表看出有多少種\(\gcd(n,k),1\leq k\leq n\),就有多少個答案。
而後再利用一下表,多枚舉幾個\(g=\gcd(n,k)\),算(\(oeis\))一下這時候的答案,發現就是\(\frac{n^2-(g-2)n}{2g}\)
或者大概能猜到,\(\gcd(n,k)=g\)時,會走到\(1,1+g,1+2g,1+3g...\)這些數,同時會有\(\frac{n}{g}\)項。等差數列求和就算出來了。
而後全部可能的\(\gcd(n,k)\)就是\(n\)的約數。對\(n\)分解約數就好了。spa

官方題解:
\(1\)從序列中拿出去。
假設咱們要走到\(v\)這個數,那麼有\(a*k\equiv v\ (mod\ n)\),也就是\(a*k-b*n=v,\ a,b\in\mathbb{N}\)。而後咱們知道當且僅當\(\gcd(n,k)\mid v\) 時方程有解。也就是當\(\gcd(n,k)=g\)時,必定會走到\(g\)的倍數這些數,有\(\frac{n}{g}\)個。一樣等差數列求和就好了。rest

#include <set>
#include <map>
#include <cstdio>
#include <cctype>
#include <vector>
#include <cstring>
#include <algorithm>
#define mp std::make_pair
#define pr std::pair<int,int>
#define gc() getchar()
typedef long long LL;
typedef unsigned long long ull;
const int N=1e6+5;

LL Ans[N];
std::map<LL,bool> vis;

inline int read()
{
    int now=0,f=1;register char c=gc();
    for(;!isdigit(c);c=='-'&&(f=-1),c=gc());
    for(;isdigit(c);now=now*10+c-'0',c=gc());
    return now*f;
}
void Work(int n)
{
    int t=0; vis.clear();
    for(int k=1; k<=n; ++k)
    {
        int p=(1+k-1)%n+1; LL s=1;
        while(p!=1) s+=p, p=(p+k-1)%n+1;
        if(!vis[s]) vis[s]=1, Ans[++t]=s, printf("(%d,%d)=%I64d\n",n,k,s);
    }
    std::sort(Ans+1,Ans+1+t);
    printf("%d\n",n);
    for(int i=1; i<=t; ++i) printf("%d ",Ans[i]);
    puts(""); puts("");
}

int main()
{
//  int n=read(),t=0;
//  for(int i=1; i<=40; ++i) Work(i); return 0;

    int n=read(),t=0;
    for(int i=1; 1ll*i*i<=n; ++i)
        if(!(n%i))
        {
            int g=i;
            Ans[++t]=(1ll*n*n-1ll*(g-2)*n)/(2*g);
            if(1ll*i*i!=n) g=n/i, Ans[++t]=(1ll*n*n-1ll*(g-2)*n)/(2*g);
        }
    std::sort(Ans+1,Ans+1+t);
    t=std::unique(Ans+1,Ans+1+t)-Ans-1;
    for(int i=1; i<=t; ++i) printf("%I64d ",Ans[i]);

    return 0;
}

D.New Year and the Permutation Concatenation(思路 計數)

\(Description\)
給定\(n\)。將\(n!\)\(n\)的排列按字典序從小到大拼接成一個長爲\(n*n!\)的序列,求該序列中有多少個長爲\(n\)的子段,知足它們的和爲\(\frac{n(n+1)}{2}\)(就是\(1,...,n\)各出現一次)。
\(n\leq 10^6\)code

\(Solution\)htm

打出表來,發現什麼都\(OEIS\)不到。再猜答案和\(n*n!\)有關係。而後用\(n*n!\)一減答案發現這個數列能夠被\(OEIS\)到,而後套幾個式子/數列再用\(n*n!\)一減就獲得答案了。。blog

沒看懂題解在說啥。寫一下(神仙)\(\mathbb{mjt}\)的作法orz。
對於\(n=3\)\(p=[1,2,3,1,3,2,2,1,3,2,3,1,3,1,2,3,2,1]\),(結合樣例解釋)咱們猜,把排列分紅\(n\)段,也就是以不一樣數開頭的排列爲一段,這些段的答案是同樣的。(對於\(n=3\)就是分紅\([1,2,3,1,3,2],[2,1,3,2,3,1],[3,1,2,3,2,1]\)三段)
事實上也確實是這樣,對於相鄰兩段好比:\(2,n,...1,3,1,...,n\),它們之間也形不成合法子段。
因此咱們如今只考慮怎麼算以某個數開頭的排列的答案(好比\([1,2,3,1,3,2]\)),再乘\(n\)就是答案了。
咱們猜是能夠遞推的。也就是假設咱們知道\(n-1\)時的答案\(f_{n-1}\),怎麼求\(f_n\)
\(n=4\)來講,考慮此時以\(4\)開頭的排列,就是在每一個\(3\)的排列前面加上一個\(4\)再拼在一塊兒。
顯然咱們能夠獲得\(3!\)種合法子段。而\(f_3\)中的每種方案,在\(n=4\)時也都能和一個\(4\)組成合法的子段(寫一寫看)。但惟獨最後面的\(4,3,2,1\)算了兩次。因此有:\(f_n=\left[f_{n-1}+(n-1)!-1\right]*n\)遊戲

代碼是\(OEIS\)的那種。。懶得再寫了。

#include <set>
#include <map>
#include <cstdio>
#include <cctype>
#include <vector>
#include <cstring>
#include <algorithm>
#define mp std::make_pair
#define pr std::pair<int,int>
#define gc() getchar()
#define mod 998244353
typedef long long LL;
typedef unsigned long long ull;
const int N=1e6+5;

inline int read()
{
    int now=0,f=1;register char c=gc();
    for(;!isdigit(c);c=='-'&&(f=-1),c=gc());
    for(;isdigit(c);now=now*10+c-'0',c=gc());
    return now*f;
}
//bool Check(int *p,int n)
//{
//  for(int i=1; i<=n; ++i) if(p[i]!=i) return 0;
//  return 1;
//}
//void Work(int n)
//{
//  static int A[10000004],p[15];
//  for(int i=1; i<=n; ++i) p[i]=i;
//  int t=0;
//  do
//  {
//      for(int i=1; i<=n; ++i) A[++t]=p[i];
//      std::next_permutation(p+1,p+1+n);
//  }while(!Check(p,n));
//  int ans=0;
//  for(int i=1; i<=t; ++i)
//  {
//      int s=0;// n<=4 && printf("%d ",A[i]);
//      for(int j=i; j-i+1<=n && j<=t; ++j)
//          s+=A[j], j-i+1==n && s==n*(n+1)/2 && (++ans);
//      if(n<=5 && i+n-1<=t) printf("%lld ",s);
//  }
//  puts("");
//  printf("%d:%d t:%d\n",n,ans,t);
//}

int fac[N],A[N];

int main()
{
//  for(int i=1; i<=9; ++i) Work(i);
    int n=read();
    fac[0]=1, A[0]=1;
    for(int i=1; i<=n; ++i) fac[i]=1ll*fac[i-1]*i%mod;
    for(int i=1; i<=n; ++i) A[i]=1ll*i*A[i-1]%mod+1;
    A[n]=(A[n]+mod-fac[n]-1)%mod;
    LL ans=1ll*n*fac[n]%mod-A[n];
    printf("%d\n",(int)(ans%mod+mod)%mod);

    return 0;
}

E.New Year and the Acquaintance Estimation(Erdos–Gallai定理 二分)

\(Description\)
給定度數序列\(d_1,...,d_n\),求\(d_{n+1}\)等於多少時,度數序列\(d_1,d_2,...,d_{n+1}\)可簡單圖化。輸出全部可能的\(d_{n+1}\)
可簡單圖化是指,存在一張簡單無向圖,使得該圖點的度數能夠與該度數序列一一對應。
\(n\leq 5\times 10^5\)

\(Solution\)
話說E只要看出是Erdős–Gallai定理而後想到二分,就是裸題麼...?
題意就是,輸出\(d_{n+1}\)等於多少時,度數序列\(d_1,d_2,...,d_{n+1}\)可簡單圖化(就是存在一張簡單圖使得知足該度數序列)。(當時居然想都沒想真是氣人)
考慮枚舉\(d_{n+1}\)。給定一個度數序列判斷其是否合法能夠用Erdős–Gallai定理,複雜度\(O(n)\)。因此如今的複雜度是\(O(n^2)\)的。
根據樣例咱們還能夠猜測並驗證:

  1. 由握手定理(就是無向圖中全部點的度數之和爲偶數),\(d_{n+1}\)的奇偶性能夠肯定。
  2. 知足條件的\(d_{n+1}\)必定是一段連續的區間。
    因此咱們就能夠二分了。

二分要獲得的是某段區間,分別二分左右端點,但還須要討論一下。
\(n=n+1\),寫一下Erdős–Gallai定理的式子:\[\sum_{i=1}^kd_i\leq k(k-1)+\sum_{i=k+1}^n\min(d_i,k)\]

二分\(n\)的度數\(d_n=mid\),而後\(sort\)一下度數序列。
從小到大枚舉\(k\)的時候,記\(left\)爲左式的值,\(right\)爲右式的值。若一直有\(left\leq right\),顯然\(mid\)可行。
不然若\(left>right\),咱們要麼減少\(left\),要麼增大\(right\)。而惟一能改變的就是\(n\)的度數\(mid\)
因此如今若\(mid\geq d_k\)(影響左式),咱們能夠減少\(mid\)使得序列合法,也就是答案偏大。
\(mid<d_k\)(影響右式),能夠增大\(mid\),也就是答案偏小。
能夠肯定答案偏大偏小,就能夠二分出區間了。複雜度\(O(n\log n)\)

通過一些預處理也能夠作到\(O(n)\)

//187ms 8200KB
#include <cstdio>
#include <cctype>
#include <algorithm>
//#define gc() getchar()
#define MAXIN 500000
#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
typedef long long LL;
const int N=5e5+5;

int A[N];
char IN[MAXIN],*SS=IN,*TT=IN;

inline int read()
{
    int now=0;register char c=gc();
    for(;!isdigit(c);c=gc());
    for(;isdigit(c);now=now*10+c-'0',c=gc());
    return now;
}
int Check(const int n,const int mid)
{
    static int d[N];
    static LL sum[N];
    A[n]=-1;
    for(int i=1,p=1; i<=n; ++i) d[i]=(p==i&&mid>A[p])?mid:A[p++], sum[i]=sum[i-1]+d[i];
    LL vl,vr;
    for(int k=1,p=n; k<=n; ++k)
    {
        while(p>k && d[p]<k) --p;
        vl=sum[k], vr=1ll*k*(k-1)+1ll*(std::max(p,k)-k)*k+sum[n]-sum[std::max(p,k)];
        if(vl>vr) return mid<d[k]?-1:1;
    }
    return 0;
}

int main()
{
    int n=read(),parity=0;
    for(int i=1; i<=n; ++i) parity^=(A[i]=read())&1;
    std::sort(A+1,A+1+n,std::greater<int>());

    int l=0,r=n-parity>>1,L=0,R=-1,mid;
    while(l<=r)
        if(Check(n+1,(mid=l+r>>1)*2+parity)>=0) L=mid, r=mid-1;
        else l=mid+1;
    l=0,r=n-parity>>1;
    while(l<=r)
        if(Check(n+1,(mid=l+r>>1)*2+parity)<=0) R=mid, l=mid+1;
        else r=mid-1;

    if(L>R) puts("-1");
    else for(int i=L; i<=R; ++i) printf("%d ",i*2+parity);

    return 0;
}

F.New Year and the Mallard Expedition(貪心)

寫了近一天,改了4遍越改越麻煩...mmp...
其實很好寫...

先假設全部路程都飛過去,能量不夠等會再補。則此時的答案爲\(ans=\sum\limits_{i=1}^nL_i\)
而後從\(i=1\sim n\)枚舉。若是此時的能量不夠飛\(L_i\)的距離,就從以前補。
因此記\(W,G\)分別表示以前飛過的\(water\)有多少、以前飛過的\(grass\)有多少。
顯然能量來自,將飛過的\(W\)路程變成游過去,還不夠的話將飛過的\(G\)路程變成走過去,再不夠的話,若是出現過\(water\)就來回遊補夠能量,沒出現過\(water\)就在\(grass\)來回走補夠能量。

因此對於\(L_i\),咱們須要從\(W\)轉移過來的能量有\(t=\min\{L_i,2W\}\)\(W\)的路程能變成\(2W\)能量,由於是將原先的飛替換成遊),也就是須要將以前\(\frac t2\)路程的飛變爲游泳,來得到這\(t\)能量。因此\(ans\)+=\(\frac t2\times2\)\(W\)-=\(\frac t2\)(爲了不小數將\(W\)*=\(2\),此時就是\(W\)-=\(t\))。
若是此時\(L_i-t\)仍不爲\(0\),那麼一樣從\(G\)轉移。
還不爲\(0\),則如上所述來回遊/走。

//46ms  1000KB
#include <cstdio>
#include <cctype>
#include <algorithm>
#define gc() getchar()
typedef long long LL;
const int N=1e5+5,Ref1[3]={5,3,1},Ref2[3]={1,3,1};

int mp[N];
LL A[N];

inline LL read()
{
    LL now=0;register char c=gc();
    for(;!isdigit(c);c=gc());
    for(;isdigit(c);now=now*10+c-'0',c=gc());
    return now;
}

int main()
{
    int n=read(); LL ans=0;
    for(int i=1; i<=n;++i) ans+=A[i]=read();
    register char c=gc(); while(c!='W'&&c!='G'&&c!='L') c=gc();
    //Grass:0 Water:1 Lava:2
    mp[1]=c=='L'?2:c=='W';
    for(int i=2; i<=n;++i) c=gc(), mp[i]=c=='L'?2:c=='W';

    LL W=0,G=0;
    for(int i=1,cost=5; i<=n; ++i)
    {
        if(!mp[i]) G+=A[i]<<1;
        else if(mp[i]==1) W+=A[i]<<1, cost=3;
        LL rest=A[i],t;
        t=std::min(rest,W), rest-=t, ans+=t, W-=t;
        t=std::min(rest,G), rest-=t, ans+=t<<1, G-=t;
        ans+=rest*cost;
    }
    printf("%I64d\n",ans);

    return 0;
}

咕咕

 

H.New Year and the Tricolore Recreation(博弈論 bitset)

\(Description\)
Alice和Bob玩遊戲。給定\(n,f\),表示有\(n\)行的棋盤,每行有三個棋子。Alice每次能夠選擇一行將該行左邊的一個或兩個棋子往右移動\(d\)步,Bob每次能夠選擇一行將該行右邊的一個或兩個棋子往左移動\(d\)步。
要求移動時一個棋子不能跨越另外一個棋子,且\(d\)是質數或兩個質數的乘積,且\(d\neq f\)。求出Alice和Bob分別做爲先手時,誰能贏。
\(n\leq10^5,\ 座標絕對值\leq10^5\)

\(Solution\)
右移一個棋子就是縮小第二三兩個棋子之間的距離,右移兩個棋子就是縮小一二兩個棋子之間的距離。設三個棋子位置爲\(a,b,c\),每一行實際就是兩個棋子數爲\(c-b-1,b-a-1\)\(nim\)遊戲。
若是能算出棋子數爲\(x\)的遊戲的\(SG\)函數,將\(2n\)個遊戲的\(sg\)值全異或起來便可。考慮如何算,顯然有\(sg(x)=\mathbb{mex}_{d\in P}\{sg(x-d)\}\),可是複雜度是\(n^2\)的。
打個表發現,\(sg\)值最大不會超過\(100\)(我也不知道怎麼能得出的)。咱們開\(100\)\(bitset\ A[i]\),分別表示每一個數字是否存在\(sg\)值爲\(i\)的後繼。再預處理\(d\in P\)\(bitset\ S\)。這樣從小到大求\(sg(i)\)時,就for一遍看它不存在哪一個\(sg\)值的後繼;而後\(A[sg(i)]|=S<<i\)便可(更新上能到\(i\)的位置)。
複雜度\(O(\frac{n^2}{w})\)

//1638ms    4700KB
#include <cstdio>
#include <cctype>
#include <bitset>
#include <algorithm>
#define gc() getchar()
#define MAXIN 500000
//#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
typedef long long LL;
const int N=2e5+1,M=102;

int sg[N];
std::bitset<N> S,A[M];
char IN[MAXIN],*SS=IN,*TT=IN;

inline int read()
{
    int now=0,f=1;register char c=gc();
    for(;!isdigit(c);c=='-'&&(f=-1),c=gc());
    for(;isdigit(c);now=now*10+c-48,c=gc());
    return now*f;
}
void Init()
{
    static int P[N],mn[N];
    int cnt=0;
    for(int i=2; i<N; ++i)
    {
        if(!mn[i]) P[++cnt]=i;
        for(int j=1; j<=cnt&&i*P[j]<N; ++j)
        {
            mn[i*P[j]]=i;
            if(!(i%P[j])) break;
        }
    }
    for(int i=2; i<N; ++i) if(!mn[mn[i]]) S[i]=1;
}

int main()
{
    int n=read(); Init(), S[read()]=0;
    for(int i=0; i<N; ++i)
    {
        while(A[sg[i]][i]) ++sg[i];
        A[sg[i]]|=S<<i;
    }
    int ans=0;
    for(int a,b,c; n--; ) a=read(),b=read(),c=read(),ans^=sg[b-a-1]^sg[c-b-1]; 
    puts(ans?"Alice\nBob":"Bob\nAlice");

    return 0;
}
相關文章
相關標籤/搜索