目录
A:Ares, Toilet Ares(期望,逆元)
题意:
你原本的得分是,你可以去次厕所,每次去厕所有的概率拿到可以让你的得分加一的卡片的一小部分,这一小部分表示为,保证,就是完整卡片的长度,你只有在拿到全部长度的时候才能让你的得分加一,问你得分的期望是多?
solution:
得分期望:1*(每次的概率累乘)+a 即为答案。(注意=0时,不需要乘)
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int mod=4933;
int ksm(int a, int k) // 求a^k mod p
{
int res = 1 % mod;
while (k)
{
if (k & 1) res = res * a % mod;
a = a * a % mod;
k >>= 1;
}
return res%mod;
}
signed main()
{
int n,m,k,a,l;
cin>>n>>m>>k>>a>>l;
int ans=a;
int res=1;
for(int i=0;i<k;i++)
{
int x,y,z;
cin>>x>>y>>z;
if(x==0)
continue;
x=z-y;
res=((res*x%mod)*ksm(z,mod-2)%mod)%mod;
}
cout<<(ans+res)%mod;
}
D:OR(位运算,思维)
题意:
给定b序列和c序列,定义,求有多少序列a满足要求。
solution:
一个重要的公式:a+b=(a|b)+(a&b).可以求出&的序列。可以发现二进制下每一位取0或1都不会影响其他位,根据已有的&和的关系,可以推出每一位可以取0或1的可能性。枚举的每一位为0或1的情况是否满足 &和的关系,分别计算模拟即可。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+10;
int b[N];
int c[N];
int a[N];
signed main()
{
int n;
cin>>n;
for(int i=2;i<=n;i++)
{
cin>>b[i];
}
for(int i=2;i<=n;i++)
{
cin>>c[i];
}
for(int i=2;i<=n;i++)
{
a[i]=c[i]-b[i];
}
int ans=1;
for(int i=31;i>=0;i--)
{
int temp=0;//当前位满足的情况
for(int j=0;j<=1;j++)
{
int st=j;//前一位
bool ok=1;
for(int k=2;k<=n;k++)
{
if(st==0)
{
if((a[k]>>i&1)==0)//当前位可以0 1
{
if((b[k]>>i&1)==0)//当前位为0
{
st=0;
continue;
}
else if((b[k]>>i&1)==1)//当前位1
{
st=1;
continue;
}
ok=0;break;
}
else
{
ok=0;break;
}
}
else //1&
{
if((a[k]>>i&1)==0)//当前位只能为0
{
if((b[k]>>i&1)==0)
{
ok=0;break;
}
st=0;
}
else//当前位1
{
if((b[k]>>i&1)==0)
{
ok=0;break;
}
st=1;
}
}
}
temp+=ok;
}
ans*=temp;
}
cout<<ans;
}
F:Robots(暴力,bitset)
题意:
给你一个n*m的网格,0可以走,1不可以走,有三种类型机器人,第一种是只能向右移动,第二种是只能向下移动,第三种是只能向下或向右移动。q次询问,给出机器人类型,起点以及终点,输出是否能从起点走到终点。
solution:
暴力做法肯定tle。考虑离线做法,维护终点,记录哪些点可以到达该点。
用bitset来开一个bitset<N*N>f[N][N]表示(i,j)为终点能到达的情况,但是开一维bitset<N*N>f[N]的空间用滚动数组的思想来降低空间。
当前这个网格只能从左边的网格或上面的网格的状态转移下来.
如果mp[i][j]==0,说明这个点可以走,可以从上一个状态转移下来,f[j] |=f[j-1]
否则,另f[j]=0即可表示不能走
怎么滚动转移,借鉴一下别人的思路:
```
这就是滚动数组的延迟更新的特性,这能到达其上方的点,本身就已经在上一轮循环中储存在b[j]里了
举个例子,这是一张地图:
0 0 0
0 0 0
0 1 0
图中1代表障碍物,0代表空地
在遍历(1,1)时,b[1]的可达性矩阵是这样的
1 0 0
0 0 0
0 0 0
在遍历(1,2)时,由于b[2]|=b[1],其的可达性矩阵是这样的
1 1 0
0 0 0
0 0 0
在遍历(1,3)时,b[3]的可达性矩阵是这样的
1 1 1
0 0 0
0 0 0
在遍历(2,1)时,在未更新b[1]前,b[1]可达性矩阵是这样
1 0 0
0 0 0
0 0 0
在更新b[1]后,b[1]可达性矩阵是这样
1 0 0
1 0 0
0 0 0
在遍历(2,2)时,未更新b[2]前,b[2]可达性矩阵是这样
1 1 0
0 0 0
0 0 0
在进行b[2]|=b[1]操作后,b[2]成了这样
1 1 0
1 1 0
0 0 0
现在快进一波,在遍历(3,2)时,由于(3,2)有障碍物,其可达性矩阵要全部清0
此时的b[2]变成了
0 0 0
0 0 0
0 0 0
接下来遍历(3,3),在更新数据前b[3]是这个样子
1 1 1
1 1 1
0 0 0
在进行b[3]|=b[2]更新后,再加上它本身,变成这个样子:
1 1 1
1 1 1
0 0 1
由此,就实现了高效维护
```
#include <bits/stdc++.h>
// #define int long long
using namespace std;
const int N = 510;
char mp[N][N];
bitset<N * N> f[N];
struct node
{
int t, x, y, id; //类型,坐标,问题标号
};
vector<node> v[N][N];
int ans[500005];
signed main()
{
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i++)
{
scanf("%s", mp[i] + 1);
}
int q;
cin >> q;
for (int i = 1; i <= q; i++)
{
int t, x1, y1, x2, y2;
scanf("%d%d%d%d%d", &t, &x1, &y1, &x2, &y2);
v[x2][y2].push_back({t, x1, y1, i});
}
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
if (mp[i][j] == '0')
{
f[j] |= f[j - 1];//上个状态继承下来
f[j][i * m + j] = 1;//更新一下自己这个点可以到达
}
else
{
f[j] = 0;
}
for (auto it : v[i][j])
{
if (it.t == 1)
ans[it.id] = (it.y == j && f[j][it.x * m + it.y]);//必须在同一行,且能到达
else if (it.t == 2)
ans[it.id] = (it.x == i && f[j][it.x * m + it.y]);//必须在同一列,且能到达
else
ans[it.id]=f[j][it.x*m+it.y];
}
}
}
for(int i=1;i<=q;i++)
{
if(ans[i])
cout<<"yes"<<endl;
else
cout<<"no"<<endl;
}
}
J:Tree(st表,博弈,dfs)
题意:
给你一棵树,起始两个人在这棵树上不同位置s,t 上进行博弈,每个人轮流移动,移动后的上一个点和与这个点相连的边删去,每移动一次这个人加一分,当两个人都不能走的时候游戏结束,A先手。为A的得分,为B的得分。A想要-的值尽量大,B想要-的值尽量大(等价于 - 的值尽量小)。输出最后的 - 的值。
solution:
可以想两人在s~t这条路径上移动是有影响的,如果离开了这条链,就对另个人没有影响只要走最长链即可。
我们把s~t的这条链看作一条路径存在数组里(1~idx),A在数组的开头1,B在数组的末尾idx。
可以dfs预处理出从这条路径上每个点离开所能走到的最长路,假设为val[i]为从i这个点离开不经过s~t这条路所能到达的最长路。
模拟一下两个人的移动过程:
假设A走到的点的坐标为l,B走到的点的坐标为r。
A先手:
1.A可以从当前点离开走其他最长路,即A的分数为val[i]+l-1 (1~l的路径+最长路),B可以从[l+1,r]这个区间选择一个点离开然后走最长路,游戏结束。
2.A继续走s~t这条路径,即A走到l=l+1,轮到B,游戏继续
轮到B:
1.B可以从当前点离开走其他最长路,即B的分数为val[i]+idx-r (r~idx的路径+最长路),A可以从[l,r-1]这个区间选择一个点离开然后走最长路,游戏结束。
2.B继续走s~t这条路径,即B走到r=r-1,轮到A,游戏继续
一直递归下去。。。直到l==r停止。
轮到A,需要取max因为 A想要-的值尽量大,轮到B,需要取min因为B想要 - 的值尽量小。
A,B选择一个点离开然后走最长路需要查询区间最大值,用st表解决
#include<bits/stdc++.h>
// #define int long long
using namespace std;
const int N=5e5+10;
int n,s,t;
vector<int>edge[N];
int pre[N];
int idx;
int path[N];//记录路径s~t
int val[N];//s~t的结点最长路
int rot;
int lmx[N][20],rmx[N][20];//st表求区间最大值
void dfs(int u,int fa)//记录路径s~t
{
pre[u]=fa;
if(u==t)
return;
for(auto v:edge[u])
{
if(v==fa)
continue;
dfs(v,u);
}
}
int dfs1(int u,int fa)
{
int dis=0;
int ans=0;
for(auto v:edge[u])
{
if(v==fa)
continue;
if(v==path[rot-1]||v==path[rot+1])
continue;
ans=max(dis+dfs1(v,u)+1,ans);
}
return ans;
}
int query(int l,int r,int flag)
{
int k=log2(r-l+1);
if(!flag) return max(lmx[l][k],lmx[r-(1<<k)+1][k]);
return max(rmx[l][k],rmx[r-(1<<k)+1][k]);
}
int dfs2(int l,int r,int flag)//falg 0是A操作 1是B操作
{
if(l==r)
{
if(flag==0)
return 1e9;//不能走 返回正无穷
else
return -1e9;//同理
}
if(!flag)
return max((val[l]+l-1)-(query(l+1,r,1)),dfs2(l+1,r,1));
else
return min((query(l,r-1,0)-(val[r]+idx-r)),dfs2(l,r-1,0));
}
signed main()
{
cin>>n>>s>>t;
for(int i=0;i<n-1;i++)
{
int a,b;
scanf("%d%d",&a,&b);
edge[a].push_back(b);
edge[b].push_back(a);
}
idx=0;
dfs(s,-1);
int cur=t;
while(cur!=-1)
{
path[++idx]=cur;
cur=pre[cur];
}
reverse(path+1,path+1+idx);
for(rot=1;rot<=idx;rot++)//求每个点能到的最长路
{
val[rot]=dfs1(path[rot],0);
}
for(int i=1;i<=idx;i++)
{
lmx[i][0]=val[i]+i-1;//A 从i点离开的最大得分
rmx[i][0]=val[i]+idx-i;//B 从i点离开的最大得分
}
for(int j=1;(1<<j)<idx;j++)
{
for(int i=1;i+(1<<j)-1<=idx;i++)
{
lmx[i][j]=max(lmx[i][j-1],lmx[i+(1<<(j-1))][j-1]);
rmx[i][j]=max(rmx[i][j-1],rmx[i+(1<<(j-1))][j-1]);
}
}
int ans=dfs2(1,idx,0);
cout<<ans;
}