Moscow Pre-Finals Workshop 2020 - Legilimens+Coffee Chicken Contest (XX Open Cup, Grand Prix of Nanjing)
这场的题目来源主要是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
",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)的一个前缀组成。
(nleq 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
",sum[i]);
return 0;
}
D. String Theory(后缀数组) √
做过的题,见这儿。
G. Blackjack(概率DP 退背包)
(Description)
给定(a,b,n)和(n)个数。从(n)个数中依次取一个数,直到当前取的数的和(sumgt a)。若此时(sum>b)则输,否则赢。求(n)个数随机排列的情况下赢的概率。
(n,a,bleq 500)。
(Solution)
问题在于如何表示任意一种排列,感觉是个套路。
(f[i][j][k])表示前(i)个数,选了其中(j)个,选的数的和为(k)的概率。则当前排列中含前(i)个数的(j)个数。所以:
注意要乘(j)!因为不乘(j)还是按顺序取的,不表示所有排列,乘(j)即在运算中乘了(j!),表示取了(j)个数的所有排列的情况。
注意这是个加法的背包,所以可以退背包,即:
枚举第(i)个数,令它作为最后选中的数,然后退背包使它的贡献从(f[n])中删去。
那么(A_i)作为最后选中的数且合法的情况即为:
注意要除以(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
",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
",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
",res);
}
return 0;
}