Codeforces Round 936 (Div. 2)

Preface

懒狗闪总开完组会不打CF直接滚去睡觉了可海星,感觉我好像退化成我们队训练最少的人了

赛后补了下发现这场题竟然都会做,不过F不知道是我实现有问题常数大得一批加了读优才惊险卡过


A. Median of an Array

签到,找到中位数后面与它相同的数的个数即可

#include<cstdio>
#include<iostream>
#include<utility>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<set>
#include<array>
#include<random>
#include<bitset>
#include<ctime>
#include<limits.h>
#include<assert.h>
#include<unordered_set>
#include<unordered_map>
#define RI register int
#define CI const int&
#define mp make_pair
#define fi first
#define se second
#define Tp template <typename T>
using namespace std;
typedef long long LL;
typedef long double LDB;
typedef unsigned long long u64;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=100005;
int t,n,a[N];
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%d",&t);t;--t)
	{
		RI i; for (scanf("%d",&n),i=1;i<=n;++i) scanf("%d",&a[i]);
		int ans=1; for (sort(a+1,a+n+1),i=(n+1)/2+1;i<=n;++i)
		if (a[i]==a[(n+1)/2]) ++ans; else break;
		printf("%d\n",ans);
	}
	return 0;
}

B. Maximum Sum

找出最大子段和\(S\)后,显然每次就是把\(S\)插入到能和原来的最大子段和一起统计的位置

最后答案就是\((2^k-1)\times S+\sum_{i=1}^n a_i\)

#include<cstdio>
#include<iostream>
#include<utility>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<set>
#include<array>
#include<random>
#include<bitset>
#include<ctime>
#include<limits.h>
#include<assert.h>
#include<unordered_set>
#include<unordered_map>
#define RI register int
#define CI const int&
#define mp make_pair
#define fi first
#define se second
#define Tp template <typename T>
using namespace std;
typedef long long LL;
typedef long double LDB;
typedef unsigned long long u64;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=200005,mod=1e9+7;
int t,n,k,a[N];
inline int quick_pow(int x,int p=mod-2,int mul=1)
{
	for (;p;p>>=1,x=1LL*x*x%mod) if (p&1) mul=1LL*mul*x%mod; return mul;
}
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%d",&t);t;--t)
	{
		RI i; for (scanf("%d%d",&n,&k),i=1;i<=n;++i) scanf("%d",&a[i]);
		LL cur=0,mx=0,sum=0; for (i=1;i<=n;++i) cur=max(cur+a[i],0LL),mx=max(mx,cur),sum+=a[i];
		printf("%d\n",(1LL*(quick_pow(2,k)-1)*(mx%mod)%mod+sum%mod+mod)%mod);
	}
	return 0;
}

C. Tree Cutting

最小值最大一眼二分答案\(x\),考虑如何check

不妨选择任意一个点为根把树变成有根树,考虑从叶子往上贪心,如果存在某个点子树大小\(\ge x\)就直接砍掉它和父亲的边

不难发现这样一定是最优的,最后检验下是否砍掉了\(k\)条边以及最后顶上留下的部分大小是否\(\ge x\)即可

同时显然这个贪心过程对于任意点为根都能跑出正确的策略,因此可以随便选

#include<cstdio>
#include<iostream>
#include<utility>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<set>
#include<array>
#include<random>
#include<bitset>
#include<ctime>
#include<limits.h>
#include<assert.h>
#include<unordered_set>
#include<unordered_map>
#define RI register int
#define CI const int&
#define mp make_pair
#define fi first
#define se second
#define Tp template <typename T>
using namespace std;
typedef long long LL;
typedef long double LDB;
typedef unsigned long long u64;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=100005;
int t,n,k,x,y,cnt,all,sz[N]; vector <int> v[N];
inline void DFS(CI lim,CI now=1,CI fa=0)
{
	if (cnt==k) return; sz[now]=1;
	for (auto to:v[now]) if (to!=fa)
	{
		DFS(lim,to,now); sz[now]+=sz[to];
		if (cnt==k) return;
	}
	if (fa==0) return; if (sz[now]>=lim) ++cnt,all-=sz[now],sz[now]=0;
	if (cnt==k) return;
}
inline bool check(CI x)
{
	all=n; cnt=0; DFS(x); return cnt==k&&all>=x;
}
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%d",&t);t;--t)
	{
		RI i; for (scanf("%d%d",&n,&k),i=1;i<=n;++i) v[i].clear();
		for (i=1;i<n;++i) scanf("%d%d",&x,&y),v[x].push_back(y),v[y].push_back(x);
		int l=1,r=n/(k+1),mid,ret; while (l<=r)
		if (check(mid=l+r>>1)) ret=mid,l=mid+1; else r=mid-1;
		printf("%d\n",ret);
	}
	return 0;
}

D. Birthday Gift

经典二进制操作,上来先考虑从高到低依次处理

不难发现对于某个二进制位\(2^p\),若序列中这一位为\(1\)的数有奇数个,则不管怎么划分最后这一位上一定为\(1\)

否则如果这一位为\(1\)的数有偶数个,只要把这些数偶数个分为一组即可,由于这题要求划分数量最多,因此显而易见的把相邻的两两合并即可

注意到在任意一个时刻,如果我们凑出了\(<x\)的数那么直接退出统计答案一定最优,否则如果前缀相等则还需要往后讨论后面的位带来的影响

暴力处理分组过程,复杂度\(O(n\log a_i)\)

#include<cstdio>
#include<iostream>
#include<utility>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<set>
#include<array>
#include<random>
#include<bitset>
#include<ctime>
#include<limits.h>
#include<assert.h>
#include<unordered_set>
#include<unordered_map>
#define RI register int
#define CI const int&
#define mp make_pair
#define fi first
#define se second
#define Tp template <typename T>
using namespace std;
typedef long long LL;
typedef long double LDB;
typedef unsigned long long u64;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=100005;
int t,n,x,ans;
inline void solve(vector <int> a,CI p=30)
{
	if (p<0) return (void)(ans=max(ans,(int)a.size()));
	RI i,j; vector <int> pos;
	for (i=0;i<a.size();++i) if ((a[i]>>p)&1) pos.push_back(i);
	if (pos.size()%2==1&&((x>>p)&1)==0) return;
	if (pos.size()%2==1&&((x>>p)&1)==1) return solve(a,p-1);
	vector <int> rmv(a.size()); vector <int> b,c=a;
	for (i=0;i<pos.size();i+=2)
	for (j=pos[i]+1;j<=pos[i+1];++j)
	rmv[j]=1,c[pos[i]]^=c[j];
	for (i=0;i<c.size();++i) if (!rmv[i]) b.push_back(c[i]);
	if (((x>>p)&1)==0) return solve(b,p-1);
	ans=max(ans,(int)b.size()); return solve(a,p-1);
}
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%d",&t);t;--t)
	{
		RI i; scanf("%d%d",&n,&x); vector <int> a(n);
		for (i=0;i<n;++i) scanf("%d",&a[i]);
		ans=-1; solve(a); printf("%d\n",ans);
	}
	return 0;
}

E. Girl Permutation

感觉算是我最近见到的最简单的计数题了,一眼秒的那种

首先如果前缀最大最后的位置和后缀最大第一个位置的值不同,则显然无解,同时还有些trivial的无解情况也一起顺手判掉

否则我们可以确定最大数\(n\)所在的位置\(x\),首先需要一个把剩下的\(n-1\)个数分到两边的方案数,就是\(C_{n-1}^{x-1}\)

然后此时每一边的问题独立,以前缀这一段为例

我们从后往前枚举前缀最大值的位置,显然当前最大的数的位置唯一确定了,但该位置到\(x\)这一段中间的数方法是任意的

刚开始傻逼了这里乘了个组合数上去,后面过不了样例才发现原来是还得乘一个随便放顺序的排列数

后缀的处理同理,最后把每部分的方案数乘起来即可

#include<cstdio>
#include<iostream>
#include<utility>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<set>
#include<array>
#include<random>
#include<bitset>
#include<ctime>
#include<limits.h>
#include<assert.h>
#include<unordered_set>
#include<unordered_map>
#define RI register int
#define CI const int&
#define mp make_pair
#define fi first
#define se second
#define Tp template <typename T>
using namespace std;
typedef long long LL;
typedef long double LDB;
typedef unsigned long long u64;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=200005,mod=1e9+7;
int t,n,m1,m2,fact[N],ifac[N];
inline int quick_pow(int x,int p=mod-2,int mul=1)
{
	for (;p;p>>=1,x=1LL*x*x%mod) if (p&1) mul=1LL*mul*x%mod; return mul;
}
inline void init(CI n)
{
	RI i; for (fact[0]=i=1;i<=n;++i) fact[i]=1LL*fact[i-1]*i%mod;
	for (ifac[n]=quick_pow(fact[n]),i=n-1;i>=0;--i) ifac[i]=1LL*ifac[i+1]*(i+1)%mod;
}
inline int C(CI n,CI m)
{
	if (n<0||m<0||n<m) return 0;
	return 1LL*fact[n]*ifac[m]%mod*ifac[n-m]%mod;
}
inline int A(CI n,CI m)
{
	if (n<0||m<0||n<m) return 0;
	return 1LL*fact[n]*ifac[n-m]%mod;
}
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%d",&t),init(2e5);t;--t)
	{
		RI i; scanf("%d%d%d",&n,&m1,&m2); vector <int> pre(m1),suf(m2);
		for (i=0;i<m1;++i) scanf("%d",&pre[i]);
		for (i=0;i<m2;++i) scanf("%d",&suf[i]);
		if (pre[0]!=1||suf[m2-1]!=n) { puts("0"); continue; }
		if (pre[m1-1]!=suf[0]) { puts("0"); continue; }
		int ans=C(n-1,suf[0]-1),cur;
		for (cur=suf[0]-1,i=m1-2;i>=0;--i)
		ans=1LL*ans*A(cur-1,pre[i+1]-pre[i]-1)%mod,cur-=pre[i+1]-pre[i];
		for (cur=n-suf[0],i=1;i<m2;++i)
		ans=1LL*ans*A(cur-1,suf[i]-suf[i-1]-1)%mod,cur-=suf[i]-suf[i-1];
		printf("%d\n",ans);
	}
	return 0;
}

F. Nobody is needed

很套路的一个题

首先对于只选一个数的情况特别考虑,然后我们发现对于某个固定的右端点,其合法的左端点选法是$\log $级别的

因此如果我们把以\(l\)为开头,\(r\)为结尾的选法的方案数统计出来,最后就是个询问\([L,R]\)包含的所有子区间的贡献和,这个是个经典的扫描线问题

现在的问题是如何计算\(l,r\)的贡献,这个也很好办,我们对于每个\(r\)按照\(l\)降序DP一下即可

具体实现看代码,复杂度\(O(n\log^2 n)\)

#include<cstdio>
#include<iostream>
#include<utility>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<set>
#include<array>
#include<random>
#include<bitset>
#include<ctime>
#include<limits.h>
#include<assert.h>
#include<unordered_set>
#include<unordered_map>
#define int long long
#define RI register int
#define CI const int&
#define mp make_pair
#define fi first
#define se second
#define Tp template <typename T>
using namespace std;
typedef long long LL;
typedef long double LDB;
typedef unsigned long long u64;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=1e6+5;
int t,n,q,a[N],pos[N],ans[N],l,r; vector <int> frac[N]; vector <pi> evt[N];
#define Tp template <typename T>
class FileInputOutput
{
    private:
        static const int S=1<<21;
        #define tc() (A==B&&(B=(A=Fin)+fread(Fin,1,S,stdin),A==B)?EOF:*A++)
        #define pc(ch) (Ftop!=Fend?*Ftop++=ch:(fwrite(Fout,1,S,stdout),*(Ftop=Fout)++=ch))
        char Fin[S],Fout[S],*A,*B,*Ftop,*Fend; int pt[30];
    public:
        inline FileInputOutput(void) { Ftop=Fout; Fend=Fout+S; }
        Tp inline void read(T& x)
        {
            x=0; char ch; while (!isdigit(ch=tc()));
            while (x=(x<<3)+(x<<1)+(ch&15),isdigit(ch=tc()));
        }
        Tp inline void write(T x,const char ch='\n')
        {
        	if (x<0) x=-x,pc('-');
            RI ptop=0; while (pt[++ptop]=x%10,x/=10);
            while (ptop) pc(pt[ptop--]+48); pc(ch);
        }
        inline void flush(void)
        {
            fwrite(Fout,1,Ftop-Fout,stdout);
        }
        #undef tc
        #undef pc
}F;
inline void init(CI n)
{
	for (RI i=1;i<=n;++i) for (RI j=i;j<=n;j+=i) frac[j].push_back(i);
}
class Tree_Array
{
	private:
		int bit[N];
	public:
		#define lowbit(x) (x&(-x))
		inline void init(CI n)
		{
			for (RI i=1;i<=n;++i) bit[i]=0;
		}
		inline void add(RI x,CI y)
		{
			for (;x;x-=lowbit(x)) bit[x]+=y;
		}
		inline int get(RI x,int ret=0)
		{
			for (;x<=n;x+=lowbit(x)) ret+=bit[x]; return ret;
		}
		#undef lowbit
}BIT;
signed main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (F.read(t),init(1e6);t;--t)
	{
		RI i,j,k; for (F.read(n),F.read(q),i=1;i<=n;++i)
		F.read(a[i]),pos[a[i]]=i,evt[i].clear();
		for (i=1;i<=n;++i)
		{
			vector <int> lpos;
			for (auto x:frac[a[i]])
			if (pos[x]<i) lpos.push_back(pos[x]);
			vector <int> dp(lpos.size());
			sort(lpos.begin(),lpos.end());
			for (j=(int)lpos.size()-1;j>=0;--j)
			for (dp[j]=1,k=j+1;k<lpos.size();++k)
			if (a[lpos[k]]%a[lpos[j]]==0) dp[j]+=dp[k];
			for (j=0;j<lpos.size();++j)
			evt[i].push_back({lpos[j],dp[j]});
		}
		for (i=1;i<=q;++i) F.read(l),F.read(r),ans[i]=r-l+1,evt[r].push_back({l,-i});
		for (BIT.init(n),i=1;i<=n;++i)
		for (auto [x,y]:evt[i]) if (y>0) BIT.add(x,y); else ans[-y]+=BIT.get(x);
		for (i=1;i<=q;++i) F.write(ans[i]," \n"[i==q]);
	}
	return F.flush(),0;
}

Postscript

接下来几天要忙CCPC Final的事情,估计没啥时间写题了的说

posted @ 2024-03-27 19:11  空気力学の詩  阅读(71)  评论(0编辑  收藏  举报