联赛模拟测试 17
A. 简单的区间
CDQ裸题,每次考虑跨过区间中点的区间的贡献。分最大值在左和在右的两种情况。
设 \(suml\) 为左边端点到中点的和,\(sumr\) 同理,那么要求的就是如下柿子:
\[suml+sumr-max\equiv 0(mod\ k)
\]
然后移项得到
\[suml\equiv -sumr+max(mod\ k)
\]
根据这个柿子求和即可。
代码
#include<bits/stdc++.h>
using namespace std;
const int L = 1 << 20;
char buffer[L],*S,*T;
#define gc (S == T && (T = (S = buffer) + fread(buffer,1,L,stdin),S == T) ? EOF : *S++)
#define read() ({int s = 0,f = 1;char ch = gc;for(;!isdigit(ch);ch = gc)if(ch == '-')f = -1;for(;isdigit(ch);ch = gc)s = s * 10 + ch - '0';s * f;})
#define ll long long
const int maxn = 1e6+10;
ll sum[maxn];
ll a[maxn];
int n,k;
int ans;
int cnt[maxn];
inline void CDQ(int l,int r){
if(l == r)return;
int mid = (l + r) >> 1;
CDQ(l,mid);
CDQ(mid+1,r);
ll i = mid,j = mid + 1,mxl = a[i],mxr = a[j],suml = 0,sumr = a[j] % k;
cnt[sumr]++;
while(i >= l){
suml = (suml + a[i]) % k;
mxl = max(mxl,a[i]);
while(j + 1 <= r && mxl >= max(mxr,a[j + 1])){
j++;
mxr = max(mxr,a[j]);
sumr = (sumr + a[j]) % k;
cnt[sumr]++;
}
if(mxl >= mxr){
ll jl = (-suml + mxl) % k;
jl = (jl + k) % k;
ans += cnt[jl % k];
}
--i;
}
i = mid,j = mid + 1,mxl = a[i],mxr = a[j],suml = a[i] % k,sumr = 0;
ll sum = 0;
for(int p = mid + 1;p <= r;++p)cnt[sum=(sum+a[p]) % k] = 0;
cnt[suml]++;
while(j <= r){
sumr = (sumr + a[j]) % k;
mxr = max(mxr,a[j]);
while(i - 1 >= l && mxr > max(mxl,a[i - 1])){
i--;
mxl = max(mxl,a[i]);
suml = (suml + a[i]) % k;
cnt[suml]++;
}
if(mxr > mxl){
ll jl = (-sumr + mxr) % k;
jl = (jl + k) % k;
ans += cnt[jl % k];
}
++j;
}
sum = 0;
for(int p = mid;p >= l;--p){
cnt[sum=(sum + a[p]) % k] = 0;
}
}
int main(){
n = read(),k = read();
for(int i = 1;i <= n;++i)a[i] = read();
CDQ(1,n);
printf("%d\n",ans);
return 0;
}
B. 简单的玄学
根据容斥,得到不满足条件的方案为 \(\frac{A_{2^n}^m}{2^{nm}}\) 。
然后减一下即可,下边我们需要约分,容易发现只有因子 \(2\) 会被约分,分子上的 \(2\) 的次方可以转化为 \((m-1)!\) 的 \(2\) 的个数。
然后我们就可以以 \(O(logm)\) 的复杂度求出,然后算一下就行了。
代码
#include<bits/stdc++.h>
using namespace std;
const int L = 1 << 20;
const int mod = 1e6 + 3;
const int ny = 500002;
char buffer[L],*S,*T;
#define ll long long
#define gc (S == T && (T = (S = buffer) + fread(buffer,1,L,stdin),S == T) ? EOF : *S++)
#define read() ({ll s = 0,f = 1;char ch = gc;for(;!isdigit(ch);ch = gc)if(ch == '-')f = -1;for(;isdigit(ch);ch = gc)s = s * 10 + ch - '0';s * f;})
inline ll qpow(ll x,ll y){
ll ans = 1;
while(y){
if(y & 1)ans = ans * x % mod;
x = x * x % mod;
y >>= 1;
}
return ans % mod;
}
int main(){
freopen("random.in","r",stdin);
freopen("random.out","w",stdout);
ll n = read(),m = read();
if(log2(m) > n)return puts("1 1"),0;
ll tmp = qpow(2,n);
ll mu = qpow(tmp,m);
ll zi = 1;
ll cnt = n;
for(ll i = 0;i < m && zi;i++){
zi = zi % mod * (tmp - i) % mod;
}
for(ll i = 2;i < m;i <<= 1)cnt += (m - 1) / i;
mu = mu * qpow(ny,cnt) % mod;
zi = zi * qpow(ny,cnt) % mod;
printf("%lld %lld\n",(mu-zi+mod) % mod,mu % mod);
return 0;
}
C. 简单的填数
模拟,咕咕咕。
D. 聪聪和可可
预处理猫和鼠在什么位置时,猫的下一步是啥。
设猫在 \(i\) ,鼠在 \(k\) ,如果 \(j\) 到 \(k\) 的距离为 \(i\) 到 \(k\) 的距离减一,那么下一步就是 \(j\)。
每次取 \(min\) ,因为走编号最小的点,然后记忆化搜索就行了。
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e3+10;
#define db double
int nxt[maxn][maxn],dis[maxn][maxn];
struct Node{
int v,next;
}e[maxn<<1];
int head[maxn],tot;
int deg[maxn];
db f[maxn][maxn];
int vis[maxn];
inline void Add(int x,int y){
e[++tot].v = y;
e[tot].next = head[x];
head[x] = tot;
}
queue<int>q;
inline void spfa(int s){
dis[s][s] = 0;
q.push(s);vis[s] = 1;
while(!q.empty()){
int x = q.front();q.pop();
vis[x] = 0;
for(int i = head[x];i;i = e[i].next){
int v = e[i].v;
if(dis[s][v] > dis[s][x] + 1){
dis[s][v] = dis[s][x] + 1;
if(!vis[v]){
q.push(v);
vis[v] = 1;
}
}
}
}
}
inline db dfs(int cat,int mouse){
if(f[cat][mouse])return f[cat][mouse];
if(cat == mouse)return 0;
int to1 = nxt[cat][mouse];
int to2 = nxt[to1][mouse];
if(to1 == mouse || to2 == mouse)return 1;
f[cat][mouse] += 1.0;
for(int i = head[mouse];i;i = e[i].next){
int v = e[i].v;
f[cat][mouse] += dfs(to2,v)/(double)(deg[mouse]+1);
}
f[cat][mouse] += dfs(to2,mouse)/(double)(deg[mouse]+1);
return f[cat][mouse];
}
int main(){
freopen("cchkk.in","r",stdin);
freopen("cchkk.out","w",stdout);
memset(dis,0x3f,sizeof(dis));
memset(nxt,0x3f,sizeof(nxt));
int n,m;scanf("%d%d",&n,&m);
int s,t;scanf("%d%d",&s,&t);
for(int i = 1;i <= m;++i){
int x,y;scanf("%d%d",&x,&y);
Add(x,y);
Add(y,x);
deg[x]++;deg[y]++;
}
for(int i = 1;i <= n;++i)spfa(i);
for(int x = 1;x <= n;++x){
for(int i = head[x];i;i = e[i].next){
int v = e[i].v;
for(int j = 1;j <= n;++j){
if(dis[x][j] - 1 == dis[v][j]){
nxt[x][j] = min(nxt[x][j],v);
}
}
}
}
printf("%.3lf\n",dfs(s,t));
return 0;
}
\(Never\ Give\ Up\)
$Never\ Give\ Up$