1. 程式人生 > 其它 >美團杯 2021【雜題】

美團杯 2021【雜題】

感覺上還是去年好啊。

以下按照講題順序排列。

I 24點

Small Task:做 24 點。

網上隨便搜個程式碼下來跑。

Large Task:對於所有有解的 24 點題,求中間結果的最小值的最大值,和最大值的最小值。

用同樣的程式碼爆搜,上界是 \((13*13-1)/7=24\)\(r=169\),下界的話看到 Small Task 裡有一個 \(2/13\) 的,並且不太搜的出來更小的,直接交上去就過了。

K 杳瑤寺吳遙寺

給定整數 \(n\),求 \(\{1,1,4,5,1,4\}\times\infty\) 的最短字首 \(A_1,A_2,\cdots,A_m\) 的長度 \(m\)

,滿足存在把 \(A_1\otimes A_2\otimes\cdots\otimes A_m\) 中的 \(\otimes\) 替換為 \(+\)\(-\) 的方案,使得其值為 \(n\)

\(|n|\le 10^9\),Small Task:\(|n|\le 10^6\)

solution

考慮 dp 過程,\(f_{i,j}\) 表示前 \(i\) 個數是否能湊出 \(j\),發現狀態只用記 \(i\bmod 6\),變為類似 bfs 的過程。

\(|n|\) 太大的時候後就用一堆同符號的迴圈節。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int maxn=6666666,mod=998244353,add[]={1,1,4,5,1,4};
#define fi first
#define se second
#define PB push_back
#define MP make_pair
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
template<typename T>
inline void read(T &x){
	x=0;
	bool f=0;char ch=getchar();
	while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
	if(f) x=-x;
}
int n,h,r;
PII q[maxn];
map<int,int> f[6],s;
void solve(){
	read(n);
	if(abs(n)<=2000) printf("%d\n",s[n]);
	else if(n>=0) printf("%d\n",s[n%16+992]+(n-992)/16*6);
	else printf("%d\n",s[-(-n)%16-992]+(-992-n)/16*6);
}
int main(){
	f[1][1]=1;
	q[h=r=1]=MP(1,1);
	while(h<=r && r<=5e5){
		PII p=q[h++];
		int i=p.first,j=p.second;
		int ii=i+add[j],jj=(j+1)%6;
		if(!f[jj].count(ii)){
			f[jj][ii]=f[j][i]+1;
			q[++r]=MP(ii,jj);
		}
		ii=i-add[j],jj=(j+1)%6;
		if(!f[jj].count(ii)){
			f[jj][ii]=f[j][i]+1;
			q[++r]=MP(ii,jj);
		}
	}
	FOR(i,-2000,2000){
		int ans=1e9;
		FOR(j,0,5) if(f[j].count(i)) ans=min(ans,f[j][i]);
		s[i]=ans;
	}
	int T;
	read(T);
	while(T--) solve();
}

M 游泳

給定正整數 \(n,m,K\),求長為 \(m\)、和為 \(n\)、相鄰數字差 \(\le K\) 的正整數序列 \(A\) 的方差最大值。

solution

貪心地讓各個數差距儘量大即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int maxn=100010,mod=998244353;
#define fi first
#define se second
#define PB push_back
#define MP make_pair
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
template<typename T>
inline void read(T &x){
	x=0;
	bool f=0;char ch=getchar();
	while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
	if(f) x=-x;
}
int n,m,k,c[maxn];
void solve(){
	read(n);read(m);read(k);
	if(n<m) return puts("-1"),void();
	if(!k){
		if(n%m) puts("-1");
		else puts("0");
		return;
	}
	int nn=n;
	FOR(i,1,m) c[i]=1;
	n-=m;
	FOR(i,1,m){
		ll tmp=1ll*i*k;
		if(tmp>n || i==m){
			int d=n/i,r=n%i;
			FOR(j,1,i) c[j]+=d;
			ROF(j,i,i-r+1) c[j]++;
			break;
		}
		n-=tmp;
		FOR(j,1,i) c[j]+=k;
	}
	ll ans=0;
	FOR(i,1,m) ans+=1ll*c[i]*c[i];
	ans*=m;
	ans-=1ll*nn*nn;
	printf("%lld\n",ans);
} 
int main(){
	int T;
	read(T);
	while(T--) solve();
}

A 資料結構

給定長為 \(n\) 的正整數序列 \(a_i\)\(m\) 次詢問 \(l,r\),表示給 \(a[l:r]\) 加上 \(1\) 之後求全域性不同數的個數,並撤銷修改(即詢問之間互相獨立)

\(n,m\le 10^6\),Small Task:\(n\le 5000\)

solution

考慮每個數 \(i\) 不出現的條件:設 \(i\) 的出現位置是 \(b_{i,1},b_{i,2},\cdots,b_{i,c_i}\),則 \(l\le b_{i,1}\)\(r\ge b_{i,c_i}\)\([l,r]\) 不包含所有的 \(b_{i-1,j}\)

這是 \(O(n)\) 個矩形加,\(O(m)\) 個單點求值,掃描線+BIT 即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int maxn=1111111,mod=998244353;
#define fi first
#define se second
#define PB push_back
#define MP make_pair
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
template<typename T>
inline void read(T &x){
	x=0;
	bool f=0;char ch=getchar();
	while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
	if(f) x=-x;
}
int n,m,a[maxn],ql,ans[maxn];
vector<int> v[maxn];
struct ques{
	int x,l,r,tp;
	bool operator<(const ques &q)const{
		if(x!=q.x) return x<q.x;
		return abs(tp)>abs(q.tp);
	}
}q[maxn*5];
inline void add(int xl,int yl,int xr,int yr){
	q[++ql]=(ques){xl,yl,yr,1};
	q[++ql]=(ques){xr+1,yl,yr,-1};
}
int b[maxn];
inline void update(int p,int v){
	for(int i=p;i<=n;i+=i&-i) b[i]+=v;
}
inline int query(int p){
	int s=0;
	for(int i=p;i;i-=i&-i) s+=b[i];
	return s;
}
int main(){
	read(n);read(m);
	FOR(i,1,n) read(a[i]);
	FOR(i,0,n+1) v[i].PB(0);
	FOR(i,1,n) v[a[i]].PB(i);
	FOR(i,0,n+1) v[i].PB(n+1);
	FOR(i,1,n+1){
		int mn=1e9,mx=-1e9;
		FOR(j,1,(int)v[i].size()-2) mn=min(mn,v[i][j]),mx=max(mx,v[i][j]);
		if(mn==1e9){
			FOR(j,0,(int)v[i-1].size()-2)
				add(v[i-1][j]+1,v[i-1][j]+1,v[i-1][j+1]-1,v[i-1][j+1]-1);
		}
		else{
			int mn2=1e9,mx2=-1e9;
			bool flag=false;
			FOR(j,0,(int)v[i-1].size()-1){
				int x=v[i-1][j];
				if(x>=mn && x<=mx){flag=true;break;}
				if(x<mn) mx2=max(mx2,x);
				if(x>mx) mn2=min(mn2,x);
			}
			if(flag) continue;
			add(mx2+1,mx,mn,mn2-1);
		}
	}
	FOR(i,1,m){
		int l,r;
		read(l);read(r);
		q[++ql]=(ques){l,r,i,0};
	}
	sort(q+1,q+ql+1);
	FOR(i,1,ql){
		if(q[i].tp){
			update(q[i].l,q[i].tp);
			update(q[i].r+1,-q[i].tp);
		}
		else{
			ans[q[i].r]=query(q[i].l);
		}
	}
	FOR(i,1,m) printf("%d\n",n+1-ans[i]);
}

C 查查查樂樂II

給定正整數 \(n\)\(\forall k\in[1,n]\),求 (長度,字典序) 最小的 \(\texttt{xl}\) 字串,滿足 \(\texttt{xxxll}\) 的個數為 \(k\)

\(n=10^5\),Small Task:\(n=100\)

solution

考慮對一個字串求 \(\texttt{xxxll}\) 的個數:列舉第 \(3\)\(\texttt x\),設左邊有 \(a\)\(\texttt x\),右邊有 \(b\)\(\texttt l\),貢獻是 \(\binom a2\binom b2\)

考慮 dp:列舉總共有多少個 \(\texttt l\),然後 \(f_{i,j,k}\) 表示當前有 \(i\)\(\texttt x\),之後有 \(j\)\(\texttt l\),已有 \(k\)\(\texttt{xxxll}\) 是否可行。

跑一下發現串長 \(\le 37\),所以 \(i,j\) 都只有 \(37\)。複雜度 \(O(37^3n/w)\)

#include<bits/stdc++.h>
using namespace std;
const int N = 100003, M = 31, INF = 0x3f3f3f3f;
int n, coe[M][M], len[N];
bitset<N> f[M][M];
bool str[M<<1], ans[N][M<<1];
int main(){
	scanf("%d", &n);
	memset(len, 0x3f, sizeof len);
	for(int i = 2;i < M;++ i)
		for(int j = 2;j < M;++ j)
			coe[i][j] = i*(i-1)*j*(j-1)>>2;
	for(int px = 3;px < M;++ px){
		for(int x = 0;x <= px;++ x)
			for(int l = 0;l < M;++ l) f[x][l].reset();
		f[0][0].set(0);
		for(int x = 0;x <= px;++ x)
			for(int l = 0;l < M;++ l){
				if(x < px) f[x+1][l] |= f[x][l]<<coe[px-x-1][l];
				if(l+1 < M) f[x][l+1] |= f[x][l];
			}
		for(int i = 1;i <= n;++ i){
			int pl = INF;
			for(int j = 0;j < M;++ j)
				if(f[px][j][i]){pl = j; break;}
			if(px + pl > len[i]) continue;
			int x = px, l = pl, cur = i, ha = 0;
			while(x || l)
				if(l && f[x][l-1][cur]){-- l; str[ha++] = 0;}
				else {cur -= coe[px-x][l]; -- x; str[ha++] = 1;}
			if(ha < len[i]){
				memcpy(ans[i], str, ha);
				len[i] = ha;
			} else {
				for(int j = 1;j <= ha;++ j)
					if(ans[i][j] > str[j]){
						memcpy(ans[i], str, ha); break;
					} else if(ans[i][j] < str[j]) break;
			}
		}
	}
	for(int i = 1;i <= n;++ i){
		if(len[i] == INF){puts("-1"); continue;}
		for(int j = 0;j < len[i];++ j)
			putchar(ans[i][j] ? 'x' : 'l');
		putchar('\n'); 
	}
}

H 哈利波特

這是一道提交答案題

給定長為 \(4807976\) 的小寫字串 \(s\)\(370103\) 個英文單詞的字典 \(D\)

定義串 \(s\) 對字典 \(D\) 的分詞代價 \(f(D,s)\) 是將 \(s\) 最小劃分子串個數,使得每個子串都是 \(D\) 中的單詞。

定義 \(D\) 中單詞 \(w\) 的阿瓦達指數是 \(s\)\(D\backslash\{w\}\) 的分詞代價。

求所有長度 \(>1\) 的單詞 \(w\) 的 (阿瓦達指數,字典序) 的前 \(K\) 大值。

\(K=200\),Small Task:\(K=2\)

solution 完全不會,被智商碾壓了(

暴力方法是把 \(f(D,s)\) 中用到的單詞 \(w\) 取出來跑。

正解是估價函式:設 \(\text{way}(w)\)\(f(D,s)\)\(w\) 的出現次數,則 \(f(D\backslash\{w\},s)\le f(D)+\text{way}(w)(f(D\backslash\{w\},w)-1)\),只有在這個值 \(\ge\) 當前第 \(K\) 大時才做。

經測試,直接按照給的字典跑需要判 \(1933\) 個詞,執行時間 \(419\text s\),如果先把單詞按 (長度,字典序) 排序就只用判 \(1626\) 個詞,執行時間 \(322\text s\),稍微優了一點,但前者判出來的很多都是認識的單詞,可以更方便地提取出一篇文章中的常用單詞。有點好玩,想再找一篇文章看看

#include<bits/stdc++.h>
#define PB emplace_back
#define MP make_pair
#define fi first
#define se second 
using namespace std;
typedef pair<int, int> pii;
const int N = 4808000, K = 370103;
template<typename T>
bool chmin(T &a, const T &b){if(a > b) return a = b, 1; return 0;}
FILE *f1 = fopen("dict.in", "r"), *f2 = fopen("harry-potter.txt", "r");
int n, ch[N][26], tot, len[K+5], id[N], dp[N], way[K+5], pre[N], all;
char str[N], ss[K+5][33];
int work(int n, int k, char *str){
	memset(dp, 0x7f, n+1<<2);
	dp[0] = 0;
	for(int i = 0;i < n;++ i)
		for(int j = i+1, u = 0;j <= n;++ j){
			u = ch[u][str[j]-'a'];
			if(!u) break;
			if(id[u] && id[u] != k) chmin(dp[j], dp[i] + 1);
		}
	return dp[n];
}
bool cmp(const pii &a, const pii &b){
	if(a.fi != b.fi) return a.fi > b.fi;
	return strcmp(ss[a.se]+1, ss[b.se]+1) < 0;
}
struct Cmp {bool operator()(const pii &a, const pii &b){return cmp(a, b);}};
priority_queue<pii, vector<pii>, Cmp> pq;
vector<pii> ans;
int main(){
	fscanf(f2, "%s", str+1);
	n = strlen(str+1);
	for(int i = 1;i <= K;++ i){
		fscanf(f1, "%s", ss[i]+1);
		int u = 0;
		len[i] = strlen(ss[i]+1);
		for(int j = 1;j <= len[i];++ j){
			int c = ss[i][j]-'a';
			if(!ch[u][c]) ch[u][c] = ++tot;
			u = ch[u][c];
		}
		id[u] = i;
	}
	memset(dp, 0x7f, n+1<<2);
	dp[0] = 0;
	for(int i = 0;i < n;++ i)
		for(int j = i+1, u = 0;j <= n;++ j){
			u = ch[u][str[j]-'a'];
			if(!u) break;
			if(id[u] && chmin(dp[j], dp[i] + 1)) pre[j] = id[u];
		}
	all = dp[n];
	for(int i = n;i;i -= len[pre[i]]) ++ way[pre[i]];
	for(int i = 1;i <= K;++ i)
		if(len[i] > 1 && (pq.size() < 200 || cmp(MP(all + way[i]*(work(len[i],i,ss[i])-1), i), pq.top()))){
			pq.push(MP(work(n,i,str), i));
			if(pq.size() > 200) pq.pop();
			printf("ss[%d] = %s\n", i, ss[i]+1);
		}
	while(!pq.empty()){ans.PB(pq.top()); pq.pop();}
	reverse(ans.begin(), ans.end());
	for(pii _ : ans) printf("%s %d\n", ss[_.se]+1, _.fi);
}

E 程式解謎II

C++ 程式碼拼圖。

solution

跟去年某道題差不多,憑藉對競賽程式碼的理解拼一拼就可以了。

注意拼的時候不要把碎片放在一起了,不然錯了很難改(

Large Task 需要用樣例算表,不是很難,根據程式碼中的計算部分反推一下即可。

#include<cstdio>
using namespace std;
namespace Sub2 {
int clude[50] = {518020025,227984854,990919605,760559275,252747709,351267635,436520588,849336757,847045033,785731263,491533093,243893699,119202559,255782057,101925721,153701673,19279237,757203511,602780864,17233756,503674646,198732600,529032347,789861212,282845866,618483948,252931964,585966855,47548815,458761589,580505477,569964015,505577677,411118499,788281248,963908046,733289631,512853327,257612428,669701279,74200836,267681712,565463498,475616488,358569984,846564286,362377870,890855192,553545630,323150357};
int n; 
int v[50], tmp;
int maine(bool nclude){
	int mai = 0;
	for(int i = 0;i < n;++ i){
		if((i&1)^nclude){
			tmp = v[i]^clude[i];
		} else {
			tmp = ((1<<30)-1)^v[i]^clude[i];
		}
		if(tmp > mai) mai = tmp;
	}
	return mai;
}
void main(){
	int t;
	scanf("%d%d", &t, &n);
	for(int i = 0;i < t;++ i){
		for(int j = 0;j < n;++ j)
			scanf("%d", v + j);
		printf("%d\n", maine(i&1));
	}
}
}
namespace Sub1 {
int t;
int solve1(int n){
	t = 0;
	for(int i = 1;i <= n;++ i)
		if(n % i == 0) t += 1;
	return t;
}
int solve2(int n){
	t = 0;
	for(int i = 2;i <= n;++ i)
		if(n % i == 0){t += i; while(n % i == 0) n /= i;}
	return t;
}
void main(){
	int t;
	scanf("%d", &t);
	while(t --){
		int n;
		scanf("%d", &n);
		printf("%d\n", solve1(n)+solve2(n));
	}
}
}
int main(){
	int sub;
	scanf("%d", &sub);
	if(sub == 1) Sub1 :: main();
	else Sub2 :: main();
}

F 面向物件

給定如下的殘缺 C++ 程式碼,求將 /*MissingModifier*/ 替換為 privateprotectedpublic/*MissingMethod*/ 替換為 method#Num 的方案數,使得編譯通過。

class Class1 {
  void method1() {
    /*MissingMethod*/();
  }
  /*MissingModifier*/:
  void method2() {}
};
class Class2: /*MissingModifier*/ Class1 {
  /*MissingModifier*/:
  void method3() {}
  /*MissingModifier*/:
  void method4() {}
};
int main() {
  Class2 o2;
  o2./*MissingMethod*/();
}

保證類的繼承關係是樹。類有 \(300\) 個,函式有 \(1000\) 個。

solution

咕了

J 隨機數

這是一道互動題

給定一個位運算 random device,你可以呼叫它 \(2000\) 次,然後 \(100\) 次模擬出它跑 \(x\) 次的結果。

全域性變數 \(\le 3\) 個。

solution

考場上看錯題了,捱打(

只要看對題就會發現,如果把全域性變數看做 \(192\) 位向量,這個東西就是線性變換。

根據 Cayley-Hamilton 定理,它有 \(192\) 階齊次線性遞推式,用多項式取模的方法做即可。

懶得拉 BM 板子了,求線性遞推式可以暴力高消,複雜度 \(O(w^3+Qw\log k)\)

#include"interactor.h"
#include"bits/stdc++.h"
using namespace std;
typedef unsigned long long u64;
namespace {
const int N = 193, M = N<<1;
u64 a[2000];
bitset<N> f[N], g;
bitset<M> A[30], cur, dif;
int m;
bitset<M> mul(const bitset<M> &a, const bitset<M> &b){
	bitset<M> c;
	for(int i = 0;i < m;++ i) if(a[i]) c ^= b<<i;
	for(int i = m-1;~i;-- i) if(c[i+m]) c ^= dif<<i;
	return c;
}
void work(int k){
	for(int i = 29;~i;-- i) if(k>>i&1) cur = mul(cur, A[i]);
}}
void solve(int ifYouDontKnowHowToNameAParameterThenYouUseIt){
	for(int i = 0;i < 2000;i ++) a[i] = random_ull();
	for(int i = 0;i < N;++ i)
		for(int _ = 0;_ < 64;++ _){
			for(int j = 0;j < N;++ j)
				g[j] = a[i+j]>>_&1;
			for(int j = 0;j < N;++ j) if(g[j]){
				if(f[j][j]) g ^= f[j];
				else {f[j] = g; break;}
			}
		}
	for(int i = N-1;~i;-- i)
		if(f[i][i]){
			bool tmp = 0;
			for(int j = i+1;j < N;++ j)
				tmp ^= dif[j] && f[i][j];
			dif[i] = tmp;
		} else dif[i] = 1;
	for(m = N-1;!dif[m];-- m);
	A[0][1] = 1; cur[0] = 1;
	for(int i = 0;i < 29;++ i) A[i+1] = mul(A[i], A[i]);
	work(1999);
}
u64 query(int k){
	work(k); u64 res = 0;
	for(int i = 0;i < m;++ i) if(cur[i]) res ^= a[i];
	return res;
}

L 挑戰出題人

構造在下圖的 0 替換為 \([x,3]\) 中的整數,使得 loopy 有唯一解。

0   0   000 
00 00  0   0
0 0 0  0   0
0 0 0  0000 
0 0 0  0    
0   0  0    
            
0  0   00000
0 0      0  
00   0 0 0  
0 0  0 0 0  
0  0 000 0  

\(x=1\),Small Task:\(x=0\)


Small Task:顯然讓答案非常小即可。

Large Task:沒聽懂,掉線了。

G 字串匹配

這是一道互動題

互動器有 \(\texttt{01}\)\(a\),給定 \(\texttt{01}\)\(b\),你每次可以詢問帶萬用字元的串 \(s\) 和兩個下標 \(0\le l\le r\le|a|-|s|\),互動器告訴你最小和最大的滿足 \(a[i:i+|s|-1]\)\(s\) 匹配的下標 \(i\),代價是 \(C+(r-l+1)w(s)\),其中 \(w(s)\)\(s\) 中非萬用字元個數,要求代價總和 \(\le S\)

\(b\) 是否是 \(a\) 的子串,如果是,求下標 \(i\) 使得 \(a[i:i+|s|-1]=b\)

\(|a|=75000\)\(|b|=50000\)\(C=30000\)\(S=4\cdot 10^6\)


咕咕咕咕。