AtCoder Beginner Contest 298
题意:有三种操作,一种是把编号为y的球放入编号为x的盒子里,一种是查询编号为x的盒子里面有哪些球,一种是查询编号为x的球都放在哪个盒子里。
直接模拟即可,一个set,一个multiset。
code:
//head
int n,m;
multiset<int> s1[N];
set<int> s2[N];
void work(){
cin>>n>>m;
while(m--)
{
int op,x,y;
cin>>op;
if(op==1){
cin>>x>>y;
s1[y].insert(x);
s2[x].insert(y);
}
else if (op==2){
cin>>x;
for(auto i:s1[x]) cout<<i<<' ';
cout<<endl;
}
else {
cin>>x;
for(auto i:s2[x]) cout<<i<<' ';
cout<<endl;
}
}
}
题意:初始字符串为1,有三种操作,第一种是把x加到末尾,第二种是删去最前面的字符,第三种是输出当前字符串代表的值%mod。
分析可知,操作可以变为 a n s ∗ 10 + x 和 a n s − s [ b e g i n ] ∗ 1 0 l e n ans*10+x 和 ans-s[begin]*10^{len} ans∗10+x和ans−s[begin]∗10len,然后就可以模拟了,注意取模即可。
code:
int n,m;
int qmi(int a,int k)
{
int ans=1;
while(k)
{
if(k&1) ans=ans*a%mod;
a=a*a%mod;
k>>=1;
}
return ans;
}
void work(){
cin>>m;
int ans=1;
string s="1";
int last=0;
int cnt=0;
while(m--)
{
int op,x;
string ch;
cin>>op;
if(op==1){
cin>>ch;
x=ch[0]-'0';
ans=(ans*10%mod+x)%mod;
s+=ch;
cnt++;
}
else if(op==2){
x=s[last]-'0';
ans=(ans-x*qmi(10,cnt-last)%mod+mod)%mod;
last++;
}
else {
cout<<ans%mod<<endl;
}
}
}
题意:甲初始站在A处,乙初始站在B处,他们分别可以等概率的跳1~P 步 和 1~Q步,如果跳出N则认为跳到N,两人轮流跳,甲先开始,谁先跳到N处谁获得胜利,问甲获胜的概率有多大。
似乎是个比较经典的概率dp,学习一下。概率dp一般是由终点状态来递推初始状态,最终初始状态的dp值即为答案。
设 d p [ i ] [ j ] [ 0 / 1 ] 表示当前甲在 i 处,乙在 j 处,且下一轮轮到甲 / 乙跳 , 甲获胜的概率 设dp[i][j][0/1]表示当前甲在i处,乙在j处,且下一轮轮到甲/乙跳,甲获胜的概率 设dp[i][j][0/1]表示当前甲在i处,乙在j处,且下一轮轮到甲/乙跳,甲获胜的概率,则状态转移为:
d p [ i ] [ j ] [ 0 ] = d p [ i + 1 ] [ j ] [ 1 ] + d p [ i + 2 ] [ j ] [ 1 ] + . . . + d p [ i + p ] [ j ] [ 1 ] p dp[i][j][0]=\frac{dp[i+1][j][1]+dp[i+2][j][1]+...+dp[i+p][j][1]}{p} dp[i][j][0]=pdp[i+1][j][1]+dp[i+2][j][1]+...+dp[i+p][j][1]
注意要取 m i n ( n , i + x ) min(n,i+x) min(n,i+x), 同理我们可以推出 d p [ i ] [ j ] [ 1 ] dp[i][j][1] dp[i][j][1]的转移。
边界条件为: d p [ n ] [ i ] [ 0 / 1 ] = 1 , d p [ i ] [ n ] [ 0 / 1 ] = 0 dp[n][i][0/1]=1,dp[i][n][0/1]=0 dp[n][i][0/1]=1,dp[i][n][0/1]=0.
code:
int dp[N][N][2];
int n,m;
int qmi(int a,int k)
{
int ans=1;
while(k)
{
if(k&1) ans=ans*a%mod;
a=a*a%mod;
k>>=1;
}
return ans;
}
void work(){
cin>>n;
int a,b,p,q;
cin>>a>>b>>p>>q;
int invp=qmi(p,mod-2),invq=qmi(q,mod-2);
for(int i=0;i<n;i++){
for(int k=0;k<2;k++) {
dp[n][i][k]=1;
dp[i][n][k]=0;
}
}
for(int i=n-1;i>=0;i--){
for(int j=n-1;j>=0;j--){
for(int k=1;k<=p;k++){
dp[i][j][0]+=dp[min(i+k,n)][j][1];
}
dp[i][j][0]%=mod;
dp[i][j][0]=dp[i][j][0]*invp%mod;
for(int k=1;k<=q;k++){
dp[i][j][1]+=dp[i][min(j+k,n)][0];
}
dp[i][j][1]%=mod;
dp[i][j][1]=dp[i][j][1]*invq%mod;
}
}
cout<<dp[a][b][0]<<endl;
}
题意:给定一个网格,网格上有N个点,给定N个点的坐标和值,可以取网格上某一行和一列上的所有的值,问这所有值的和最大是多少。
分析不难发现,我们要求的是其实是 r [ i ] + c [ j ] − v [ i ] [ j ] 的最大值 r[i]+c[j]-v[i][j]的最大值 r[i]+c[j]−v[i][j]的最大值, r [ i ] 表示 i 行上的和, c [ j ] 表示 j 列上的和, v [ i ] [ j ] 表示 i 行 j 列上的值 r[i]表示i行上的和,c[j]表示j列上的和,v[i][j]表示i行j列上的值 r[i]表示i行上的和,c[j]表示j列上的和,v[i][j]表示i行j列上的值。
首先想到的是可以枚举每一行,然后枚举每一列,但是这样的时间复杂度是不允许的,不妨把每一列的值从大到小排序,这样我们可以进行分类讨论了:
- 枚举到第 j 列时,当前格子上没有值 , 也就是 v [ i ] [ j ] = 0 枚举到第j列时,当前格子上没有值,也就是v[i][j]=0 枚举到第j列时,当前格子上没有值,也就是v[i][j]=0,则我们无需继续枚举了,因为不会让结果更优,因为我们是从大到小枚举的。
- 枚举到第 j 列时,当前格子上有值 , 也就是 v [ i ] [ j ] ! = 0 枚举到第j列时,当前格子上有值,也就是v[i][j]!=0 枚举到第j列时,当前格子上有值,也就是v[i][j]!=0,则我们仍要继续枚举,不断更新答案,直到出现情况1.
可是看起来这样的时间复杂度还是
O
(
n
2
)
O(n^2)
O(n2)的,但实际上不是的,因为实际的点的数量只有N个,我们N个点总共最多只会枚举N次(根据什么什么原理),因此时间复杂度是
O
(
n
)
O(n)
O(n)的。
code:
#define all(x) (x).begin(),(x).end()
typedef pair<int,int> pii;
int n,m;
map<int,int> r,c;
map<pii,int> mp;
void work(){
cin>>n;
for(int i=1;i<=n;i++){
int x,y,z;
cin>>x>>y>>z;
r[x]+=z;
c[y]+=z;
mp[{x,y}]=z;
}
vector<pii> v1,v2;
for(auto [x,y]:r) {
v1.push_back({y,x});
}
for(auto [x,y]:c) {
v2.push_back({y,x});
}
sort(all(v1));
sort(all(v2));
reverse(all(v1));
reverse(all(v2));
int ans=0;
for(auto i:v1){
for(auto j:v2){
if(mp[{i.y,j.y}]){
ans=max(ans,r[i.y]+c[j.y]-mp[{i.y,j.y}]);
}
else {
ans=max(ans,r[i.y]+c[j.y]);
break;
}
}
}
cout<<ans<<endl;
}