• Good Bye 2018 (A~F, H)



    Codeforces 1091

    比赛链接

    为什么我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
    ",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(nlog n))地判断。复杂度(O(n^2log n))
    我写的最傻的这种==。

    #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
    ",ansx,ansy),0;
    	}
    	return 0;
    }
    

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

    首先可以猜到和是多少与(gcd(n,k))有关。因为可以打表看出有多少种(gcd(n,k),1leq kleq 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)分解约数就行了。

    官方题解:
    (1)从序列中拿出去。
    假设我们要走到(v)这个数,那么有(a*kequiv v (mod n)),也就是(a*k-b*n=v, a,binmathbb{N})。然后我们知道当且仅当(gcd(n,k)mid v) 时方程有解。也就是当(gcd(n,k)=g)时,一定会走到(g)的倍数这些数,有(frac{n}{g})个。同样等差数列求和就行了。

    #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,k,s);
    	}
    	std::sort(Ans+1,Ans+1+t);
    	printf("%d
    ",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)各出现一次)。
    (nleq 10^6)

    (Solution)

    打出表来,发现什么都(OEIS)不到。再猜答案和(n*n!)有关系。然后用(n*n!)一减答案发现这个数列可以被(OEIS)到,然后套几个式子/数列再用(n*n!)一减就得到答案了。。

    没看懂题解在说啥。写一下(神仙)(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 ight]*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,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
    ",(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})
    可简单图化是指,存在一张简单无向图,使得该图点的度数可以与该度数序列一一对应。
    (nleq 5 imes 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_ileq k(k-1)+sum_{i=k+1}^nmin(d_i,k)$$

    二分(n)的度数(d_n=mid),然后(sort)一下度数序列。
    从小到大枚举(k)的时候,记(left)为左式的值,(right)为右式的值。若一直有(leftleq right),显然(mid)可行。
    否则若(left>right),我们要么减小(left),要么增大(right)。而唯一能改变的就是(n)的度数(mid)
    所以现在若(midgeq d_k)(影响左式),我们可以减小(mid)使得序列合法,也就是答案偏大。
    (mid<d_k)(影响右式),可以增大(mid),也就是答案偏小。
    可以确定答案偏大偏小,就可以二分出区间了。复杂度(O(nlog 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=sumlimits_{i=1}^nL_i)
    然后从(i=1sim 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 imes2)(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
    ",ans);
    
    	return 0;
    }
    

    咕咕

    
    

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

    (Description)
    Alice和Bob玩游戏。给定(n,f),表示有(n)行的棋盘,每行有三个棋子。Alice每次可以选择一行将该行左边的一个或两个棋子往右移动(d)步,Bob每次可以选择一行将该行右边的一个或两个棋子往左移动(d)步。
    要求移动时一个棋子不能跨越另一个棋子,且(d)是质数或两个质数的乘积,且(d eq f)。求出Alice和Bob分别作为先手时,谁能赢。
    (nleq10^5, 坐标绝对值leq10^5)

    (Solution)
    右移一个棋子就是缩小第二三两个棋子之间的距离,右移两个棋子就是缩小一二两个棋子之间的距离。设三个棋子位置为(a,b,c),每一行实际就是两个棋子数为(c-b-1,b-a-1)(nim)游戏。
    如果能算出棋子数为(x)的游戏的(SG)函数,将(2n)个游戏的(sg)值全异或起来即可。考虑如何算,显然有(sg(x)=mathbb{mex}_{din P}{sg(x-d)}),但是复杂度是(n^2)的。
    打个表发现,(sg)值最大不会超过(100)(我也不知道怎么能得出的)。我们开(100)(bitset A[i]),分别表示每个数字是否存在(sg)值为(i)的后继。再预处理(din 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
    Bob":"Bob
    Alice");
    
    	return 0;
    }
    
  • 相关阅读:
    扩展springMVC
    SpringBoot推荐的Thymeleaf
    springboot使用注解添加组件
    @PropertySource 加载指定的配置文件
    读取yml配置文件中的值
    媒体查询
    若是前台接收的数据为null的不进行传递
    DATA时间:若是数据库存储的为毫秒,而前台需要的是秒,下面介绍一个工具类:
    表单验证
    Redis集群总结
  • 原文地址:https://www.cnblogs.com/SovietPower/p/10209994.html
Copyright © 2020-2023  润新知