2019 Multi-University Training Contest 2
题号 | A | B | C | D | E | F | G | H | I | J | K | L |
状态 | . |
. |
. | . | Ο | . | . | . | . | Ο | Ο | Ο |
1005 Everything Is Generated In Equal Probability
题意:给出一个N,然后随机选取1到N之间的一个整数n,然后随机选取集合{1,2,3...n}的一个排列,之后对于这个数列,进行如下操作:
1、ans=0;
2、如果数列长度等于0,返回最终答案ans的值;
3、设数列中的逆序对对数位k,ans=ans+k;
4、选取当前数列的一个子序列(子序列可以为空),替代当前的数列,跳转到步骤2。
问ans的期望为多少。
思路:打表找规律,设当选取的数字为n时,当前ans的期望为f(n),可以发现,当n=1时,f(1)为0,当n>1时,ans的期望为f(n)=f(n-1)+2*(n-1)/3,所以对于N,总体ans的期望为(f(1)+f(2)+f(3)+...+f(n))/n。
//#pragma comment(linker, "/STACK:102400000,102400000") #include<iostream> #include<stdio.h> #include<stdlib.h> #include<string.h> #include<string> #include<math.h> #include<cmath> #include<time.h> #include<map> #include<set> #include<vector> #include<queue> #include<algorithm> #include<numeric> #include<stack> #include<bitset> #include<unordered_map> const int maxn = 0x3f3f3f3f; const double EI = 2.71828182845904523536028747135266249775724709369995957496696762772407663035354594571382178525166427; const double PI = 3.141592653589793238462643383279; //#ifdef TRUETRUE //#define gets gets_s //#endif using namespace std; long long p = 998244353; long long quick(long long a, int b, int c) { long long ans = 1; a = a % c; while (b != 0) { if (b & 1) { ans = (ans * a) % c; } b >>= 1; a = (a * a) % c; } return ans; } struct s { long long a, b,ans; }z[3010]; long long ans[3010], aa[3010]; int main(void) { int i, j, k; long long zz = 2; z[1].a = 0; z[1].ans = 0; long long z3 = quick(3, p - 2, p); //printf(" %lld\n",z3); for (i = 2; i <= 3000; i++) { z[i].a = z[i - 1].a + zz; z[i].a %= p; z[i].ans = (z[i].a * z3) % p; zz += 2; zz %= p; } ans[1] = 0; aa[1] = 0; for (i = 2; i <= 3000; i++) { ans[i] = (ans[i - 1] + z[i].ans) % p; aa[i] = (ans[i] * quick(i, p - 2, p)) % p; } int N; while (~scanf("%d", &N)) { printf("%lld\n", aa[N]); } return 0; }
题意:现在有一个数字x,但是你不知道x是多少,所以需要通过一些询问去得出x的值。你唯一知道的是x的二进制位有n位(二进制状态下可能有前导0),对于每一次询问,你可以给出一个0到x之间的一个整数y,然后你可以知道x^y是不是等于y,问在询问次数最少的情况下,有几种方案数。
思路:因为询问次数要最少,所以y的值从1<<0,1<<1,1<<2一直枚举到1<<(n-1),一共枚举n次即可,所以方案数即为n!。因为答案要对1e6+3取模,所以当n>=1e6+3时,答案必为0,所以时间复杂度为min(n,1e6+3)。
//#pragma comment(linker, "/STACK:102400000,102400000") #include<iostream> #include<stdio.h> #include<stdlib.h> #include<string.h> #include<string> #include<math.h> #include<cmath> #include<time.h> #include<map> #include<set> #include<vector> #include<queue> #include<algorithm> #include<numeric> #include<stack> #include<bitset> #include<unordered_map> const int maxn = 0x3f3f3f3f; const double EI = 2.71828182845904523536028747135266249775724709369995957496696762772407663035354594571382178525166427; const double PI = 3.141592653589793238462643383279; //#ifdef TRUETRUE //#define gets gets_s //#endif using namespace std; long long p = 1e6 + 3; int main(void) { long long n,i,ans; while (~scanf("%lld",&n)) { if (n >= p) { printf("0\n"); continue; } ans = 1; for (i = 1;i <= n;i++) { ans *= i; ans %= p; } printf("%lld\n",ans); } return 0; }
1011 Keen On Everything But Triangle
题意:给出n根火柴,q次询问,每次询问[l,r]区间内的火柴组成三角形的最长周长是多少。
思路:考虑暴力,必定是将l到r区间内的所有数字排序,然后从大到小依次check相邻的三根火柴能否组成三角形。
考虑优化,由斐波那契数列的性质可得,45根1e9范围内的火柴必定能组成一个三角形(最坏情况是1,1,2,3,5,第45项就超过1e9了),所以线段树区间维护46个最大值,然后做上面的check就可以了。
#include<bits/stdc++.h> #define clr(a,b) memset(a,b,sizeof(a)) using namespace std; typedef long long ll; const int maxn=200010; struct node{ int l,r,size; ll a[50]; }tr[maxn<<2]; ll val[maxn]; int n,q; inline ll rd() { ll x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } inline void pushup(int o,int oa,int ob){ int i=1,j=1,k=0; while(++k<=tr[o].size){ if(i<=tr[oa].size&&tr[oa].a[i]>=tr[ob].a[j]){ tr[o].a[k]=tr[oa].a[i]; i++; }else{ tr[o].a[k]=tr[ob].a[j]; j++; } } } inline void build(int o,int l,int r){ tr[o].size=min(r-l+1,46); for(int i=1;i<=tr[o].size;i++){ tr[o].a[i]=0; } if(l==r){ tr[o].a[1]=val[l]; return; } int mid=(l+r)>>1; build(o<<1,l,mid); build(o<<1|1,mid+1,r); pushup(o,o<<1,o<<1|1); } ll ans[50],temp[50]; int top=0; inline void query(int o,int l,int r,int ql,int qr){ if(ql<=l&&r<=qr){ int si=min(46,top+tr[o].size); int i=1,j=1,k=0; while(++k<=si){ if(i<=top&&ans[i]>=tr[o].a[j]){ temp[k]=ans[i]; i++; }else{ temp[k]=tr[o].a[j]; j++; } } top=si; for(int i=1;i<=top;i++){ ans[i]=temp[i]; } for(int i=top+1;i<=46;i++){ ans[i]=0; } return; } int mid=(l+r)>>1; if(ql<=mid)query(o<<1,l,mid,ql,qr); if(mid<qr)query(o<<1|1,mid+1,r,ql,qr); } int main(){ while(cin>>n>>q){ for(int i=1;i<=n;i++){ // scanf("%lld",&val[i]); val[i]=rd(); } build(1,1,n); while(q--){ int l,r; top=0; l=rd(),r=rd(); query(1,1,n,l,r); ll an=-1; for(int i=1;i<=top-2;i++){ if(ans[i]<ans[i+1]+ans[i+2]){ an=ans[i]+ans[i+1]+ans[i+2]; break; } } printf("%lld\n",an); } } }
1012 Longest Subarray
题意:给出n个数字,求一个最长子串,要求这个串包含的数字的次数必定大于等于k。
思路:先将整体的字符串,次数小于k的数全部去掉,得到若干个不连续的子串,对这些子串递归做这个工作。调一下参数,递归到30层时直接return。
(这个做法可以被hack,但数据比较难造,赌出题人没有卡这个做法,队友tql)
#include<bits/stdc++.h> #define rep(i,a,b) for(int i=a;i<=b;i++) #define dep(i,a,b) for(int i=b;i>=a;i--) using namespace std; #define ll long long const int N=3e5+5; const int mod = 998244353; int a[301010],c[301010],q[301010]; int n,C,k,ans; ll rd() { ll x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } void solve(int l,int r,int dep) { if(dep>=30) return; //printf("l=%d r=%d\n",l,r); if(r-l+1<=ans) return; int flag=0,pre,cnt=0; rep(i,l,r) c[a[i]]=0; rep(i,l,r) ++c[a[i]]; q[++cnt]=l-1; rep(i,l,r) { if(c[a[i]]<k) { q[++cnt]=i; flag=1; } } q[++cnt]=r+1; if(flag==0) { ans=r-l+1; } rep(i,1,cnt) solve(q[i]+1,q[i+1]-1,dep+1); } int main() { // freopen("1.in","r",stdin); // freopen("1.out","w",stdout); while(~scanf("%d%d%d",&n,&c,&k)) { ans=0; rep(i,1,n) a[i]=rd(); solve(1,n,1); printf("%d\n",ans); } }