2017 多校联合训练 2 题解
Problem 1002
这个Hash好难……其实要用到抽屉原理。
这道题当时做出的队伍很少。
我们取出1000*1000矩阵的所有8*8的矩阵,放到一个map里面。
然后我们去大矩阵里分块去找这些map里存过的东西……如果找到了那就有了答案。
也就是说根据原来map里存下的信息判断位置即可。
因为大矩阵是完全随机的,所以冲突概率很小。
#include <bits/stdc++.h> using namespace std; #define rep(i, a, b) for (int i(a); i <= (b); ++i) #define dec(i, a, b) for (int i(a); i >= (b); --i) #define fi first #define se second typedef long long LL; typedef pair <int, int> PII; const int N = 1e3 + 10; int T, flag, ca = 0; char s[N][N]; unordered_map <LL, PII> mp; inline unsigned sfr(unsigned h, unsigned x) { return h >> x; } int f(LL i, LL j) { LL w = i * 1000000ll + j; int h = 0; for(int k = 0; k < 5; ++k) { h += (int) ((w >> (8 * k)) & 255); h += (h << 10); h ^= sfr(h, 6); } h += h << 3; h ^= sfr(h, 11); h += h << 15; return sfr(h, 27) & 1; } int main(){ scanf("%d", &T); while (T--){ rep(i, 1, 1e3) scanf("%s", s[i] + 1); mp.clear(); rep(i, 1, 992){ rep(j, 1, 992){ LL tmp = 0; rep(k, 0, 7){ rep(l, 0, 7){ tmp <<= 1; tmp |= (s[i + k][j + l] == '1'); } } mp[tmp] = PII(i, j); } } flag = 1; for (int i = 1; i <= 1e6 && flag; i += 984){ for (int j = 1; j <= 1e6 && flag; j += 984){ LL tmp = 0; rep(k, 0, 7){ rep(l, 0, 7){ tmp <<= 1; tmp |= f(i + k, j + l); } } if (mp.find(tmp) != mp.end()){ PII ans = mp[tmp]; flag = 0; printf("Case #%d :%d %d\n", ++ca, i - ans.fi + 1, j - ans.se + 1); } } } } return 0; }
Problem 1003
这道题其实很简单……
我们对b数组升序排序,显然要从前面开始取。
做的时候维护后缀最大值,那么就可以O(1)知道原a数列的要取的数了。
因为从前面开始取,这样之后生成的数也就越大,对整个局面肯定更有利。
#include <bits/stdc++.h> using namespace std; #define rep(i, a, b) for (int i(a); i <= (b); ++i) #define dec(i, a, b) for (int i(a); i >= (b); --i) typedef long long LL; const int N = 250010; const LL mod = 1e9 + 7; int b[N], n; LL a[N], c[N], d[N], ans; int main(){ while (~scanf("%d", &n)){ rep(i, 1, n) scanf("%lld", a + i); rep(i, 1, n) scanf("%d", b + i); sort(b + 1, b + n + 1); ans = 0; rep(i, 1, n) c[i] = a[i] - (LL)i; d[n] = c[n]; dec(i, n - 1, 1) d[i] = max(d[i + 1], c[i]); LL now = -1e10; rep(i, 1, n){ LL cnt = max(d[b[i]], now); ans = (ans + cnt) % mod; now = max(now, cnt - i - n); } printf("%lld\n", ans); } return 0; }
Problem 1004
Problem 1005
Problem 1006
通过打表找规律
发现当n为偶数时,答案为2*(4^((n+1)/2)-1)*(2^n-1)^(m-2)
当n为奇数时,较复杂,还要减去一个数,当然减去的那个数也有规律
最后快速幂+乘法逆元即可
#include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<algorithm> #include<cstring> #include<string> #include<vector> #include<map> #include<set> #include<queue> using namespace std; typedef long long ll; const ll mod=1e9+7; ll n,m; ll pow_mod(ll a,ll b) { ll ans=1; while (b) { if (b&1) ans=ans*a%mod; b>>=1; a=a*a%mod; } return ans; } int main() { int _; scanf("%d",&_); while (_--) { scanf("%lld%lld",&n,&m); if (m==1) { puts("1"); continue; } ll p=(n+1)/2; ll fs=pow_mod(4ll,p); fs=(fs-1+mod)%mod; fs=fs*pow_mod(3,mod-2)%mod; if (n%2==0) fs=(fs*2)%mod; //cout<<fs<<endl; if (m==2) { printf("%lld\n",fs); continue; } ll g=(pow_mod(2ll,n)-1+mod)%mod; if (n&1) { p--; ll jian=pow_mod(4,p); jian=((jian-1)+mod)%mod; jian=jian*2%mod; jian=jian*pow_mod(3,mod-2)%mod; ll times=m-2; ll xx=(g-1+mod)%mod; ll yy=jian; ll nfs=(fs*xx-yy+mod)%mod; ll ans=nfs*pow_mod(g,times)%mod; ans=(ans+yy)%mod; ans=ans*pow_mod(xx,mod-2)%mod; printf("%lld\n",ans); } else { ll times=m-2; ll ans=fs*pow_mod(g,times)%mod; printf("%lld\n",ans); } } return 0; }
Problem 1008
每个数先单独算出期望然后相加
这里设置数量为13为临界点
数量小于13的数用容斥做
数量大于13的数,用全部的矩阵个数减去全不含的矩阵个数
#include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<algorithm> #include<cstring> #include<string> #include<vector> #include<map> #include<set> #include<queue> using namespace std; int _,n,m; int w[105][105],c[105][105]; int fg[10010],a[105],b[105]; vector<pair<int,int>> s[10010]; int calc(int x) { int ans=0; int i,j; for (i=1;i<=n;i++) { int tmp,tot=0,sum=0; for (j=1;j<=m;j++) { if (w[i][j]!=x) c[i][j]=c[i-1][j]+1; else c[i][j]=0; tmp=1; while (tot&&a[tot]>=c[i][j]) { tmp+=b[tot]; sum-=a[tot]*b[tot]; tot--; } tot++; a[tot]=c[i][j]; b[tot]=tmp; sum+=a[tot]*b[tot]; ans+=sum; } } return ans; } int calc2(int x) { int siz=s[x].size(); int tot=0; int i,j; for (i=1;i<(1<<siz);i++) { bool flag=false; int p1=n+500,p2=-1,p3=m+500,p4=-1; for (j=0;j<siz;j++) { if (i>>j&1) { flag=1-flag; p1=min(p1,s[x][j].first); p2=max(p2,s[x][j].first); p3=min(p3,s[x][j].second); p4=max(p4,s[x][j].second); } } if (flag) tot+=p1*(n-p2+1)*p3*(m-p4+1); else tot-=p1*(n-p2+1)*p3*(m-p4+1); } return tot; } int main() { scanf("%d",&_); while (_--) { scanf("%d%d",&n,&m); int i,j; for (i=1;i<=n;i++) for (j=1;j<=m;j++) scanf("%d",&w[i][j]); int tot=0; memset(fg,0,sizeof(fg)); for (i=1;i<=n;i++) for (j=1;j<=m;j++) { if (fg[w[i][j]]) continue; fg[w[i][j]]=++tot; } for (i=1;i<=tot;i++) s[i].clear(); for (i=1;i<=n;i++) for (j=1;j<=m;j++) { w[i][j]=fg[w[i][j]]; s[w[i][j]].push_back({i,j}); } double ans=0; int zans=n*(n+1)*m*(m+1)/4; for (i=1;i<=tot;i++) { if (s[i].size()>13) ans+=(zans-calc(i))*1.0/zans; else ans+=calc2(i)*1.0/zans; } printf("%.9f\n",ans); } return 0; }
Problem 1009
令F[i]表示是i的倍数时的方案数
可通过处理前缀和
然后再通过莫比乌斯反演算出答案
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int inf=(1<<30)-1; 4 const int maxn=100010; 5 #define REP(i,n) for(int i=(0);i<(n);i++) 6 #define FOR(i,j,n) for(int i=(j);i<=(n);i++) 7 typedef long long ll; 8 bool vis[maxn]; 9 int p[maxn]; 10 int cnt; 11 int g[maxn],u[maxn]; 12 int n; 13 int a[maxn]; 14 int F[maxn]; 15 const int Mo=1e9+7; 16 int pow(int a,int b,int c) 17 { 18 int ans=1; 19 while(b) 20 { 21 if(b&1) ans=1ll*ans*a%c; 22 a=1ll*a*a%c; 23 b>>=1; 24 } 25 return ans; 26 } 27 void Init() 28 { 29 memset(vis,0,sizeof(vis)); 30 u[1] = 1; 31 cnt = 0; 32 for(int i=2;i<maxn-5;i++) 33 { 34 if(!vis[i]) 35 { 36 p[cnt++] = i; 37 u[i] = -1; 38 } 39 for(int j=0;j<cnt&&i*p[j]<maxn-5;j++) 40 { 41 vis[i*p[j]] = 1; 42 if(i%p[j]) 43 { 44 u[i*p[j]] = -u[i]; 45 } 46 else 47 { 48 u[i*p[j]] = 0; 49 break; 50 } 51 } 52 } 53 } 54 int main() 55 { 56 Init(); 57 int T;scanf("%d",&T); 58 int Cas=0; 59 while(T--) 60 { 61 scanf("%d",&n); 62 ll ans=1; 63 int mn=1e5; 64 memset(a,0,sizeof(a)); 65 memset(F,0,sizeof(F)); 66 for(int i=1;i<=n;i++) { 67 int x;scanf("%d",&x); 68 ans=ans*x%Mo; 69 a[x]++; 70 } 71 for(int i=1;i<=mn;i++) 72 a[i]+=a[i-1]; 73 for(int i=1;i<=mn;i++) 74 if(!a[i-1]) 75 { 76 F[i]=1; 77 for(int j=1;i*j<=mn;j++) 78 F[i]=1ll*F[i]*pow(j,a[min(i*(j+1)-1,mn)]-a[i*j-1],Mo)%Mo; 79 } 80 ll tmp=0; 81 for(int i=1;i<=mn;i++) 82 { 83 tmp=(tmp+1ll*u[i]*F[i]%Mo)%Mo; 84 } 85 ans=(ans-tmp+Mo)%Mo; 86 printf("Case #%d: %lld\n",++Cas,ans); 87 } 88 return 0; 89 }
Problem 1011
这个题思路很明显。
正多边形肯定只有正方形。
那么枚举一下就可以了。
#include <bits/stdc++.h> using namespace std; #define rep(i, a, b) for (int i(a); i <= (b); ++i) #define dec(i, a, b) for (int i(a); i >= (b); --i) typedef long long LL; const int N = 603; int f[N][N]; int n; int ans; struct Point{ int x, y; } p[N]; int solve(Point a, Point b){ int x = a.x - b.x; int y = a.y - b.y; int ret = 0; if (a.x + y >= 0 && a.y >= x && b.x + y >= 0 && b.y >= x && f[a.x + y][a.y - x] && f[b.x + y][b.y - x]) ++ret; if (a.x >= y && a.y + x >= 0 && b.x >= y && b.y + x >= 0 && f[a.x - y][a.y + x] && f[b.x - y][b.y + x]) ++ret; return ret; } int main(){ while (~scanf("%d", &n)){ memset(f, 0, sizeof f); rep(i, 1, n){ int x, y; scanf("%d%d", &x, &y); x += 100; y += 100; p[i].x = x; p[i].y = y; f[x][y] = 1; } ans = 0; rep(i, 1, n - 1) rep(j, i + 1, n) ans += solve(p[i], p[j]); printf("%d\n", ans / 4); } return 0; }