C
题面
思路
题目要求我们在满足字典序最大的同时有更多的数字;
不难想到,
m
e
x
(
1
,
n
)
mex(1,n)
mex(1,n)肯定能得到最大的mex
;
现在我们需要找到一个 k k k,使得这个 k k k尽可能小的同时满足 m e x ( 1 , k ) = m e x ( 1 , n ) mex(1,k) = mex(1,n) mex(1,k)=mex(1,n);
完成了这一步以后,只需要循环的执行 [ k + 1 , n ] [k+1,n] [k+1,n]这一部分即可;
基于这个思路,我们维护一个前缀、一个后缀;
前缀不断增加,后缀不断减少,直到找到这个 k k k为止;
然后我们就可以写出这样的代码(这份会TLE)
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <set>
using namespace std;
typedef long long ll;
const int N = 2e5 + 10;
struct Mex{
int cnt[N];//cnt(i)表示数字i出现的次数
set<int> st;//记录未出现的数字
multiset<int> mulst;//记录出现过的数字
Mex(){
for(int i=0;i<N;++i) cnt[i] = 0;
for(int i=0;i<N;++i) st.insert(i);
}
void add(int x){
//第一次添加
if(cnt[x] == 0){
st.erase(x);
}
++cnt[x];
mulst.insert(x);
}
void del(int x){
//只剩一个了
if(cnt[x] == 1){
st.insert(x);
}
--cnt[x];
//不能写成mulst.erase(x) 这样是删除所有值为x的元素
mulst.erase(mulst.find(x));
}
int mex(){
return *st.begin();
}
void clear(){
while(!mulst.empty()){
del(*mulst.begin());
}
}
};
int a[N];
void solve(){
int n;
cin >> n;
Mex prefix,suffix;
for(int i=1;i<=n;++i){
cin >> a[i];
suffix.add(a[i]);
}
vector<int> ans;
int mex = suffix.mex();
for(int i=1;i<=n;++i){
prefix.add(a[i]);
suffix.del(a[i]);
if(prefix.mex() == mex){
ans.push_back(mex);
mex = suffix.mex();
prefix.clear();
}
}
cout << ans.size() << '\n';
for(auto x : ans){
cout << x << ' ';
}
cout << '\n';
}
signed main(){
std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int t;
cin >> t;
while(t--)
solve();
return 0;
}
我们发现前缀
p
r
e
f
i
x
prefix
prefix除了clear
以外用不到del
;
同时我们可以预处理出每个位置suffix
的mex
;
那么我们就不需要del
了,这样可以节省很多时间;并且我们可以将set
换成一个vector
;
AC_Code
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <set>
using namespace std;
typedef long long ll;
const int N = 2e5 + 10;
struct Mex{
int cnt[N];//cnt(i)表示数字i出现的次数
vector<int> ve;//记录进来的数
int now = 0;//mex 因为这里只有add 因此只会增加
Mex(){
memset(cnt,0,sizeof cnt);
ve.clear();
}
void add(int x){
ve.push_back(x);
++cnt[x];
}
void clear(){
now = 0;
for(auto x : ve) --cnt[x];
ve.clear();
}
int mex(){
while(cnt[now]) ++now;
return now;
}
};
int a[N];
int suffix[N];
void solve(){
int n;
cin >> n;
Mex prefix,tmp;
for(int i=1;i<=n;++i){
cin >> a[i];
}
for(int i=n;i>=1;--i){
tmp.add(a[i]);
suffix[i] = tmp.mex();
}
vector<int> ans;
int mex = suffix[1];
for(int i=1;i<=n;++i){
prefix.add(a[i]);
if(prefix.mex() == mex){
ans.push_back(mex);
mex = suffix[i+1];
prefix.clear();
}
}
cout << ans.size() << '\n';
for(auto x : ans){
cout << x << ' ';
}
cout << '\n';
}
signed main(){
std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int t;
cin >> t;
while(t--)
solve();
return 0;
}
D
题面
思路
假设最终拼接得到是字符串是个回文串;
那么我们去掉回文串的中间一部分,得到的也是回文串;
因为给我们的字串长度至多为3;
我们分以下情况讨论;
- 长度为1,那么肯定是回文串了;
- 长度为2,比如
ab
,我们需要找到ba
,无论拼前还是拼后,即abba
还是baab
都是回文的; - 长度为3,和上一条一样,最直接的就是找一个长度为3的来拼接;或者找长度为2的来拼接,这里拼前和拼后不一样,自己拿个
abc
来模拟即可;当然可以找长度为1的,但是我为啥不直接YES;
Code
#include <iostream>
#include <cstring>
#include <cstdio>
#include <string>
#include <map>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
bool solve(){
int n;
cin >> n;
map<string,vector<int>> mp;
for(int i=1;i<=n;++i){
string s;
cin >> s;
mp[s].push_back(i);
}
for(auto x : mp){
if(x.first.size() == 1) return 1;
if(x.first.size() == 2){
auto y = x.first;
reverse(y.begin(),y.end());
if(mp.count(y)) return 1;
}
else{
auto s1 = x.first.substr(0,2);
auto s2 = x.first.substr(1,2);
auto s3 = x.first;
reverse(s1.begin(),s1.end());
reverse(s2.begin(),s2.end());
reverse(s3.begin(),s3.end());
if(mp.count(s3)) return 1;
if(mp.count(s1) && mp[s1].back() > mp[x.first].front()) return 1;
if(mp.count(s2) && mp[s2].front() < mp[x.first].back()) return 1;
}
}
return 0;
}
signed main(){
std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int t;
cin >> t;
while(t--){
bool f = solve();
if(f) cout << "YES\n";
else cout << "NO\n";
}
return 0;
}
E
题面
思路
因为题目只让我们求所有格子异或起来的结果,那么我们肯定不必去构造/还原每个格子是什么;
输入的方阵中每个元素是周围四个元素的异或和;
要想得到全部元素的异或和;
有两种思路;
- 异或起来的元素出现次数为奇数次且不遗漏;
- 异或起来的元素出现次数为1次且不重不漏;
我手推了 2 ∗ 2 2*2 2∗2和 4 ∗ 4 4*4 4∗4的方阵;发现第二种思路比较容易实现;
一定是一次选定两个连续的元素,为1表示选定,为0表示不选;
然后代码的话,发现一个个格子枚举来填充就行,因为是连续的嘛,没必要一次填充两个格子;
Code
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 1e3 + 10;
bool st[N][N];
int a[N][N];
int dx[] = {1,-1,0,0};
int dy[] = {0,0,1,-1};
void solve(){
memset(st,0,sizeof st);
int ans = 0;
int n;
cin >> n;
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
cin >> a[i][j];
for(int i=1;i<=n;++i){
for(int j=1;j<=n;++j){
bool flag = 0;
for(int k=0;k<4;++k){
int xx = i + dx[k];
int yy = j + dy[k];
if(st[xx][yy]) flag = 1;
}
if(!flag){
ans ^= a[i][j];
for(int k=0;k<4;++k){
int xx = i + dx[k];
int yy = j + dy[k];
st[xx][yy] = 1;
}
}
}
}
cout << ans << '\n';
}
signed main(){
std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int t;
cin >> t;
while(t--)
solve();
return 0;
}
F
题面
思路
Code
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 2e3 + 10;
const int MOD = 1e9+7;
#define int ll
int f[N][N];
int qpow(int x,int y){
int base = x,ret = 1;
while(y){
if(y&1){
ret *= base;
ret %= MOD;
}
base *= base;
base %= MOD;
y>>=1;
}
return ret;
}
void solve(){
int n,m,k;
cin >> n >> m >> k;
for(int i=1;i<=m;++i) f[i][i] = k*i % MOD,f[i][0] = 0;
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
if(i != j) f[i][j] = ((f[i-1][j]+f[i-1][j-1])*qpow(2,MOD-2))%MOD;
cout << f[n][m] << '\n';
}
signed main(){
std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int t;
cin >> t;
while(t--)
solve();
return 0;
}
F2
思路
是 C ( n − 1 − i , m − i ) C(n-1-i,m-i) C(n−1−i,m−i)而不是 C ( n − i , m − i ) C(n-i,m-i) C(n−i,m−i),我个人理解是从 f ( i − 1 , j − 1 ) f(i-1,j-1) f(i−1,j−1)斜着走过来;
因此贡献不应该算到与 n n n同行,应该到 n − 1 n-1 n−1才对;
Code
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 1e6 + 10;
const int MOD = 1e9+7;
#define int ll
int fact[N],infact[N];
int qpow(int x,int y){
int base = x,ret = 1;
while(y){
if(y&1){
ret *= base;
ret %= MOD;
}
base *= base;
base %= MOD;
y>>=1;
}
return ret;
}
int C(int n,int m){
ll ret = (fact[n] * infact[m])%MOD * infact[n-m];
return ret % MOD;
}
void init(){
fact[0] = infact[0] = 1;
for(int i=1,inv;i<N;++i){
fact[i] = (fact[i-1] * i)%MOD;
inv = qpow(i,MOD-2);
infact[i] = (infact[i-1] * inv)%MOD;
}
}
int inv_2 = 500000004;
//typedef __int128 int128;
void solve(){
int n,m,k;
cin >> n >> m >> k;
if(n == m) cout << k * n%MOD << '\n';
else{
int ans = 0;
for(int i=1;i<=m;++i){
ans %= MOD;
//f(i,i) * (2^{n-i})^-1 * C(n-1-i,m-i);
ans += (i%MOD) * qpow(inv_2,n-i) %MOD
* C(n-1-i,m-i) % MOD;
}
cout << ans*k%MOD << '\n';
}
}
signed main(){
//cout << qpow(2,MOD-2) << '\n'; 500000004
std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
init();
int t;
cin >> t;
while(t--)
solve();
return 0;
}