2020牛客暑期多校训练营(第六场)
目录
Contest Info
Solved | A | B | C | D | E | F | G | H | I | J | K |
---|---|---|---|---|---|---|---|---|---|---|---|
7 / 13 | O | O | O | - | O | - | - | O | - | O | O |
- O 在比赛中通过
- Ø 赛后通过
- ! 尝试了但是失败了
- - 没有尝试
Solutions
A. African Sort
题意:
给定一个排列,每次选择一个子序列随机对他们进行排序。
问最后使得这个排列为\(1,2,...,n\)的期望次数为多少。
思路:
是个假题。。说一下AC的思路吧。
比较直觉的想法是,每次对一个环进行操作最优。那么操作一个环过后就会产生若干个环。
定义\(f(i)\)表示将长度为\(i\)的环归位的期望次数,那么考虑对这个环进行操作的所有情况,直接枚举所有情况环的拆分比较困难,所以就考虑每个长度的环的贡献。
- 对于一个长度为\(n\)的排列,其全排列中产生大小为\(i\)的环的个数为\(\frac{n!}{i}\)。
那么根据上式推一推就好了。
B. Binary Vector
线性代数题,每次会有\(2^n-2^i\)个线性无关的,然后推一推就好了。
C. Combination of Physics and Maths
每次选择一列最优,所以直接枚举即可。
容易证明选择多列不如选择一列优。
E. Easy Construction
当\(n\)为奇数,则类似\(\{n,1,n-1,2,n-2,\cdots\}\)构造即可。
当\(n\)为偶数,则类似\(\{n,n/2,1,n-1,2,n-2,\cdots\}\)构造即可。
H. Harmony Pairs
数位dp模板题...
Code
#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define mp make_pair
#define pb push_back
#define LC k<<1
#define RC k<<1|1
typedef long long LL;
const int N=110;
const int M=1100000;
const LL mod=1e9+7;
LL dp[110][2][2][2100];
char s[N];
int a[N],n;
LL ans;
LL dfs(int len,int cur,int less,int delta)
{
if (len==0)
{
if (delta<1000) dp[len][cur][less][delta]=1;
else dp[len][cur][less][delta]=0;
return dp[len][cur][less][delta];
}
if (dp[len][cur][less][delta]!=-1) return dp[len][cur][less][delta];
LL res=0;
for (int i=0;i<10;i++)
{
if (cur&&i>a[len]) break;
for (int j=0;j<10;j++)
{
if (less==0&&j>i) break;
int ncur=(cur&&i==a[len]);
int nless=less||(j<i);
res=(res+dfs(len-1,ncur,nless,delta+i-j))%mod;
}
}
return dp[len][cur][less][delta]=res;
}
int main()
{
memset(dp,-1,sizeof(dp));
scanf("%s",s+1);
n=strlen(s+1);
reverse(s+1,s+1+n);
for (int i=1;i<=n;i++)
a[i]=s[i]-'0';
ans=dfs(n,1,0,1000);
cout<<ans<<endl;
return 0;
}
J. Josephus Transform
通过线段树/树状数组求出初始排列,然后直接快速幂即可。
后面也有\(O(n)\)的做法,就先求出环然后直接算。
Code
#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define mp make_pair
#define pb push_back
#define LC k<<1
#define RC k<<1|1
typedef unsigned long long LL;
const int N=110000;
const int M=1100000;
int n,m;
int a[N],b[N],sum[M],c[N];
void build(int k,int l,int r)
{
if (l==r)
{
sum[k]=1;
return;
}
int mid=(l+r)/2;
build(LC,l,mid);
build(RC,mid+1,r);
sum[k]=sum[LC]+sum[RC];
}
int pre[N],nxt[N];
int find(int k,int l,int r,int x)
{
if (l==r) return 1;
int mid=(l+r)/2;
if (x<=mid) return find(LC,l,mid,x);
else return sum[LC]+find(RC,mid+1,r,x);
}
void dec(int k,int l,int r,int x)
{
if (l==r)
{
sum[k]--;
return;
}
int mid=(l+r)/2;
if (x<=mid) dec(LC,l,mid,x);
else dec(RC,mid+1,r,x);
sum[k]=sum[LC]+sum[RC];
}
int find2(int k,int l,int r,int x)
{
if (l==r) return l;
int mid=(l+r)/2;
if (sum[LC]>=x) return find2(LC,l,mid,x);
else return find2(RC,mid+1,r,x-sum[LC]);
}
int pp[N],tmp[N],cp[N];
int main()
{
scanf("%d %d",&n,&m);
for (int i=1;i<=n;i++)
a[i]=i;
while (m--)
{
for (int i=1;i<=n;i++)
b[i]=i,pre[i]=i-1,nxt[i]=i+1;
nxt[n]=1,pre[1]=n;
int k,x;
scanf("%d %d",&k,&x);
build(1,1,n);
int cur=1,len=n;
for (int i=1;i<=n;i++)
{
int tmp=(k-1)%len;
int pos=find(1,1,n,cur);
int left=len-pos;
if (tmp<=left) pos=find2(1,1,n,pos+tmp);
else pos=find2(1,1,n,tmp-left);
cur=nxt[pos];
nxt[pre[pos]]=pre[pos];
pre[nxt[pos]]=nxt[pos];
b[i]=pos;
dec(1,1,n,pos);
len--;
}
for (int i=1;i<=n;i++)
pp[i]=0;
for (int i=1;i<=n;i++)
{
if (pp[i]) continue;
int now=i,len=0;
while (!pp[now])
{
tmp[len++]=now;
pp[now]=1;
now=b[now];
}
int pos=x%len;
int cur=0;
for (int j=0;j<len;j++)
cp[j]=0;
for (int j=0;j<len;j++)
{
if (!cp[j])
{
cur=j;
cp[cur]=1;
while (!cp[(pos+cur)%len])
{
cp[(pos+cur)%len]=1;
int x=tmp[cur],y=tmp[(pos+cur)%len];
swap(a[x],a[y]);
cur=(cur+pos)%len;
}
}
}
}
}
for (int i=1;i<=n;i++)
{
printf("%d",a[i]);
if (i!=n) printf(" ");
else printf("\n");
}
return 0;
}
K. K-Bag
我们将序列划分为三段,注意某一段可能为\(0\)。
首先把\(k\geq n\)的情况判掉,之后注意到枚举起点只可能有\(O(k)\)个位置,那么通过hash\(O(1)\)判断一个区间里是否存在\(1\)~\(k\)即可。
算法总的时间复杂度为\(O(nlogn)\)的,瓶颈在于预处理。
Code
// Author : heyuhhh
// Created Time : 2020/07/27 13:24:29
#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
void err(int x) {cerr << x;}
void err(long long x) {cerr << x;}
void err(double x) {cerr << x;}
void err(char x) {cerr << '"' << x << '"';}
void err(const string &x) {cerr << '"' << x << '"';}
void _print() {cerr << "]\n";}
template<typename T, typename V>
void err(const pair<T, V> &x) {cerr << '{'; err(x.first); cerr << ','; err(x.second); cerr << '}';}
template<typename T>
void err(const T &x) {int f = 0; cerr << '{'; for (auto &i: x) cerr << (f++ ? "," : ""), err(i); cerr << "}";}
template <typename T, typename... V>
void _print(T t, V... v) {err(t); if (sizeof...(v)) cerr << ", "; _print(v...);}
#ifdef Local
#define dbg(x...) cerr << "[" << #x << "] = ["; _print(x)
#else
#define dbg(x...)
#endif
//head
const int N = 5e5 + 5;
const int P[] = {998244353};
#define ull unsigned long long
#define rep(ii,a,b) for(int ii=a;ii<=b;++ii)
#define forn(i, n) for(int i = 0; i < n; ++i)
#define for1(i, n) for(int i = 1; i <= n; ++i)
#define FI(n) FastIO::read(n)
#define FO(n) FastIO::write(n)
#define Flush FastIO::Fflush()
namespace FastIO {
const int SIZE = 1 << 16;
char buf[SIZE], obuf[SIZE], str[60];
int bi = SIZE, bn = SIZE, opt;
double D[] = {0.1, 0.01, 0.001, 0.0001, 0.00001, 0.000001, 0.0000001, 0.00000001, 0.000000001, 0.0000000001};
int read(char *s) {
while (bn) {
for (; bi < bn && buf[bi] <= ' '; bi++);
if (bi < bn) break;
bn = fread(buf, 1, SIZE, stdin);
bi = 0;
}
int sn = 0;
while (bn) {
for (; bi < bn && buf[bi] > ' '; bi++) s[sn++] = buf[bi];
if (bi < bn) break;
bn = fread(buf, 1, SIZE, stdin);
bi = 0;
}
s[sn] = 0;
return sn;
}
bool read(int& x) {
int n = read(str), bf = 0;
if (!n) return 0;
int i = 0; if (str[i] == '-') bf = 1, i++; else if (str[i] == '+') i++;
for (x = 0; i < n; i++) x = x * 10 + str[i] - '0';
if (bf) x = -x;
return 1;
}
bool read(long long& x) {
int n = read(str), bf;
if (!n) return 0;
int i = 0; if (str[i] == '-') bf = -1, i++; else bf = 1;
for (x = 0; i < n; i++) x = x * 10 + str[i] - '0';
if (bf < 0) x = -x;
return 1;
}
void write(int x) {
if (x == 0) obuf[opt++] = '0';
else {
if (x < 0) obuf[opt++] = '-', x = -x;
int sn = 0;
while (x) str[sn++] = x % 10 + '0', x /= 10;
for (int i = sn - 1; i >= 0; i--) obuf[opt++] = str[i];
}
if (opt >= (SIZE >> 1)) {
fwrite(obuf, 1, opt, stdout);
opt = 0;
}
}
void write(long long x) {
if (x == 0) obuf[opt++] = '0';
else {
if (x < 0) obuf[opt++] = '-', x = -x;
int sn = 0;
while (x) str[sn++] = x % 10 + '0', x /= 10;
for (int i = sn - 1; i >= 0; i--) obuf[opt++] = str[i];
}
if (opt >= (SIZE >> 1)) {
fwrite(obuf, 1, opt, stdout);
opt = 0;
}
}
void write(unsigned long long x) {
if (x == 0) obuf[opt++] = '0';
else {
int sn = 0;
while (x) str[sn++] = x % 10 + '0', x /= 10;
for (int i = sn - 1; i >= 0; i--) obuf[opt++] = str[i];
}
if (opt >= (SIZE >> 1)) {
fwrite(obuf, 1, opt, stdout);
opt = 0;
}
}
void write(char x) {
obuf[opt++] = x;
if (opt >= (SIZE >> 1)) {
fwrite(obuf, 1, opt, stdout);
opt = 0;
}
}
void Fflush() { if (opt) fwrite(obuf, 1, opt, stdout); opt = 0;}
};
const int maxsz = 3e5 + 7;//@maxsz素数表@
//1e7+19,2e7+3,3e7+23,4e5+9 @maxsz最好为素数@
//1e6+3,2e6+3,3e6+7,4e6+9,1e5+3,2e5+3,3e5+7
//@要保证取值的操作值集合小于maxsz,@
//@count操作不增加新节点@
template<typename key,typename val>
class hash_map{public:
struct node{key u;val v;int next;};
vector<node> e;
int head[maxsz],nume,numk,id[maxsz];
bool count(key u){
int hs=(u%maxsz + maxsz) % maxsz;
for(int i=head[hs];i;i=e[i].next)
if(e[i].u==u) return 1;
return 0;
}
val& operator[](key u){
int hs=(u%maxsz + maxsz) % maxsz;
for(int i=head[hs];i;i=e[i].next)
if(e[i].u==u) return e[i].v;
if(!head[hs])id[++numk]=hs;
if(++nume>=e.size())e.resize(nume<<1);
return e[nume]=(node){u,0,head[hs]},head[hs]=nume,e[nume].v;
}
void clear(){
rep(i,0,numk)head[id[i]]=0;
numk=nume=0;
}
};
hash_map<int, int> mp;
int qpow(ll a, ll b, int op) {
int MOD = P[op];
ll res = 1;
while(b) {
if (b & 1) res = res * a % MOD;
a = a * a % MOD;
b >>= 1;
}
return res;
}
void add(int& a, int b, int op) {
a += b;
if (a >= P[op]) a -= P[op];
}
int n, k;
int a[N];
int val[N][1], chkv[1], sum[N][1], vpow[N][1];
void init() {
for (int i = 1; i < N; i++) {
for (int j = 0; j < 1; j++) {
vpow[i][j] = qpow(i, P[j], j);
}
}
}
void run() {
FI(n), FI(k);
for (int i = 1; i <= n; i++) {
FI(a[i]);
}
int lb = -1, rb = -1;
mp.clear();
for (int i = 1; i <= n; i++) {
if (mp.count(a[i]) == 0) {
++mp[a[i]];
} else {
lb = i;
break;
}
}
mp.clear();
for (int i = n; i >= 1; i--) {
if (mp.count(a[i]) == 0) {
++mp[a[i]];
} else {
rb = i;
break;
}
}
if (k > n) {
if (lb == -1) {
puts("YES");
} else {
if (rb >= lb) {
puts("NO");
} else {
puts("YES");
}
}
return;
}
for (int j = 0; j < 1; j++) {
int tmp = 0;
for (int i = 1; i <= k; i++) {
add(tmp, vpow[i][j], j);
}
chkv[j] = tmp;
for (int i = 1; i <= n; i++) {
val[i][j] = vpow[a[i]][j];
sum[i][j] = (sum[i - 1][j] + val[i][j]) % P[j];
}
}
auto query = [&](int l, int r, int op) {
return (sum[r][op] - sum[l - 1][op] + P[op]) % P[op];
};
for (int l = 1; l <= k; l++) {
int i;
bool flag = true;
for (i = l; i + k - 1 <= n; i += k) {
bool ok = true;
for (int j = 0; j < 1; j++) {
if (query(i, i + k - 1, j) != chkv[j]) {
ok = false;
break;
}
}
if (!ok) {
flag = false;
break;
}
}
if (flag) {
if (lb == -1 || (lb >= l && rb <= i)) {
puts("YES");
return;
}
}
}
puts("NO");
}
int main() {
#ifdef Local
freopen("input.in", "r", stdin);
#endif
init();
int T; FI(T); while(T--)
run();
Flush;
return 0;
}
重要的是自信,一旦有了自信,人就会赢得一切。