Day1:T1
考场上觉得第二题比较水,于是就果断地跳进第二题的坑了,结果部分分都没来的及拿...
f[i][l]:用了i-n的数,这段子序列开始位置为l的方案数
考虑新加的一个数x,设原序列为S,
那么只会有三种放法:xSx,xxS,Sxx
对于限制条件,为了方便先把大于转小于
考虑三种转移
对于xSx:
f[i][l]+=f[i+1][l+1]
不能有以下限制:
h[l]<h[r],h[r]<h[l]
h[x]<=h[l],h[x]<=h[r] (l+1<=x<=r-1)
对于xxS:
f[i][l]+=f[i+1][l+2]
不能有以下限制:
h[l]<h[l+1] ,h[l+1]<h[l]
h[x]<=h[l],h[x]<=h[l+1] (l+2<=x<=r)
对于Sxx:
f[i][l]+=f[i+1][l]
不能有以下限制:
h[r]<h[r-1],h[r-1]<h[r]
h[x]<=h[r-1],h[x]<=h[r] (l<=x<=r-2)
对于小于号的条件直接判
对于小于等于号的条件,开一个二维前缀和数组,
那么就相当于判矩形内是否有点,有则不成立
#include<cstdio>
#include<cstring>
#include<algorithm>
const int maxn=2010;
using namespace std;
int n,m,s[maxn][maxn];long long f[maxn][maxn];bool t[maxn][maxn];char str[5];
int get(int x1,int y1,int x2,int y2){return s[x2][y2]-s[x1-1][y2]-s[x2][y1-1]+s[x1-1][y1-1];}
void prework(){
memset(t,0,sizeof(t)),memset(s,0,sizeof(s));
for (int i=1,a,b;i<=m;i++){
scanf("%d%s%d",&a,str,&b);
if (str[0]=='>') swap(a,b),str[0]='<';
++s[a][b];
if (str[0]=='=') ++s[b][a];
else if (!str[1]) t[a][b]=1;
}
for (int i=1;i<=n*2;i++)
for (int j=1;j<=n*2;j++)
s[i][j]+=s[i-1][j]+s[i][j-1]-s[i-1][j-1];
}
void dp(){
memset(f,0,sizeof(f));
for (int i=1;i<=(n<<1)-1;i++) f[n][i]=(!t[i][i+1]&&!t[i+1][i]);
for (int i=n-1;i;i--)
for (int l=1,r=(n-i+1)*2;r<=n*2;l++,r++){
if (!t[l][r]&&!t[r][l]&&!get(l+1,l,r-1,l)&&!get(l+1,r,r-1,r))//xSx
f[i][l]+=f[i+1][l+1];
if (!t[l][l+1]&&!t[l+1][l]&&!get(l+2,l,r,l+1))//xxS
f[i][l]+=f[i+1][l+2];
if (!t[r-1][r]&&!t[r][r-1]&&!get(l,r-1,r-2,r))//Sxx
f[i][l]+=f[i+1][l];
}
long long ans=f[1][1]%(1ll<<60);
if (ans<0) ans+=(1ll<<60);
printf("%I64d\n",ans);
}
int main(){
//freopen("dragon.in","r",stdin); freopen("dragon.out","w",stdout);
while (scanf("%d%d",&n,&m)!=EOF) prework(),dp();
return 0;
}
正解是考虑上1/8圆,x++时,y有可能-1,也有可能不减。
怎么避免大数开根呢,画图可知,只要判断(x+1,y-0.5)在圆内还是圆外即可
判圆内圆外就用x*x+y*y-r*r是小于0还是大于0
这样就可以通过只算乘法解决了
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
const int mod=1000000007;
using namespace std;
typedef long long ll;
ll r,r2,lim;int ans;
int main(){
//freopen("circle.in","r",stdin);freopen("circle.out","w",stdout);
scanf("%I64d",&r),r2=r*r,lim=(int)round(sqrt(1.0*r2/2));
ans=((int)round(sqrt(r2-lim*lim)))%mod;
if (ans>lim) ans+=lim,ans%=mod;
else if (ans<lim) ans=0;
ans+=((lim*(lim-1)/2)%mod),ans%=mod;
ans+=r,ans%=mod;
for (int x=0,y=r;x<lim-1;x++){
//if (1ll*x*x+2ll*x+1ll*y*y-y-1ll*r*r+1>=0) y--;
if (1.0*x*x+2.0*x+1.0*y*y-1.0*y+5.0/4.0-1.0*r2>0) y--;
ans+=y,ans%=mod;
}
printf("%d\n",ans);
return 0;
}
T3:恶心的SPFA题,考场上这题没写部分分,又坑了...
对于没有怪兽的情况,跑双关键字SPFA即可
对于怪兽的格子,因为怪兽只有一次伤害,所以对于与怪兽相邻的点,两两连距离为2,伤害为周围其他怪兽的伤害的边即可。
对于陷阱,就做点权即可。
细节比较多,还必须写Dijkstra+Heap优化才可以过...
#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define fi first
#define se second
#define mp(a,b) make_pair(a,b)
#define PI pair<int,int>
#define abs(a) (a>=0?a:-(a))
const int maxn=505,maxq=maxn*maxn*10,maxm=1100000;
const int dx[]={-maxn,-1,maxn,1};
using namespace std;
int n,m,stx,sty,edx,edy,pre[maxm],now[maxn*maxn],son[maxm],tot,st,ed;char map[maxn*maxn],s[maxn][maxn];
PI dis[maxn*maxn],val[maxm];
int P(int x,int y){return x*maxn+y;}
PI D(int x){return mp(x/maxn,x%maxn);}
void add(int a,int b,int bl,int dis){pre[++tot]=now[a],now[a]=tot,son[tot]=b,val[tot]=mp(bl,dis);}
bool check(int x){
int a=x/maxn,b=x%maxn;
if (a>n||a<=0||b>m||b<=0||map[x]=='#') return 0;
return 1;
}
bool near(int a,int b){
int x1=D(a).fi,y1=D(a).se,x2=D(b).fi,y2=D(b).se;
if (abs(x1-x2)+abs(y1-y2)<=1) return 1;
return 0;
}
PI operator +(PI a,PI b){return mp(a.fi+b.fi,a.se+b.se);}
struct node{
int x,b,d;
node( int x, int b, int d ){this->x=x,this->b=b,this->d=d;}
bool operator <(const node & xx)const{
if (b!=xx.b) return b>xx.b;
return d>xx.d;
}
};
void spfa(){
priority_queue<node> q;
q.push(node(st,0,0));
memset(dis,63,sizeof(dis));
dis[st]=mp(0,0);
while (!q.empty()){
int x=q.top().x;
if (x==ed) break;
q.pop();
for (int y=now[x];y;y=pre[y]){
PI tmp=dis[x]+val[y];
if (islower(map[son[y]])) tmp.fi+=map[son[y]]-'a'+1;
if (dis[son[y]]>tmp){
dis[son[y]]=tmp;
q.push(node(son[y],tmp.fi,tmp.se));
}
}
}
printf("%d %d\n",dis[ed].fi,dis[ed].se);
}
void prework(){
for (int i=1;i<=n;i++)//not monster
for (int j=1;j<=m;j++){
int x=P(i,j);
if (map[x]=='#'||isupper(map[x])) continue;
for (int k1=0;k1<=3;k1++){
int x1=x+dx[k1];
if (!check(x1)||isupper(map[x1])) continue;
int bl=0;
for (int k2=0;k2<=3;k2++){
int x2=x1+dx[k2];
if (!check(x2)) continue;
if (isupper(map[x2])) bl+=map[x2]-'A'+1;
}
add(x,x1,bl,1);
}
}
for (int i=1;i<=n;i++)//monster
for (int j=1;j<=m;j++){
int x=P(i,j);
if (isupper(map[x])){
for (int k1=0;k1<=3;k1++){
int x1=x+dx[k1];
if (!check(x1)) continue;
for (int k2=0;k2<=3;k2++) if (k2!=k1){
int x2=x+dx[k2];
if (!check(x2)) continue;
int bl=0;
for (int k3=0;k3<=3;k3++){
int x3=x2+dx[k3];
if (!check(x3)||near(x1,x3)) continue;
if (isupper(map[x3])) bl+=map[x3]-'A'+1;
}
add(x1,x2,bl,2);
}
}
}
}
}
int main(){
memset(map,'#',sizeof(map));
scanf("%d%d",&n,&m),scanf("%d%d%d%d",&stx,&sty,&edx,&edy),st=P(stx,sty),ed=P(edx,edy);
for (int i=1;i<=n;i++) scanf("%s",s[i]+1);
for (int i=0;i<=n+1;i++)
for (int j=0;j<=m+1;j++)
map[P(i,j)]=s[i][j];
prework(),spfa();
return 0;
}
Day2:T1树形DP。
设f[i][j]表示i号节点子树中距离为j的点的权值和,这可以递推求出。
然后对于每组询问(p,d),就是子树中距离为d的点和+向上走i步的父亲的子树中距离为d-i的点的和 再减去走了回头路的点即可
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ls (j<<1)
#define rs ((j<<1)|1)
const int maxn=280000,maxt=44;
typedef long long ll;
using namespace std;
int n,Q,a[maxn],pow[maxt],dep,dis;ll ans,f[maxn][maxt];
int main(){
freopen("dream.in","r",stdin);freopen("dream.out","w",stdout);
scanf("%d%d",&n,&Q);
pow[0]=1;for (int i=1;i<=maxt-1;i++) pow[i]=pow[i-1]<<1;
for (int i=0;i<=maxt-1;i++) if (pow[i]-1==n){dep=i;break;}
for (int i=1;i<=n;i++) scanf("%d",&a[i]);
for (int i=pow[dep-1];i<pow[dep];i++) f[i][0]=a[i];
for (int i=dep-1;i>=0;i--)
for (int j=pow[i-1];j<pow[i];j++){
f[j][0]=a[j];
for (int k=1;k<=dep;k++)
f[j][k]=f[j*2][k-1]+f[j*2+1][k-1];
}
// for (int i=1;i<=3;i++) printf("%d ",f[3][i]);puts("fuck");
for (int i=1,p,d;i<=Q;i++){
scanf("%d%d",&p,&d),ans=f[p][d];
if (d==0){printf("%d\n",a[p]);continue;}
for (dis=0;pow[dis]<=p;dis++);dis--;
if (d>dep+dis-1){puts("0");continue;}
if (dis==0){printf("%I64d\n",ans);continue;}
int ed=min(dis,d);
for (int j=1;j<ed;j++){
ans+=f[p>>j][d-j];
//printf("%d ",ans);
ans-=f[p>>(j-1)][d-j-1];
//printf("%d\n",ans);
}
if (d<=dis) ans+=a[p>>d];
else{
ans+=f[1][d-dis];
ans-=f[p>>(dis-1)][d-dis-1];
}
printf("%I64d\n",ans);
}
return 0;
}
T2:数论题,大致思路是类比辗转相除法,由f(a,b,l,r)得到f(b,a%b,x,y)就可以loga时间求出,但是推导过程想法很神...只知道推出来是对的,但是还不知道怎么想到这样推...考场上只拿了20分,不应该不拿另外40分部分分。
#include<cstdio>
#include<cstring>
#include<algorithm>
const int inf=2e9+10;
using namespace std;
ll a,b,s,l,r;int ans;
int gcd(int a,int b){return !b?a:gcd(b,a%b);}
int f(int a,int b,int l,int r){
if (!l) return 0;
if (r/a>(l-1)/a) return (l-1)/a+1;
int t=f(b,a%b,a-r%a,b-r%b);
return (l+1ll*b*t-1)/a+1;
}
int getans(int a,int b,int l,int r){
if (!a) return l?inf:0;
int t=gcd(a,b);if (r/g==(l-1)/g) return inf;
return f(a,b,l,r);
}
int main(){
//freopen("ice.in","r",stdin);freopen("ice.out","w",stdout);
scanf("%d",&T);
while (T--){
scanf("%I64d%I64d%I64d%I64d%I64d",&a,&b,&s,&l,&r);
l-=s,r-=s,a%=b;
/*if (l>r) r+=b;
if (r>=b) r-=b,l-=b;
if (r<=b) r+=b,l+=b;*/
if(l>r) r+=b; l-=s,r-=s;
if(r>=b) r-=b,l-=b; if(r<0) r+=b,l+=b;
if (l>=0) ans=getans(a,b,l,r);
else ans=min(getans(a,b,0,r),getans(a,b,l+b,b-1));
if (ans==inf) puts("Poor PPS.");
else printf("%d\n",ans);
}
return 0;
}
T3:考场上看到这题就放弃了,其实就是找黑白连通块数目就可以区分10个人了,剩下两个只要比较黑白区域的比例即可,代码也不是特别长。
#include<cstdio>
#include<cstring>
#include<algorithm>
#define mp(a,b) make_pair(a,b)
#define fi first
#define se second
#define PI pair<int,int>
using namespace std;
const int maxn=905,maxq=maxn*maxn*5;
const int dx[]={1,1, 1,0, 0,-1,-1,-1};
const int dy[]={1,0,-1,1,-1, 1, 0,-1};
const char name[][15]={"Baekhyun","Chanyeol","Chen","D.O", "Kai","Kris","Lay","Luhan", "Sehun","Suho","Tao","Xiumin"};
const int bc[12]={9,5,1,1, 2,3,6,5, 5,2,2,5};
const int wc[12]={2,1,3,2, 13,1,2,8, 2,8,4,2};
int T,ans[2],cnt[2],cb,n,m,map[maxn][maxn],head,tail;bool bo[maxn][maxn];
PI q[maxq+10];
void bfs(int stx,int sty,int c){
head=0,q[tail=1]=mp(stx,sty),bo[stx][sty]=1;
while (head!=tail){
if (++head>maxq) head=1;
int x=q[head].fi,y=q[head].se;
for (int i=0;i<8;i++){
int nx=x+dx[i],ny=y+dy[i];
if (map[nx][ny]!=c||bo[nx][ny]) continue;
if (++tail>maxq) tail=1;
q[tail]=mp(nx,ny),bo[nx][ny]=1;
}
}
}
int main(){
scanf("%d",&T);
while (T--){
memset(bo,0,sizeof(bo)),memset(map,-1,sizeof(map)),cb=0,ans[0]=ans[1]=0;
scanf("%d%d",&n,&m);int col=0,num,x=1,y=0;
for (int i=1;i<=m;i++){
scanf("%d",&num),col^=1;
if (!col) cb+=num;
while (num--){
++y;if (y>n) y=1,x++;
map[x][y]=col;
}
}
for (int i=1;i<=n;i++) for (int j=1;j<=n;j++)
if (!bo[i][j]) ans[map[i][j]]++,bfs(i,j,map[i][j]);
if (ans[0]==5&&ans[1]==2){
if (cb*100<18*n*n) puts("Xiumin");
else puts("Sehun");
continue;
}
for (int i=0;i<12;i++)
if (ans[0]==bc[i]&&ans[1]==wc[i]){printf("%s\n",name[i]);break;}
}
return 0;
}