Codeforces Round 932 (Div. 2)

空気力学の詩發表於2024-03-13

Preface

這周開始晚課變得巨多,導致基本沒有時間自己寫題

眼看著這週五週六又有CF,趕緊把上週五的這場補了

這場感覺C有點詐騙了,我反正是去寫線段樹上二分了,不帶$\log $的做法還要動腦


A. Entertainment in MAC

簽到,可能的串就兩種,要麼保留原串;要麼reverse後再拼接原串

#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;
int t,n; string a;
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	ios::sync_with_stdio(false); cin.tie(0);
	for (cin>>t;t;--t)
	{
		cin>>n>>a; string tmp=a;
		reverse(tmp.begin(),tmp.end()); string b=tmp+a;
		cout<<min(a,b)<<endl;
	}
	return 0;
}

B. Informatics in MAC

首先不難發現如果分多個區間合法,那麼合併其中若干個區間後亦然合法,因此只要考慮分成兩個區間的情況

從小到大列舉每個數,用它在原序列中出現的位置來更新分界點的可能取值,當出現某個未出現的數時則找到解

#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],l[N],r[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=0;i<=n;++i) l[i]=n+1,r[i]=0;
		for (i=1;i<=n;++i) scanf("%d",&a[i]),l[a[i]]=min(l[a[i]],i),r[a[i]]=max(r[a[i]],i);
		int L=1,R=n-1; bool flag=0;
		for (i=0;i<=n;++i)
		{
			if (l[i]>n)
			{
				flag=1; printf("2\n%d %d\n%d %d\n",1,L,L+1,n); break;
			}
			L=max(L,l[i]); R=min(R,r[i]-1);
			if (L>R) break;
		}
		if (!flag) puts("-1");
	}
	return 0;
}

C. Messenger in MAC

首先一眼將二元組按照\(b_i\)排序,考慮當我們確定選取的下標位於區間\({L,R}\)時,後面的貢獻總是\(b_R-b_L\)

由於本題資料範圍允許\(O(n^2)\)列舉區間,因此確定端點後就是要在\([L+1,R-1]\)中找出最多數量的數使得它們的和小於某個值了

用權值線段樹隨便處理一下,詢問的話線上段樹上二分,總複雜度\(O(n^2\log n)\)

(PS:好像有基於單調性的\(O(n^2)\)DP做法,但懶得管了能過就行)

#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=2005;
int t,n,m; pi p[N]; vector <int> rst;
class Segment_Tree
{
	private:
		int sz[N<<2],sum[N<<2];
	public:
		#define TN CI now=1,CI l=0,CI r=rst.size()-1
		#define LS now<<1,l,mid
		#define RS now<<1|1,mid+1,r
		inline void init(TN)
		{
			sz[now]=sum[now]=0; if (l==r) return; int mid=l+r>>1; init(LS); init(RS);
		}
		inline void insert(CI pos,TN)
		{
			++sz[now]; sum[now]+=rst[pos]; if (l==r) return; int mid=l+r>>1;
			if (pos<=mid) insert(pos,LS); else insert(pos,RS);
		}
		inline int query(CI lim,TN)
		{
			if (l==r) return min(sz[now],lim/rst[l]); int mid=l+r>>1;
			if (lim<=sum[now<<1]) return query(lim,LS); else return sz[now<<1]+query(lim-sum[now<<1],RS);
		}
		#undef TN
		#undef LS
		#undef RS
}SEG;
signed main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%lld",&t);t;--t)
	{
		RI i,j; rst.clear(); int ans=0;
		for (scanf("%lld%lld",&n,&m),i=1;i<=n;++i)
		scanf("%lld%lld",&p[i].se,&p[i].fi),rst.push_back(p[i].se);
		sort(rst.begin(),rst.end()); rst.erase(unique(rst.begin(),rst.end()),rst.end());
		for (i=1;i<=n;++i) if (p[i].se<=m) ans=1;
		for (sort(p+1,p+n+1),i=1;i<n;++i)
		{
			if (p[i+1].fi-p[i].fi+p[i].se+p[i+1].se<=m) ans=max(ans,2LL);
			for (SEG.init(),j=i+1;j<n;++j)
			{
				SEG.insert(lower_bound(rst.begin(),rst.end(),p[j].se)-rst.begin());
				int tmp=p[j+1].fi-p[i].fi+p[i].se+p[j+1].se;
				if (tmp<=m) ans=max(ans,2LL+SEG.query(m-tmp));
			}
		}
		printf("%lld\n",ans);
	}
	return 0;
}

D. Exam in MAC

剛開始讀假題了感覺一點不可做,後面發現原來是又犯病了

考慮用容斥,所有二元組的總數為\(\frac{(c+1)\times (c+2)}{2}\),對於每個給定的\(s_i\),分別考慮\(y-x=s_i\)\(y+x=s_i\)的二元組\((x,y)\)數量,顯然前者有\(c+1-s_i\)個,後者有\(\lfloor\frac{s_i}{2}\rfloor +1\)

現在考慮要加回那些被統計了兩次的二元組數量,設存在二元組\((x,y)\)被統計了兩次,即\(M=y-x,N=y+x\)均在\(\{s_i\}\)中出現

注意到\(x=\frac{N-M}{2},y=\frac{N+M}{2}\),因此當\(M,N\)的奇偶性相同時,必然存在一組合法且唯一的\((x,y)\)與之對應

因此統計出\(\{s_i\}\)中奇/偶數個數\(odd,even\),答案再加上\(\frac{odd\times (odd+1)}{2}+\frac{even\times (even+1)}{2}\)即可

#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;
int t,n,c,x;
signed main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%lld",&t);t;--t)
	{
		RI i; int cnt[2]={0,0}; int dec=0;
		for (scanf("%lld%lld",&n,&c),i=1;i<=n;++i)
		scanf("%lld",&x),++cnt[x&1],dec+=(c+1-x)+(x/2+1);
		printf("%lld\n",(c+2)*(c+1)/2LL-dec+(cnt[0]+1)*cnt[0]/2LL+(cnt[1]+1)*cnt[1]/2LL);
	}
	return 0;
}

E. Distance Learning Courses in MAC

經典二進位制+DS的題,為什麼感覺這類題目就是出不完呢

首先考慮如果每個數選擇沒有下界怎麼做,不妨考慮二進位制下每一位在區間中出現的次數

顯然如果出現\(0/1\)次那麼這位的取值肯定是確定的,如果出現\(>1\)次則必然可以透過將其中一個數這一位改為\(0\),然後將小於該位的全部取為\(0\),這樣一定是最優的

現在考慮加上下界的限制該怎麼做,不難發現這其實就是限制了某些位是不能用上述的方式修改的

具體地,我們找到\(x,y\)二進位制下最高且不同的位,則對於這個數來說,這一段字首的值就固定了,而後面部分可以隨便取

因此把每個數固定的部分拆出來後,剩下的部分就轉化為沒有下界的問題了,可以從高位到低位貪心處理

注意到或運算允許查詢的時候區間有重疊,因此可以直接拿個RMQ維護下固定部分的貢獻,而每一位出現的次數可以用字首和處理

總複雜度\(O((n+q)\log 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 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;
int t,n,q,x,y,pfx[N][30];
namespace RMQ
{
	int f[N][20],_log[N];
	inline void init(void)
	{
		RI i,j; for (_log[0]=-1,i=1;i<=n;++i) _log[i]=_log[i>>1]+1;
		for (j=1;(1<<j)<=n;++j) for (i=1;i+(1<<j)-1<=n;++i)
		f[i][j]=f[i][j-1]|f[i+(1<<j-1)][j-1];
	}
	inline int query(CI l,CI r)
	{
		int k=_log[r-l+1]; return f[l][k]|f[r-(1<<k)+1][k];
	}
};
using namespace RMQ;
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	for (scanf("%lld",&t);t;--t)
	{
		RI i,j; for (scanf("%d",&n),i=1;i<=n;++i)
		{
			scanf("%d%d",&x,&y);
			if (x==y) f[i][0]=x,y=0; else
			{
				int k=__lg(x^y); f[i][0]=y;
				y&=((1<<k+1)-1); f[i][0]^=y;
			}
			for (j=0;j<30;++j) pfx[i][j]=pfx[i-1][j]+((y>>j)&1);
		}
		for (init(),scanf("%d",&q),i=1;i<=q;++i)
		{
			scanf("%d%d",&x,&y); int ans=0,res=query(x,y);
			for (j=29;j>=0;--j)
			{
				int bits=pfx[y][j]-pfx[x-1][j]+((res>>j)&1);
				if (bits>1) { ans|=((1<<j+1)-1); break; }
				else if (bits==1) ans|=(1<<j);
			}
			printf("%d%c",ans," \n"[i==q]);
		}
	}
	return 0;
}

Postscript

最近事情好多的說……

相關文章