JSOI2018day2简要题解

war

反过来求向量 v v v使得第二个凸包 B B B移动后与第一个凸包 A A A相交

∃ a ∈ A , b ∈ B , b + v = a → v = a − b \exists a\in A,b\in B,b+v=a\to v=a-b aA,bB,b+v=av=ab

求出闽科夫斯基和 C = { A + ( − B ) } C=\{A+(-B)\} C={A+(B)}(将 B B B中点坐标全部取反)

判断向量 v v v是否在 C C C内即可

闽科夫斯基和

#include<bits/stdc++.h>
typedef double db;
typedef long long ll;
const int N=2e5+10;
using namespace std;

int q;

inline char gc()
{
	static char buf[(1<<15)];static int p1=0,p2=0;
	if(p1==p2) p1=0,p2=fread(buf,1,(1<<15),stdin);
	return (p1==p2)?EOF:buf[p1++];
}

char cp;
inline void rd(int &x)
{
	cp=gc();x=0;int f=0;
	for(;!isdigit(cp);cp=gc()) if(cp=='-') f=1;
	for(;isdigit(cp);cp=gc()) x=x*10+(cp^48);
	if(f) x=-x;
}

struct P{
	int x,y;
	P(int x_=0,int y_=0):x(x_),y(y_){};
	P operator +(const P&ky){return P(x+ky.x,y+ky.y);}
	P operator -(const P&ky){return P(x-ky.x,y-ky.y);}
	ll operator ^(const P&ky){return (ll)x*ky.y-(ll)y*ky.x;}
	inline ll len(){return (ll)x*x+(ll)y*y;}
}stk[N],st,nw;
int top;

inline bool cmp(P a,P b)
{
	if(a.x!=b.x) return a.x<b.x;
	return a.y<b.y;
}

inline bool cmq(P a,P b){
    ll dlt=(a-st)^(b-st);
    if(dlt!=0) return dlt>0;
    return (a-st).len()<(b-st).len(); 
}
inline bool onlf(P a,P b,P c,P d){return ((b-a)^(d-c))>=0;}

struct convx{
int n;
P p[N],v[N];

inline void graham()
{
	int i;
	for(i=2;i<=n;++i) if(cmp(p[i],p[1])) swap(p[1],p[i]);
	st=p[1];sort(p+2,p+n+1,cmq);stk[1]=p[1];stk[2]=p[2];top=2;
	for(i=3;i<=n;++i){
		for(;top>1 && onlf(stk[top],p[i],stk[top-1],stk[top]);top--);
		stk[++top]=p[i];
	}
	for(i=1;i<=top;++i) p[i]=stk[i];n=top;
	for(i=1;i<n;++i) v[i]=p[i+1]-p[i];v[n]=p[1]-p[n];
}

}A,B,C;

inline void Minkowski()
{
	int i,j;int &k=C.n;
	k=1;C.p[1]=A.p[1]+B.p[1];
	for(i=j=1;i<=A.n || j<=B.n;){
	 	k++;
		if(j>B.n || (i<=A.n && (A.v[i]^B.v[j])>=0))
	 		C.p[k]=C.p[k-1]+A.v[i++];
		else C.p[k]=C.p[k-1]+B.v[j++];
	 }
}

inline bool in(P o)
{
	if(((o-st)^(C.p[2]-st))>0 || ((C.p[C.n]-st)^(o-st))>0) return false;
	int pos=lower_bound(C.p+1,C.p+C.n+1,o,cmq)-C.p-1;
	return (C.v[pos]^(o-C.p[pos]))>=0;
}

int main(){
	freopen("war.in","r",stdin);
	freopen("war.out","w",stdout);
	int i,x,y;
	rd(A.n);rd(B.n);rd(q);
	for(i=A.n;i>0;--i){rd(x);rd(y);A.p[i]=P(x,y);}
	for(i=B.n;i>0;--i){rd(x);rd(y);B.p[i]=P(-x,-y);}
	A.graham();B.graham();
	Minkowski();C.graham();
	st=C.p[1];
	for(;q;--q){
		rd(x);rd(y);nw=P(x,y);
		putchar(in(nw)?'1':'0');
		putchar('\n');
	}
	fclose(stdin);fclose(stdout);
	return 0;
}

robot

九老师的计数题。。。

( i , j ) (i,j) (i,j)向下走相当于 i → ( i % n + 1 ) i\to (i\% n+1) i(i%n+1),向右走相当于 j → ( j % m + 1 ) j\to (j\%m+1) j(j%m+1)

结论1:

每条左下-右上对角线格子走的方向相同。

考虑若在某个格子向下走,则它右侧相邻的格子必然由它上面的格子向下走得到。

结论2:

对于 n × n n\times n n×n的矩阵,每条对角线上格子相等。
那么对于 n × m ( n ≠ m ) n\times m(n\neq m) n×m(n̸=m)的矩阵,设 d = gcd ⁡ ( n , m ) d=\gcd(n,m) d=gcd(n,m),将矩阵划分成若干个 d × d d\times d d×d的矩阵,则小矩阵内部对角线上格子相等,且每个小矩阵形态相同。

考虑用 n × m n\times m n×m的矩阵拼成一个边长为 l c m ( n , m ) lcm(n,m) lcm(n,m)的方阵,显然每条对角线格子是相等的:
从行来看,第 i i i条对角线和第 i + n i+n i+n条相等;从列来看,第 i i i条对角线和第 i + m i+m i+m条相等。
所以第 i i i条对角线和第 i + gcd ⁡ ( n , m ) i+\gcd(n,m) i+gcd(n,m)条对角线相等。

结论3:

若在每个 d × d d\times d d×d的矩阵中,向下走了 x x x格,向右走了 y y y格( x + y = d x+y=d x+y=d),则 gcd ⁡ ( x , y ) = 1 , gcd ⁡ ( x , n ) = 1 , gcd ⁡ ( y , m ) = 1 \gcd(x,y)=1,\gcd(x,n)=1,\gcd(y,m)=1 gcd(x,y)=1,gcd(x,n)=1,gcd(y,m)=1

l c m ( x , n ) x \dfrac{lcm(x,n)}{x} xlcm(x,n)轮后回到第一行,走 l c m ( y , m ) y \dfrac{lcm(y,m)}{y} ylcm(y,m)后回到第一列,要求只在最后一步 ( n × m ) (n\times m) (n×m)时恰好第一次回到 ( 1 , 1 ) (1,1) (1,1),即:

l c m ( l c m ( x , n ) x , l c m ( y , m ) y ) = n m gcd ⁡ ( n , m ) lcm(\dfrac{lcm(x,n)}{x},\dfrac{lcm(y,m)}{y})=\dfrac{nm}{\gcd(n,m)} lcm(xlcm(x,n),ylcm(y,m))=gcd(n,m)nm

通过 l c m ( a , b ) a = b gcd ⁡ ( a , b ) , l c m ( a , b ) = a b gcd ⁡ ( a , b ) \dfrac{lcm(a,b)}{a}=\dfrac{b}{\gcd(a,b)},lcm(a,b)=\dfrac{ab}{\gcd(a,b)} alcm(a,b)=gcd(a,b)b,lcm(a,b)=gcd(a,b)ab这两种变换可以得到:

gcd ⁡ ( gcd ⁡ ( x , n ) , gcd ⁡ ( y , m ) ) = gcd ⁡ ( x , n ) gcd ⁡ ( y , m ) \gcd(\gcd(x,n),\gcd(y,m))=\gcd(x,n)\gcd(y,m) gcd(gcd(x,n),gcd(y,m))=gcd(x,n)gcd(y,m)

当且仅当 gcd ⁡ ( x , y ) = 1 , gcd ⁡ ( x , n ) = 1 , gcd ⁡ ( y , m ) = 1 \gcd(x,y)=1,\gcd(x,n)=1,\gcd(y,m)=1 gcd(x,y)=1,gcd(x,n)=1,gcd(y,m)=1时等式成立。

DP:
发现若在第一个小矩阵中走到了 ( i , j ) (i,j) (i,j),则在后面的小矩阵中必然都走到了 ( ( i + k d − 1 ) % n + 1 , ( j + k d − 1 ) % m + 1 ) ((i+kd-1)\%n+1,(j+kd-1)\%m+1) ((i+kd1)%n+1,(j+kd1)%m+1),所以可以 O ( d n m ) O(dnm) O(dnm)求出 d r [ i ] [ j ] dr[i][j] dr[i][j]表示第一轮经过 ( i , j ) (i,j) (i,j)时最多可以经过多少个格子(考虑每轮是否到达障碍格)。

f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k]表示第一轮到达 ( i , j ) (i,j) (i,j)最多能经过 k k k个格子的方案数,转移 O ( 1 ) O(1) O(1)

#include<bits/stdc++.h>
#define gc getchar
using namespace std;
const int N=52,mod=998244353;
typedef long long ll;

int tk,n,m,d,ans,f[N][N][N*N],dr[N][N];
char s[N][N];

inline int gcd(int x,int y){return (!y)?x:gcd(y,x%y);}
inline void dn(int &x,int y){if(y<x) x=y;}
inline void ad(int &x,int y){x+=y;if(x>=mod) x-=mod;} 

int main(){
	freopen("robot.in","r",stdin);
	freopen("robot.out","w",stdout);
	int i,j,x,y,k,ix,iy,w,rnd,lim;
	for(scanf("%d",&tk);tk;--tk){
		scanf("%d%d",&n,&m);d=gcd(n,m);ans=0;lim=n*m;rnd=lim/d;
		for(i=1;i<=n;++i){
			scanf("%s",s[i]+1);
			for(j=1;j<=m;++j) s[i][j]-='0';
		}
		for(x=0;x<=d;++x){
			y=d-x;
			if((x==0 && n>1) || (y==0 && m>1)) continue;
			if(x>0 && y>0 && ((gcd(x,y)!=1) || (gcd(y,m)!=1) || (gcd(x,n)!=1))) continue;
		    for(i=1;i<=x+1;++i)
			  for(j=1;j<=y+1;++j){
		         ix=i;iy=j;w=i+j-2;dr[i][j]=lim;
		         for(k=0;k<rnd;++k){
		         	if(s[ix][iy])
		         	 {dn(dr[i][j],w);break;}
		         	ix+=x;iy+=y;w+=d;
		         	if(ix>n) ix-=n;if(iy>m) iy-=m; 
				 }
			  }
			memset(f,0,sizeof(f));f[1][1][dr[1][1]]=1;
			for(i=1;i<=x+1;++i)
			 for(j=1;j<=y+1;++j)
			  for(k=1;k<=lim;++k){
			  	if(i<=x) ad(f[i+1][j][min(k,dr[i+1][j])],f[i][j][k]);
			  	if(j<=y) ad(f[i][j+1][min(k,dr[i][j+1])],f[i][j][k]);
			  }
			for(k=1;k<=lim;++k) ad(ans,(ll)f[x+1][y+1][k]*k%mod);
		}
		printf("%d\n",ans);
	} 
	fclose(stdin);fclose(stdout);
	return 0;
}

line

k , k + 1 , . . . , k + r − l + 1 k,k+1,...,k+r-l+1 k,k+1,...,k+rl+1是连续的!

主席树上二分即可(前段后段绝对值拆开分别为负/正)

离散化还会TLE…(sort+lower_bound的常数。。。)

#include<bits/stdc++.h>
#define mid ((l+r)>>1)
using namespace std;
const int N=1e6+10,mx=1e6;
typedef long long ll;

int n,m,a[N],cnt;
int rt[N],ls[N*22],rs[N*22];
ll qz[N],ans;

struct P{ll ss;int gs;}t[N*22],tp;

inline char gc()
{
	static char buf[(1<<15)];static int p1=0,p2=0;
	if(p1==p2) p1=0,p2=fread(buf,1,(1<<15),stdin);
	return (p1==p2)?EOF:buf[p1++];
}

char cp;
inline void rd(int &x)
{
	cp=gc();x=0;int f=0;
	for(;!isdigit(cp);cp=gc()) if(cp=='-') f=1;
	for(;isdigit(cp);cp=gc()) x=x*10+(cp^48);
	if(f) x=-x;
}

template<class T>inline void prit(T x)
{if(x>9) prit(x/10);putchar('0'+(x%10));}

void ins(int pre,int &nw,int l,int r,int pos)
{
	nw=++cnt;ls[nw]=ls[pre];rs[nw]=rs[pre];
	t[nw].gs=t[pre].gs+1;t[nw].ss=t[pre].ss+pos;
	if(l==r) return;
	if(pos<=mid) ins(ls[pre],ls[nw],l,mid,pos);
	else ins(rs[pre],rs[nw],mid+1,r,pos);
}

void ask(int pre,int nw,int l,int r,int pos)
{
	if(t[nw].gs==t[pre].gs) return;
	if(l==r){
		if(pos+tp.gs>=l) tp.gs++,tp.ss+=(t[nw].ss-t[pre].ss);
		return;
	}
	if(mid<pos+tp.gs+(t[ls[nw]].gs-t[ls[pre]].gs)){
		tp.gs+=(t[ls[nw]].gs-t[ls[pre]].gs);
		tp.ss+=(t[ls[nw]].ss-t[ls[pre]].ss);
		ask(rs[pre],rs[nw],mid+1,r,pos);
	}else ask(ls[pre],ls[nw],l,mid,pos);
}

int main(){
	freopen("line.in","r",stdin);
	freopen("line.out","w",stdout);
	int i,x,y,z,len;rd(n);rd(m);qz[0]=0LL;
	for(i=1;i<=n;++i) {rd(a[i]);qz[i]=qz[i-1]+a[i];}
	for(i=1;i<=n;++i){
		ins(rt[i-1],rt[i],1,mx,a[i]);
	}
	for(i=1;i<=m;++i){
		rd(x);rd(y);rd(z);tp.gs=0;tp.ss=0LL;len=y-x+1;ans=0;
		ask(rt[x-1],rt[y],1,mx,z);
		if(tp.gs) ans+=((ll)((z<<1)+tp.gs-1)*tp.gs/2-tp.ss);
		if(tp.gs<len) ans+=(qz[y]-qz[x-1]-tp.ss-((z<<1)+tp.gs+len-1)*(ll)(len-tp.gs)/2);
		prit(ans);putchar('\n');
	}
	return 0;
}

总结

T3比较傻,但我傻到总是发现不了傻题。。。

T1大胆创造闽科夫斯基和,T2大胆找规律

加起来能有200分?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值