1. 程式人生 > >【SMOJ模擬】2018.12.26 反正都是原題系列2

【SMOJ模擬】2018.12.26 反正都是原題系列2

【前言】
原題是 COCI \text{COCI} 的,然而懶得找。

T1

【題目】
一個 n × m n\times m

的二維矩陣,第 i i 行第 j j 列的價值是 w i
, j w_{i,j}
,初始起點在第 A A 行第 B
B
列的格子,要走 K K 步,最終回到起點,每一步可以走四相鄰的格子。每進入一個格子可以得到該格子的價值,可以重複進入相同格子,而且重複進入可以得到當前格子。求能獲得最大價值。 n , m 100 , k , w i , j 1 0 9 n,m\leq 100,k,w_{i,j}\leq 10^9 ,且 k k 是偶數

【解題思路】
我們可以 YY \text{YY} 一下答案的組成,那麼一定是沿著一條路徑走,然後反覆橫跳,再沿著路徑走回去。
因為所有路徑最長只有 n × m n\times m ,於是我們可以暴力迭代前 n × m n\times m 步,剩下的步數直接計算即可。

【參考程式碼】

#pragma GCC optimize("O3")
#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const int N=105,MX=10000;
int dx[]={0,1,0,-1},dy[]={1,0,-1,0};
int n,m,bx,by,K,now,las;
int a[N][N];
ll ans,f[2][N][N];

int read()
{
	int ret=0;char c=getchar();
	while(!isdigit(c)) c=getchar();
	while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
	return ret;
}

void init()
{
	now=0;las=1;
	n=read();m=read();bx=read();by=read();K=read();
	for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) a[i][j]=read();
	memset(f,0xc0,sizeof(f));f[now][bx][by]=0;
}

void walk()
{
	now^=1;las^=1;
	for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) for(int d=0;d<4;++d)
		f[now][i][j]=max(f[now][i][j],f[las][i+dx[d]][j+dy[d]]+a[i][j]);
}

void solve()
{
	if(K<=MX) 
	{
		for(int i=1;i<=K;++i) walk();
		printf("%lld\n",f[now][bx][by]);
	}
	else
	{
		for(int i=1;i<=MX/2;++i) walk();
		for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) for(int d=0;d<4;++d)
			ans=max(ans,f[now][i][j]*2-a[i][j]+(ll)(K-MX)/2*(a[i][j]+a[i+dx[d]][j+dy[d]]));
		printf("%lld\n",ans);
	}
}

int main()
{
	freopen("2738.in","r",stdin);
	freopen("2738.out","w",stdout);

	init();solve();
	return 0;
}

T2

【題目】
有一個 n × m n\times m 的網格,在 k k 秒內每秒會有一場災難,用 ( x , y , r ) (x,y,r) 來表示,意為 ( x i , y i ) , ( x i x ) 2 + ( y i y ) 2 r 2 \forall (x_i,y_i), (x_i-x)^2+(y_i-y)^2\leq r^2 ,將 ( x i , y i ) (x_i,y_i) 上的數置零,否則加一。初始每個格子上的數字為 1 1 ,問 k k 秒後所有數之和。 n , m 1 0 5 , k 100 n,m\leq 10^5,k\leq 100

【解題思路】
我們可以對每一條橫線進行考慮,那麼我們要考慮的就是一個線段與圓交的問題,這個可以通過簡單計算完成。我們現在得到每條斜線上的若干個交點,用一個 set \text{set} 來維護計算這條斜線上最終所有數字的和即可。

【參考程式碼】

#include<bits/stdc++.h>
#define pb push_back
#define mkp make_pair
#define fi first
#define se second
using namespace std;

typedef long long ll;
typedef pair<int,int> pii;
const int N=105;
int lx,ly,n;
ll ans;
vector<pii>p;
set<int>st;

int read()
{
	int ret=0;char c=getchar();
	while(!isdigit(c)) c=getchar();
	while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
	return ret;
}
ll sqr(ll x){return x*x;}

struct data
{
	ll x,y,r;
	void in(){x=read();y=read();r=read();r=sqr(r);}
}a[N];

ll calc()
{
	ll res=0;int las=1;
	sort(p.begin(),p.end());st.clear();st.insert(0);
	//for(int i=0;i<p.size();++i) printf("%d %d\n",p[i].fi,p[i].se); puts("");
	for(int i=0;i<(int)p.size() && las<=ly;++i)
	{
		if(p[i].fi>las) res+=(p[i].fi-las)*(*st.rbegin()),las=p[i].fi;
		if(p[i].se>0) st.insert(p[i].se);
		else st.erase(-p[i].se);
	}
	return res;
}

int main()
{
	freopen("2741.in","r",stdin);
	freopen("2741.out","w",stdout);

	lx=read();ly=read();n=read();ans=(ll)lx*ly*n;
	for(int i=1;i<=n;++i) a[i].in();
	for(int i=1;i<=lx;++i)
	{
		p.clear();
		for(int j=1;j<=n;++j) if(a[j].r>=sqr(i-a[j].x))
		{
			ll t=sqrt(a[j].r-sqr(i-a[j].x));
			p.pb(mkp(a[j].y-t,j));p.pb(mkp(a[j].y+t+1,-j));
		}
		p.pb(mkp(ly+1,0));
		ans-=calc();
	}
	printf("%lld\n",ans);
	return 0;
}

T3

【題目】
n n 個人,每個人可以挑戰人也可以被挑戰,現在已知每個人是否挑戰人和是否被挑戰成功。挑戰是有時間順序的。當一個人被挑戰成功後,接下來所有對他的挑戰都會失敗,同時他將不再會挑戰別人。一個人最多挑戰一次人,求滿足要求的方案數。 n