这场单独的div2真的好难,trick太多。A题几乎写了的人都是错的,各种hack。。。B题出题人想错了,最后unrated了 。我B题错的和出题人一样。。。最后只过了C。。。
A:
把4个位移式子相互叠加一下可以推出类似(x,y-2b)的式子。
- (x - a, y - b)
- (x + a, y - b)
就是一个坐标可以不变,另一个坐标偏移2次。前提是x要有足够的空间上下各偏移一次。难么只要2个坐标变换次数相差偶数,就能调整成一样。满足条件后,答案取二者中偏移次数多的。枚举4个角即可。
code:
#include <algorithm>
#include <iostream>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <string>
#include <math.h>
#include <vector>
#include <queue>
#include <stack>
#include <cmath>
#include <list>
#include <set>
#include <map>
using namespace std;
#define N 100010
#define ALL(x) x.begin(),x.end()
#define CLR(x,a) memset(x,a,sizeof(x))
typedef long long ll;
typedef pair<int,int> PI;
const int INF = 0x3fffffff;
const int MOD = 100000007;
const double EPS = 1e-7;
bool move=true;
int ans=INF;
void solve(int x,int y)
{
if(!move && (x||y)) return ;
if(abs(x-y)%2) return ;
ans=min(ans,max(x,y));
}
int main()
{
int n,m,i,j,a,b;
scanf("%d%d%d%d%d%d",&n,&m,&i,&j,&a,&b);
if( (i-1<a && n-i<a) || (j-1<b && m-j<b) ) move=false;
if((i-1)%a==0 && (j-1)%b==0) solve((i-1)/a, (j-1)/b);
if((i-1)%a==0 && (m-j)%b==0) solve((i-1)/a, (m-j)/b);
if((n-i)%a==0 && (j-1)%b==0) solve((n-i)/a, (j-1)/b);
if((n-i)%a==0 && (m-j)%b==0) solve((n-i)/a, (m-j)/b);
if(ans==INF) puts("Poor Inna and pony!");
else printf("%d\n",ans);
return 0;
}
B:
对于一段连续的区间[l,r],满足(digit[i]+dight[i+1])==9,(l<=i<r)。设长度为n。
那么这段区间内,变换成最多9的情况有多少种呢?
1.n为偶数,显然变成n/2个9,只有一种变法。
2.n为奇数,有(n+1)/2种变成最多9的变法。这里最开始我认为只有2种,即从左往右开始变,和从右往左开始变,出题人也是这么认为的。。。。然后过了pre我就没管了。。。事实上能忽略一个奇数位的数字,剩下的数字个数为偶数,能全用完变成9。那么答案就是奇数位数字的个数。
累乘每段区间的种数就是答案了。
code:
#include <algorithm>
#include <iostream>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <string>
#include <math.h>
#include <vector>
#include <queue>
#include <stack>
#include <cmath>
#include <list>
#include <set>
#include <map>
using namespace std;
#define N 200010
#define ALL(x) x.begin(),x.end()
#define CLR(x,a) memset(x,a,sizeof(x))
typedef long long ll;
typedef pair<int,int> PI;
const int INF = 0x3fffffff;
const int MOD = 100000007;
const double EPS = 1e-7;
char s[N];
int dp[N];
int main()
{
scanf("%s",s);
for(int i=0;s[i];i++) s[i]-='0';
int star=0;
int n=strlen(s);
ll ans=1;
for(int i=0;i<n-1;i++){
if(s[i]+s[i+1]!=9){
if(i-star+1>2 && (i-star+1)%2 ) ans*=(i-star+2)/2;
star=i+1;
}
}
if(n-star>2 && (n-star)%2) ans*=(n-star+1)/2;
printf("%I64d\n",ans);
return 0;
}
C:
可以记忆化搜索,dp[x][y]表示从这点出发,最多走出几个“DIMA”。不过比赛的时候,我第一反应是建图,写个拓扑排序。建图就是对于所有“DIMA”的路径,D到A连一条权值为1的边。然后所有A到D连一条权值为0的边,都是有向的。
下面代码是拓扑排序+dp的做法。
code:
#include <algorithm>
#include <iostream>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <string>
#include <math.h>
#include <vector>
#include <queue>
#include <stack>
#include <cmath>
#include <list>
#include <set>
#include <map>
using namespace std;
#define N 1024
#define ALL(x) x.begin(),x.end()
#define CLR(x,a) memset(x,a,sizeof(x))
typedef long long ll;
typedef pair<int,int> PI;
const int INF = 0x3fffffff;
const int MOD = 100000007;
const double EPS = 1e-7;
const int dx[] = {-1, 1, 0, 0};
const int dy[] = {0, 0, -1, 1};
int n,m,U;
char c[4]={'D','I','M','A'};
char mp[N][N];
bool g[N][N];
vector<PI> e[N*N];
int has;
bool vis[N*N];
void dfs(int x,int y,int step)
{
if(step==4){
has++;
vis[m*x+y]=true;
e[U].push_back((PI){m*x+y,1});
return ;
}
for(int i=0;i<4;i++){
int xx=dx[i]+x;
int yy=dy[i]+y;
if(xx<0 || xx>=n || yy<0 || yy>=m) continue;
if(mp[xx][yy]==c[step]){
dfs(xx,yy,step+1);
}
}
}
int dp[N*N];
int in[N*N];
void topSort()
{
int num=0,cnt=0;
for(int i=0;i<n*m;i++){
if(vis[i]) cnt++;
for(int j=0;j<e[i].size();j++)
in[e[i][j].first]++;
}
queue<int> que;
for(int i=0;i<n*m;i++){
if(in[i]==0 && vis[i]) que.push(i);
}
while(!que.empty()){
int u=que.front();
que.pop();
num++;
for(int i=0;i<e[u].size();i++){
int v=e[u][i].first;
dp[v]=max(dp[v],dp[u]+e[u][i].second);
if(--in[v]==0) que.push(v);
}
}
if(num<cnt)
puts("Poor Inna!");
else
printf("%d\n",*max_element(dp,dp+n*m));
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++) scanf("%s",mp[i]);
for(int i=0;i<n;i++)
for(int j=0;j<m;j++) if(mp[i][j]=='D'){
U=m*i+j;
vis[U]=true;
dfs(i,j,1);
}
for(int i=0;i<n;i++)
for(int j=0;j<m;j++) if(mp[i][j]=='A'){
U=m*i+j;
vis[U]=true;
for(int k=0;k<4;k++){
int xx=dx[k]+i;
int yy=dy[k]+j;
if(xx<0 || xx>=n || yy<0 || yy>=m) continue;
if(mp[xx][yy]=='D'){
vis[m*xx+yy]=true;
e[U].push_back((PI){m*xx+yy,0});
}
}
}
if(has) topSort();
else puts("Poor Dima!");
return 0;
}
D:
二分+树状数组
先把每次删除的k记录下来,然后把所有01元素按顺序都加到序列里,最后统一进行删除操作。
我们用1标记某个位置上的元素是否存在。如果sum(i)==p 且 sum(i)-sum(i-1)==1 那么说明,第i个元素目前是序列里的第p个1(也就是经过一些删除操作后,它现在排在第p个)。
对于删除操作,二分出要删除元素的位置,把它清零就好了。
code:
#include <algorithm>
#include <iostream>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <string>
#include <math.h>
#include <vector>
#include <queue>
#include <stack>
#include <cmath>
#include <list>
#include <set>
#include <map>
using namespace std;
#define N 1000010
#define ALL(x) x.begin(),x.end()
#define CLR(x,a) memset(x,a,sizeof(x))
typedef long long ll;
typedef pair<int,int> PI;
const int INF = 0x3fffffff;
const int MOD = 1000000007;
const double EPS = 1e-7;
int a[N],c[N],d[N],t[N];
int n,m,num;
void add(int i,int x)
{
for(;i<=num;i+= -i&i) c[i]+=x;
}
int sum(int i)
{
if(i>num) return num+1;
int ans=0;
for(;i>=1;i-= -i&i) ans+=c[i];
return ans;
}
void remove(int end)
{
vector<int> pos;
for(int i=1;i<=end;i++){
int l=1,r=num+1;
while(l<r){
int mid=(l+r)>>1;
int x=sum(mid);
if(x<a[i] || (x==a[i] && sum(mid)-sum(mid-1)==1) ) l=mid+1;
else r=mid;
}
pos.push_back(r-1);
}
for(int i=0;i<pos.size();i++) add(pos[i],-1);
}
int main()
{
int op,len=0,T=0;
scanf("%d%d",&m,&n);
for(int i=1;i<=n;i++) scanf("%d",a+i);
while(m--){
scanf("%d",&op);
if(op==-1){
int end=upper_bound(a+1,a+n+1,len)-a-1;
len-=end;
t[T++]=end;
}else{
num++;
len++;
d[num]=op;
}
}
for(int i=1;i<=num;i++) add(i,1);
for(int i=0;i<T;i++) remove(t[i]);
bool can=true;
for(int i=1;i<=num;i++) if(sum(i)-sum(i-1)==1){
printf("%d",d[i]);
can=false;
}
if(can) puts("Poor stack!");
return 0;
}