博客园 首页 私信博主 显示目录 隐藏目录 管理 动画

Moscow Pre-Finals Workshop 2020 - Legilimens+Coffee Chicken Contest


Moscow Pre-Finals Workshop 2020 - Legilimens+Coffee Chicken Contest (XX Open Cup, Grand Prix of Nanjing)

CF GYM 102994

这场的题目来源主要是19年多校。


A. Everyone Loves Playing Games(线性基 带悔贪心)

线性基+带悔贪心,具体我先咕了

#include <bits/stdc++.h>

#define pb emplace_back
#define pii pair<int, int>
#define fir first
#define sec second
#define ll long long
using namespace std;

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

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

const int mod = 1e9+7;
inline int add(int a,int b){return a+b>=mod? a+b-mod: a+b;}
inline int sub(int a,int b){return a<b? a-b+mod: a-b;}
inline int mul(int a,int b){return 1LL*a*b%mod;}
int qpow(int a,int b){
    int ret=1;
    for(; b; b>>=1){
        if(b&1) ret=mul(ret,a);
        a=mul(a,a);
    }
    return ret;
}
inline int rev(int x){return qpow(x,mod-2);}

const int N = 1e4+10;
const int M = 60;
struct LB{
    ll b[65];
    void clear(){memset(b,0,sizeof(b));}
    void ins(ll x){
        for(int i=M; i>=0; --i){
            if((x>>i)&1){
                if(!b[i]) { b[i]=x; return;}
                x ^= b[i];
            }
        }
    }
} A, B;

void solve(){
    int n=read(), m=read();
    A.clear(), B.clear();
    ll X = 0;
    for(int i=1; i<=n; i++){
        ll x=readll(), y=readll();
        X ^= x; A.ins(x^y);
    }
    for(int i=1; i<=m; i++){
        ll x=readll(), y=readll();
        X ^= x; B.ins(x^y);
    }
    ll ans=X;
    for(int i=M; i>=0; --i){
        if(!A.b[i] && !B.b[i]) continue;
        if(A.b[i] && B.b[i]){
            if((ans>>i)&1) ans ^= A.b[i];
            A.ins(A.b[i]^B.b[i]);
        } 
        else if(A.b[i]) ans=max(ans, ans^A.b[i]);
        else if(B.b[i]) ans=min(ans, ans^B.b[i]);
    }
    printf("%lld\n",ans);
}
int main(){
    int T=read();
    while(T--) solve();
    return 0;
}

B. Gifted Composer(二分 Hash)

\(Description\)
初始有一个空字符串,\(n\)次操作,每次在串的前面或后面加一个字符,求每次操作后字符串的循环节种数。
\(s\)的循环节\(t\)定义为:\(s\)\(k\)\(t\)组成或由\(k\)\(t\)\(t\)的一个前缀组成。
\(n\leq 10^6\)

\(Solution\)
给定\(s\)和长度\(k\),判断\(s\)是否有上述循环节 等价于 判断是否有\(s[1,n-k]==s[k+1,n]\)。哈希即可\(O(1)\)判断。

有个性质是:长度为\(k\)的循环节会在\(k\)时刻出现,若它在\(k+t\)时刻消失,则以后不会再出现。
因为多一个字符导致\(k\)循环节消失后,再在前面或后面加字符,它也不会再成为循环节。
所以先对最终串哈希。枚举循环节长度\(k\),二分它消失的时刻\(k+t\),使\([k,k+t]\)的答案\(+1\)即可。

//655ms	46000KB
#include <bits/stdc++.h>
#define pc putchar
#define gc() getchar()
#define pb emplace_back
#define seed 31
typedef long long LL;
typedef unsigned long long ull;
const int N=1e6+6;

int sum[N],L[N],R[N];
ull hs[N*3],pw[N];
char s[N*3];

inline int read()
{
	int now=0,f=1; char c=gc();
	for(;!isdigit(c);c=='-'&&(f=-1),c=gc());
	for(;isdigit(c);now=now*10+c-48,c=gc());
	return now*f;
}
inline char Get()
{
	char c=gc(); while(!isalpha(c)) c=gc();
	return c;
}
inline ull Hash(int l,int r)
{
	return hs[r]-hs[l-1]*pw[r-l+1];
}
bool Check(int t,int k)
{
	return Hash(L[t],R[t]-k)==Hash(L[t]+k,R[t]);
}

int main()
{
	int n=read(),h=1e6+1,t=1e6+1;
	static char ss[5];
	for(int i=1; i<=n; ++i)
	{
		if(Get()=='a') scanf("%s",ss), s[t++]=ss[0]=='s'?ss[1]:ss[0];
		else scanf("%s",ss), s[--h]=ss[0]=='s'?ss[1]:ss[0];
		L[i]=h, R[i]=t-1;
	}

	pw[0]=1;
	for(int i=1; i<=n; ++i) pw[i]=pw[i-1]*seed;
	for(int i=h; i<t; ++i) hs[i]=hs[i-1]*seed+s[i]-'a';

	for(int i=1; i<=n; ++i)
	{
		int l=i,r=n,mid;
		while(l<r)
			if(Check(mid=l+r+1>>1,i)) l=mid;
			else r=mid-1;
		++sum[i], --sum[l+1];
	}
	for(int i=1; i<=n; ++i) sum[i]+=sum[i-1], printf("%d\n",sum[i]);

	return 0;
}

D. String Theory(后缀数组) √

做过的题,见这儿



G. Blackjack(概率DP 退背包)

\(Description\)
给定\(a,b,n\)\(n\)个数。从\(n\)个数中依次取一个数,直到当前取的数的和\(sum\gt a\)。若此时\(sum>b\)则输,否则赢。求\(n\)个数随机排列的情况下赢的概率。
\(n,a,b\leq 500\)

\(Solution\)
问题在于如何表示任意一种排列,感觉是个套路。
\(f[i][j][k]\)表示前\(i\)个数,选了其中\(j\)个,选的数的和为\(k\)的概率。则当前排列中含前\(i\)个数的\(j\)个数。所以:

\[f[i][j][k]=f[i-1][j][k]+f[i-1][j-1][k-A[i]]*\frac{j}{n-(j-1)} \]

注意要乘\(j\)!因为不乘\(j\)还是按顺序取的,不表示所有排列,乘\(j\)即在运算中乘了\(j!\),表示取了\(j\)个数的所有排列的情况。

注意这是个加法的背包,所以可以退背包,即:

\[f[i-1][j][k]=f[i][j][k]-f[i-1][j-1][k-A[i]]*\frac{j}{n-(j-1)} \]

枚举第\(i\)个数,令它作为最后选中的数,然后退背包使它的贡献从\(f[n]\)中删去。
那么\(A_i\)作为最后选中的数且合法的情况即为:

\[\sum_{j=0}^{n-1}\sum_{k,k<a\&k+A_i\in(a,b]}\frac{f'[j][k]}{n-j} \]

注意要除以\(n-j\)!即当前已选了\(j\)个限定\(i\)为最后一个的概率。
系数的细节也太容易漏了。

复杂度\(O(n^3)\)

PS:CF上除法还挺快的... 不需要存\(\frac1i\)的数组变成乘法。

//358ms	4000KB
#include <bits/stdc++.h>
#define pc putchar
#define gc() getchar()
#define pb emplace_back
typedef long long LL;
const int N=505;

int A[N];
double f[N][N],g[N][N];

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

int main()
{
	int n=read(),a=read(),b=read();
	for(int i=1; i<=n; ++i) A[i]=read();

	f[0][0]=1;
	for(int i=1,sum=0; i<=n; ++i)
	{
		sum=std::min(sum+A[i],b);
		for(int j=i,t=A[i]; j; --j)
			for(int k=sum; k>=t; --k)
				f[j][k]+=f[j-1][k-t]*j/(n-j+1);
	}
	double ans=0;
	for(int i=1; i<=n; ++i)
	{
		memcpy(g,f,sizeof f);
		for(int j=1,t=A[i]; j<n; ++j)
			for(int k=t; k<=b; ++k)
				g[j][k]-=g[j-1][k-t]*j/(n-j+1);
		for(int j=0,t=A[i]; j<n; ++j)
			for(int k=0; k<=a && k+t<=b; ++k)
				if(k+t>a) ans+=g[j][k]/(n-j);
	}
	printf("%.10f\n",ans);

	return 0;
}

I. Gaokao √

找规律题。

#include <bits/stdc++.h>

#define pb emplace_back
#define pii pair<int, int>
#define fir first
#define sec second
#define ll long long
using namespace std;

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

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

const int mod = 1e9+7;
inline int add(int a,int b){return a+b>=mod? a+b-mod: a+b;}
inline int sub(int a,int b){return a<b? a-b+mod: a-b;}
inline int mul(int a,int b){return 1LL*a*b%mod;}
int qpow(int a,int b){
    int ret=1;
    for(; b; b>>=1){
        if(b&1) ret=mul(ret,a);
        a=mul(a,a);
    }
    return ret;
}
inline int rev(int x){return qpow(x,mod-2);}

const int N = 1e3+10;
int fac[N], ifac[N];
void init(int n){
    fac[0]=1;for(int i=1; i<=n; i++) fac[i]=mul(fac[i-1], i);
    ifac[n]=rev(fac[n]); for(int i=n-1; i>=0; --i) ifac[i]=mul(ifac[i+1],i+1);
}
inline int C(int n,int m){
    return mul(fac[n], mul(ifac[m], ifac[n-m]));
}
int f[N][N];
void print(int x){
    for(int i=7; i>=0; --i) cout<<((x>>i)&1);
}

ll cal(ll x){
    ll ret=1;
    for(; x; x>>=1) if(x&1) ret<<=1;
    return ret;
}
int main(){
    int T=read();
    while(T--){
        printf("%lld\n",cal(readll()-1));
    }
    return 0;
}

L. Landlord(DFS) √

\(Description\)
给定无限大平面上的两个矩形,求它们将平面分成几个连通块。

\(Solution\)
对给定的\(4\)个点离散化,然后直接DFS求连通块数。
离散化的时候对所得下标乘\(2\)\(+1\)得到新下标,再标记\(vis=1\)会很方便。

//78ms	0KB
#include <bits/stdc++.h>
#define pc putchar
#define gc() getchar()
#define pb emplace_back
typedef long long LL;
const int N=20,Way[5]={1,0,-1,0,1};

int refx[N],refy[N],vis[N][N];
struct Point
{
	int x1,y1,x2,y2;
}A[2];

inline int read()
{
	int now=0,f=1; char c=gc();
	for(;!isdigit(c);c=='-'&&(f=-1),c=gc());
	for(;isdigit(c);now=now*10+c-48,c=gc());
	return now*f;
}
int Find(int *a,int x,int r)
{
	int l=1,mid;
	while(l<r)
		if(a[mid=l+r>>1]<x) l=mid+1;
		else r=mid;
	return l*2+1;
}
void DFS(int x,int y)
{
	vis[x][y]=1;
	for(int i=0; i<4; ++i)
	{
		int xn=x+Way[i],yn=y+Way[i+1];
		if(xn && yn && xn<N && yn<N && !vis[xn][yn]) DFS(xn,yn);
	}
}

int main()
{
	for(int T=read(); T--; )
	{
		for(int i=0; i<2; ++i) A[i]=(Point){read(),read(),read(),read()};
		int tx=0,ty=0;
		for(int i=0; i<2; ++i) refx[++tx]=A[i].x1, refx[++tx]=A[i].x2;
		for(int i=0; i<2; ++i) refy[++ty]=A[i].y1, refy[++ty]=A[i].y2;
		std::sort(refx+1,refx+1+tx), std::sort(refy+1,refy+1+ty);
		tx=std::unique(refx+1,refx+1+tx)-refx-1;
		ty=std::unique(refy+1,refy+1+ty)-refy-1;

		memset(vis,0,sizeof vis);
		for(int i=0; i<2; ++i)
		{
			int l1=Find(refx,A[i].x1,tx),r1=Find(refx,A[i].x2,tx);
			int l2=Find(refy,A[i].y1,ty),r2=Find(refy,A[i].y2,ty);
			for(int a=l1; a<=r1; ++a) vis[a][l2]=1, vis[a][r2]=1;
			for(int b=l2; b<=r2; ++b) vis[l1][b]=1, vis[r1][b]=1;
		}
		int res=0;
		for(int i=1; i<N; ++i)
			for(int j=1; j<N; ++j)
				if(!vis[i][j]) ++res, DFS(i,j);
		printf("%d\n",res);
	}

	return 0;
}

posted @ 2021-04-04 21:09  SovietPower  阅读(318)  评论(0编辑  收藏  举报