10.25
题单介绍
这场比较史。
$t1$ 简单题,$t2$ 数据分治,$t3$ 是非 **基础** 数学,$t4$ 最小割。
没什么推荐的题目。
### t1
给定 $n$ 行 $m$ 列的网格图,每格是障碍或者空地,只有空地可以通行。
每个白天你可以走**至多** $k$ 格,每个夜晚你可以使至多 $k$ 格障碍变成空地,求从给定起点到达网格图边界的最少天数。
发现第二天起就等价于没有障碍,所以第二天以后从某格走出网格图的最少天数可以 $O(1)$ 算。
dfs 或者 bfs 出第一天能到达的格子然后取其最少天数 $+1$ 即可。
特判初始就在网格图边界的情况。
```cpp
#include <cstdio>
#include <algorithm>
#include <cstdlib>
static char stkk[200];
#define FOR(i,a,b) for(int i = (a);i <= (b);++i)
#define REP(i,a,b) for(int i = (a);i >= (b);--i)
typedef long long ll;
template<typename T>inline void output(T x){
if(!x)return putchar('0'),void();
if(x<0)x = ~x+1,putchar('-');
int top = 0;
for(;x;stkk[++top]=x%10^48,x/=10);
for(;top;putchar(stkk[top--]));
}
template<typename T>inline void readx(T &x){
x = 0;int y = 1;char c = getchar();
for(;c<48||c>58;c = getchar())if(c=='-')y = -1;
for(;c>=48&&c<=58;c = getchar())x = (x<<1)+(x<<3)+(c^48);
x *= y;
}
inline void ckmin(int &x,int y){x>y&&(x=y);}
inline int mini(int x,int y){return x<y?x:y;}
const int N = 2e3+10,nf = 2e9+10,dx[] = {0,0,-1,1},dy[] = {1,-1,0,0};
static int n,m,k,ans,sx,sy;
inline int cal(int x,int y){return mini((mini(x-1,n-x)+k-1)/k,(mini(y-1,m-y)+k-1)/k);};
static bool ok[N][N],vis[N][N];
static char s[N][N];
void dfs(int x,int y,int dep){
if(x<1||x>n||y<1||y>m||!ok[x][y]||vis[x][y]||dep>k)return;
vis[x][y] = 1;ckmin(ans,cal(x,y));
FOR(t,0,3)dfs(x+dx[t],y+dy[t],dep+1);
}
inline void solve(){
readx(n),readx(m),readx(k);
FOR(i,1,n)scanf(" %s",s[i]+1);
FOR(i,1,n)FOR(j,1,m){
vis[i][j] = 0;
if(s[i][j]=='S')sx = i,sy = j,ok[i][j] = 1;
else ok[i][j] = s[i][j]=='.';
}
if(sx==1||sx==n||sy==1||sy==m)return output(0),putchar(10),void();
ans = nf;
dfs(sx,sy,0);
output(ans+1),putchar(10);
}
signed main(){
freopen("escape.in","r",stdin);
freopen("escape.out","w",stdout);
int t;for(readx(t);t--;solve());
// puts("Yes");
return 0;
}
//1
//10 5
//1 2 3 3 3 4 3 3 2 4
```
### t2
交互题。
有一个长为 $n$ 的序列,每个位置有颜色,每次你可以询问区间 $[l,r]$ 中不同的颜色个数,求出这个序列的模样(颜色可以任意编号,只要原序列同色位置在你返回序列中仍然同色,异色位置仍然异色即可)。
至多询问 $Q$ 次。
一部分数据:$1 \leq n \leq 10^3,1 \leq Q \leq 10^4$。
一部分数据:$1 \leq n,Q \leq 10^3$,保证颜色段连续。
一部分数据:$1 \leq n \leq 10^4,1 \leq Q \leq 10^4$,保证至多有 $4$ 种不同的颜色 **或者** 至少也有 $n-1$ 种不同的颜色。
第一部分数据,从左往右扫,对于每个点二分出其之前第一个与其同色的点即可,至多询问 $O(n \log n)$ 次,可以通过。
第二部分数据直接双指针或者只判断与前一个数是否同色即可,至多询问 $O(n)$ 次,可以通过。
第三部分数据,对于颜色少的情况,之前不同色的点至多有 $4$ 种,可以只判断从左往右数第 $3$ 个点,然后根据询问情况决定询问第 $2$ 个点或者第 $4$ 个数,得出与其同色的点,至多询问 $2n$ 次;对于颜色多的情况,如果均异色随便做,如果有两个同色就直接二分存在点同色的最大左端点和最小右端点,然后就可以得出同色点对,至多询问 $2 \log n$ 次,可以通过。
```cpp
#include <bits/stdc++.h>
#include "generals.h"
using namespace std;
int query(int l, int r);
vector<int> getColor(int T, int N, int Q){
#define FOR(i,a,b) for(int i = (a);i <= (b);++i)
#define REP(i,a,b) for(int i = (a);i >= (b);--i)
int ct[1010],c[1010],cct;
FOR(i,1,N)ct[i] = c[i] = 0;cct = 1;
c[1] = ct[1] = 1;
if(T>=9&&T<=15){
int j = 1;cct = 0;
for(int i = 2;i <= N;++i){
if(query(j,i)!=1){
++cct;
FOR(t,j,i-1)c[t] = cct;
j = i;
}
}
++cct;
FOR(t,j,N)c[t] = cct;
}
else if(T>=16&&T<=34){
bool vis[4];
FOR(i,2,N){
vis[1] = vis[2] = vis[3] = 0;
int t1 = -1,t2 = -1,t3 = -1,tans = -1;
REP(j,i-1,1){
if(!vis[c[j]]){
vis[c[j]] = 1;
if(t3==-1)t3 = j;
else if(t2==-1)t2 = j;
else {
t1 = j;break;
}
}
}
if(t2==-1){
if(query(t3,i)==1)tans = t3;
}
else{
int tmp0,tmp1;
if((tmp0=query(t2,i))==2){
if((tmp1=query(t3,i))==1)tans = t3;
else tans = t2;
}
else{
if(t1!=-1)tans = t1;
}
}
if(tans==-1){
FOR(j,1,i)++ct[j];
c[i] = ++cct;
}
else{
c[i] = c[tans];
FOR(j,tans+1,i)++ct[j];
}
}
}
else if(T>=35&&T<=48){
bool vis[5];
FOR(i,2,N){
vis[1] = vis[2] = vis[3] = vis[4] = 0;
int t1 = -1,t2 = -1,t3 = -1,t4 = -1,tans = -1;
REP(j,i-1,1){
if(!vis[c[j]]){
vis[c[j]] = 1;
if(t4==-1)t4 = j;
else if(t3==-1)t3 = j;
else if(t2==-1)t2 = j;
else {
t1 = j;break;
}
}
}
if(t3==-1){
if(query(t4,i)==1)tans = t4;
}
else if(query(t3,i)==2){
if(query(t4,i)==1)tans = t4;
else tans = t3;
}
else if(t2!=-1&&query(t2,i)==3){
tans = t2;
}
else if(t1!=-1)tans = t1;
if(tans==-1){
FOR(j,1,i)++ct[j];
c[i] = ++cct;
}
else{
c[i] = c[tans];
FOR(j,tans+1,i)++ct[j];
}
}
}
else if(T>=49&&T<=60){
int tot = query(1,N);
if(tot==N){
FOR(i,1,N)c[i] = i;
}
else{
int dl = 2,dr = N,mid,tans = -1;
while(dl<=dr){
mid = dl+dr>>1;
if(query(1,mid)==mid-1)tans = mid,dr = mid-1;
else dl = mid+1;
}
if(tans==-1)puts("?");
int R = tans;
dl = 1,dr = R-1,tans = -1;
while(dl<=dr){
mid = dl+dr>>1;
if(query(mid,R)==R-mid)tans = mid,dl = mid+1;
else dr = mid-1;
}
if(tans==-1)puts("?");
int L = tans;
c[L] = c[R] = ++cct;
FOR(i,1,N)if(!c[i])c[i] = ++cct;
}
}
else{
FOR(i,2,N){
// printf("i:%d\n",i);
// FOR(j,1,i-1)printf("%d ",ct[j]);putchar(10);
int dl = 1,dr = i-1,mid,tans = -1;
while(dl<=dr){
mid = dl+dr>>1;
if(query(mid,i)==ct[mid]+1)dr = mid-1;
else dl = mid+1,tans = mid;
}
// printf("tans:%d cct:%d\n",tans,cct);
if(tans==-1){
FOR(j,1,i)++ct[j];
c[i] = ++cct;
}
else{
c[i] = c[tans];
FOR(j,tans+1,i)++ct[j];
}
}
}
// FOR(i,1,N)printf("%d ",c[i]);putchar(10);
std::vector<int> ans;
FOR(i,1,N)ans.push_back(c[i]);
return ans;
#undef FOR
#undef REP
}
```
### t3
我还不会,我尽力了()。
给定常数 $T$ 和 $n$ 个点的 $t_i$,其坐标为 $(\cos(\dfrac{2 \pi t_i}{T}),\sin(\dfrac{2 \pi t_i}{T}))$,求任取三点得到的三角形的内心期望横坐标和纵坐标,$t_i$ 严格不同。
记 $a_i = \dfrac{2 \pi t_i}{T}$,然后排序。
不妨设 $a_1 \lt a_2 \lt a_3$,则有内心横坐标为 $\cos(t_1+t_2)+\cos(t_2+t_3)-\cos(t_1+t_3)$,纵坐标为 $\sin(t_1+t_2)+\sin(t_2+t_3)-\sin(t_1+t_3)$(我觉得这个结论不显然,倒不如说我根本不理解这个结论awa)。
然后只考虑横坐标,可以考虑对形如 $\cos(t_i+t_j)$ 的形式统计贡献,则有 $\dfrac{1}{\binom{n}{3}}\sum_{i \lt j}{\cos(a_i+a_j)}(2i-2j+n)$。
然后你把上面那个东西拆开,然后直接维护 $i\cos(a_i)$,$i\sin(a_i)$ 和 $\cos(a_i)$,$\sin(a_i)$ 的后缀和即可。
总坐标同理。
时间复杂度 $O(n)$。
```cpp
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <queue>
#include <unordered_map>
#include <vector>
static char stkk[200];
#define FOR(i,a,b) for(int i = (a);i <= (b);++i)
#define REP(i,a,b) for(int i = (a);i >= (b);--i)
#define GO(x) for(int i = h[x],y = e[i];i;y = e[i=ne[i]])
#define up std::unordered_map
#define ve std::vector
#define ll long long
#define ld long double
template<typename T>inline void output(T x){
if(!x)return putchar('0'),void();
if(x<0)x = ~x+1,putchar('-');
int top = 0;
for(;x;stkk[++top]=x%10^48,x/=10);
for(;top;putchar(stkk[top--]));
}
template<typename T>inline void readx(T &x){
x = 0;int y = 1;char c = getchar();
for(;c<48||c>58;c = getchar())if(c=='-')y = -1;
for(;c>=48&&c<=58;c = getchar())x = (x<<1)+(x<<3)+(c^48);
x *= y;
}
const int N = 1e6+10;
const ld pi = acos(-1);
static int n,T;
static ld co[N],si[N],a[N],bco[N],bsi[N],dco[N],dsi[N];
signed main(){
freopen("geometry.in","r",stdin);
freopen("geometry.out","w",stdout);
readx(n),readx(T);
int t;FOR(i,1,n)readx(t),a[i] = pi*t/T,bco[i] = std::cos(a[i]),bsi[i] = std::sin(a[i]);
REP(i,n,1)co[i] = bco[i]+co[i+1],si[i] = bsi[i]+si[i+1],dco[i] = 2*i*bco[i]+dco[i+1],dsi[i] = 2*i*bsi[i]+dsi[i+1];
ld x = 0,y = 0;
FOR(i,1,n){
x += (2*i+n)*(bco[i]*co[i+1]-bsi[i]*si[i+1])-(bco[i]*dco[i+1]-bsi[i]*dsi[i+1]);
y += (2*i+n)*(bsi[i]*co[i+1]+bco[i]*si[i+1])-(bsi[i]*dco[i+1]+bco[i]*dsi[i+1]);
}
x = x*6/n/(n-1)/(n-2);
y = y*6/n/(n-1)/(n-2);
printf("%.15Lf %.15Lf",x,y);
return 0;
}
// 2 1
// 2 1
// 1 2 2
```
$t4$
给定 $n$ 个平面直角坐标系中的点 $(x_i,y_i)$ 及其点权 $v_i$。
定义横纵坐标均为偶数的点为 **重要的点**。
如果存在一个重要点,有三个与其切比雪夫距离不超过 $1$ 的点,与其构成了一个有一边平行于 $x$ 轴的平行四边形,那么这个局面是不合法的。
你可以删掉一些点,使得局面合法,最大化未被删除的点的点权和。
可以证明局面合法等价于不存在 $(奇,偶)$ -> $(偶,偶)$ -> $(偶,奇)$ -> $(奇,奇)$ 这样的路径,其中奇/偶 表示对应坐标的奇偶性, -> 表示两点相邻。
然后就拆点,每个点拆成 $in_i$,$ou_i$,将 $in_i$ 向 $ou_i$ 连 $w_i$ 的边。
然后将源点向 $(奇,偶)$ 点的 $in_i$ 连正无穷的边,将 $(奇,奇)$ 点的 $ou_i$ 向汇点连正无穷的边,对于满足上述路径的点对将他们的出点和入点对应连正无穷的边,跑最小割即可。
最后输出总点权和减去最小割。
```cpp
#include <cstdio>
#include <algorithm>
#include <queue>
#include <unordered_map>
static char stkk[200];
#define FOR(i,a,b) for(int i = (a);i <= (b);++i)
#define REP(i,a,b) for(int i = (a);i >= (b);--i)
#define GO(x) for(int i = h[x],y = e[i];i;y = e[i=ne[i]])
#define GO1(x) for(int i = now[x],y = e[i];i;y = e[i=ne[i]])
#define up std::unordered_map
typedef long long ll;
template<typename T>inline void output(T x){
if(!x)return putchar('0'),void();
if(x<0)x = ~x+1,putchar('-');
int top = 0;
for(;x;stkk[++top]=x%10^48,x/=10);
for(;top;putchar(stkk[top--]));
}
template<typename T>inline void readx(T &x){
x = 0;int y = 1;char c = getchar();
for(;c<48||c>58;c = getchar())if(c=='-')y = -1;
for(;c>=48&&c<=58;c = getchar())x = (x<<1)+(x<<3)+(c^48);
x *= y;
}
template<typename T1,typename T2>inline void ckmin(T1 &x,T2 y){x>y&&(x=y);}
const int N = 2e4+10,dx[] = {1,-1,0,0},dy[] = {0,0,1,-1};
const ll lnf = 1e18;
static int e[N*10],ne[N*10],from[N*10],h[N<<1],tot,n,x[N],y[N],v[N],s,t;
static ll w[N*10];
static up<int,up<int,int> > id;
inline void add(int x,int y,ll ww){e[++tot] = y,ne[tot] = h[x],h[x] = tot;from[tot] = x,w[tot] = ww;}
inline void link(int x,int y,ll w){add(x,y,w),add(y,x,0);}
// printf("x:%d y:%d w:%lld]\n",x,y,w);
static int dep[N<<1],gra[N<<1],pree[N<<1],now[N<<1];
struct node{int x,y;};
static node to[2][2];
inline ll isap(){
FOR(i,1,t)now[i] = h[i];
std::queue<int> q;dep[t] = 1;q.push(t);
while(!q.empty()){
int x = q.front();q.pop();
++gra[dep[x]];
GO(x)if(w[i^1]&&!dep[y])q.push(y),dep[y] = dep[x]+1;
}
ll ans = 0,mx;int x = s;
while(dep[s]<=t){
if(x==t){
mx = lnf;
for(int i;x!=s;x = from[i])ckmin(mx,w[i=pree[x]]);
x = t;ans+=mx;
for(int i;x!=s;x = from[i])i = pree[x],w[i]-=mx,w[i^1]+=mx;
}
bool ok = 0;
GO1(x)if(w[i]&&dep[y]+1==dep[x]){
ok = 1;pree[y] = now[x] = i;
x = y;
break;
}
if(!ok){
if(!--gra[dep[x]])break;now[x] = h[x];
int mii = t+10;
GO(x)if(w[i]&&mii>dep[y])mii = dep[y];
++gra[dep[x]=mii+1];
if(x!=s)x = from[pree[x]];
}
}
return ans;
}
signed main(){
freopen("mis.in","r",stdin);
freopen("mis.out","w",stdout);
readx(n);to[1][0] = {0,0};to[0][0] = {0,1},to[0][1] = {1,1},to[1][1] = {-1,-1};
s = 2*n+1,t = s+1;tot = 1;ll ans = 0;
FOR(i,1,n){
readx(x[i]),readx(y[i]),readx(v[i]),id[x[i]][y[i]] = i;
link(i,i+n,v[i]);ans+=v[i];
if((x[i]&1)&&!(y[i]&1))link(s,i,lnf);
else if(((x[i]&1))&&(y[i]&1))link(i+n,t,lnf);
}
node tmp;
FOR(i,1,n){
tmp = to[x[i]&1][y[i]&1];
FOR(k,0,3){
int tx = x[i]+dx[k],ty = y[i]+dy[k];
if(!id[tx][ty])continue;
if((tx&1)==tmp.x&&(ty&1)==tmp.y)link(i+n,id[tx][ty],lnf);
}
}
output(ans-isap());
return 0;
}
// 5
// 0 0 4
// 0 1 5
// 1 0 3
// 1 1 1
// -1 1 2
```