Time:2018.4.15 13:00-18:00
Link
A - Alphabet solved by czh&ym
题意
给一个字符串,问最少添加几个字符使得它能够存在一个子序列“abcdefg…xyz”。长度不超过50
分析
ym:求一个最大上升子序列即可
B - Buggy Robot solve by ym&czh
题意
大概就是写一个up,down,left,right的指令,操控小机器人走出迷宫,然后如果机器人遇到的这条指令,是让它走到障碍物上的,它会跳过这条指令。如果机器人到达了终点,则所有剩余指令失效。你可以通过增加和删除指令,来使得机器人走到中间,问你至少需要修改几个指令。迷宫的大小50*50。指令的长度是50
分析
ym:定义:dp[i][j][k]:当前位于点(i,j),即将执行第k条指令需要增加的最小指令数,状态数:50*50*50=125*1000 完全ok,转移:考虑BFS暴力转移,同一个点的一种状态只能出发一次
#include
using namespace std;
const int maxn = 50+3;
const int INF=0x3f3f3f3f;
const int dx[]={0,0,-1,1};
const int dy[]={-1,1,0,0};
char mp[maxn][maxn];
int sx,sy;
char cmd[maxn];
int len;
int n,m;
int dp[maxn][maxn][maxn];
int answer;
struct node{
int x,y,k;
}d[maxn];
bool ok(int x,int y){
if(((x>=0)&&x=0)&&yq;
void bfs()
{
int fx;
q.push({sx,sy,0});
dp[sx][sy][0]=0;
while(!q.empty())
{
node now=q.front();
q.pop();
if(mp[now.x][now.y]=='E')
{
answer=min(answer,dp[now.x][now.y][now.k]);
continue;
}
for(int i=0;i<4;i++)
{
int xx=now.x+dx[i],yy=now.y+dy[i];
if(!ok(xx,yy)||mp[xx][yy]=='#')
{
if(now.kdp[now.x][now.y][now.k])
{
if(dp[now.x][now.y][now.k+1]==INF)
q.push({now.x, now.y, now.k+1});
dp[now.x][now.y][now.k+1]=dp[now.x][now.y][now.k];
}
}
}
else
{
if(now.kdp[now.x][now.y][now.k])
{
if(dp[xx][yy][now.k+1]==INF)
q.push({xx, yy, now.k+1});
dp[xx][yy][now.k+1]=dp[now.x][now.y][now.k];
}
}
else
{
int last=dp[now.x][now.y][now.k]+1;
if(dp[xx][yy][now.k]>last)
{
if(dp[xx][yy][now.k]==INF)
q.push({xx, yy, now.k});
dp[xx][yy][now.k]=last;
}
}
}
}
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=0;i
C - Cameras solved by czh&ym
题意
数轴上1-n,你已经在其中k个位置有了标记,问你至少需要添加几个标记,使得任意连续r个位置,都至少有diff = 2个标记。2≤n≤100000,0≤k≤n,2≤r≤n
分析
单调队列问题
ym:处理好区间[1,r],从左往右的贪心的移动即可,因为移动一个仅能变化一个值,所以对于<=r的数量的标记都可以做
D
推组合数公式较难,组合数问题
E
题意
给定 n个点,已知选定 k个点,再多选一个点,使得 k+1 个点的凸包最大。
分析
F - Illumination solved by ym
题意
给你一个n*n的方格,里面有l个方格有灯,每个灯可以选择左右发射长度r格的光或者上下发射,问你是否有一种方案,使得每个格子,不会被同为纵向(横向)的多束光照射。n≤100
分析
ym:已填坑,2-SAT模板题确认无误(前置技能:Tarjan求联通分量)
#include
#define ll long long
using namespace std;
const int mod = 1e9+7;
const int maxn = 2000+7;
stacks;
bool vis[maxn];
int dfn[maxn],low[maxn],scc[maxn],idx;
vectorg[maxn];
int n,r,l;
int x[maxn],y[maxn];
void tarjan(int u)
{
dfn[u]=low[u]=++idx;
s.push(u);
vis[u]=true;
int v;
for(int i=0;i0)return x;
return -x;
}
int main()
{
ios::sync_with_stdio(false);
cin>>n>>r>>l;
for(int i=0;i>x[i]>>y[i];
for(int i=0;i
G - Maximum Islands solved by ym
题意
给你一个50*50 的图,里面有LWC三种字符,L代表陆地,W代表水,C代表可以由你决定是水还是陆地,问你这个图中L的联通块最多有几块
分析
ym:已填坑,首先很直观的每个L周围都应该是W(预处理),对于剩下的所有C,肯定一个L的联通块只有一个L最优,剩下的是W,所以划分为两个点集,显然不相邻的C肯定可以状态相同((x+y)%2相同的点),对每个状态为((x+y)%2相同的点)C点周围的C点建边,转化为最大独立集问题,跑一个二分图即可,注意由于将所有点都跑了二分图,最后的最大匹配要 /2
二分图的建立:
对于一个点[x,y]
,和它连通的四个点的x+y
的奇偶性是和它相反的,所以我们可以利用这个性质去建立二分图,把x+y
为奇数的放一边,为偶数的放另一边,相邻的建边,以此建立二分图
Trick:建的图满足二分图:因为状态相同((x+y)%2)的点的集合之间没有边,与其相邻的点都在另一个点集里
#include
#define ll long long
using namespace std;
const int dx[]={0,0,1,-1};
const int dy[]={1,-1,0,0};
const int maxn = 80;
int tot,head[maxn*maxn],nxt[maxn*maxn],to[maxn*maxn];
int boy[maxn*maxn],used[maxn*maxn];
char mp[maxn][maxn];
int n, m, ans, cnt,vis[maxn][maxn];
int xy[maxn][maxn];
void add(int u,int v){
to[++tot]=v;
nxt[tot]=head[u];
head[u]=tot;
}
bool found(int x){
for(int i=head[x]; i!=0; i=nxt[i]){
int u=to[i];
if(!used[u]){
used[u]=1;
if(boy[u]==0 || found(boy[u])){
boy[u]=x;
return 1;
}
}
}
return 0;
}
bool ok(int x,int y)
{
if(x>=1&&x<=n&&y>=1&&y<=m)
return 1;
return 0;
}
void dfs(int x,int y)
{
vis[x][y]=1;
for(int i=0;i<4;i++)
{
int xx=x+dx[i];
int yy=y+dy[i];
if(ok(xx,yy)&&!vis[xx][yy])
{
if(mp[xx][yy]=='L') dfs(xx,yy);
if(mp[xx][yy]=='C') mp[xx][yy]='W';
}
}
}
int main()
{
ios::sync_with_stdio(false);
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>mp[i]+1;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(!vis[i][j]&&mp[i][j]=='L')
dfs(i,j),ans++;
}
}
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
if (mp[i][j]=='C')
xy[i][j]=++cnt;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(mp[i][j]=='C'&&(i+j)%2==1) ///防止多次建边
{
for(int k=0;k<4;k++)
{
int xx=i+dx[k];
int yy=j+dy[k];
if(ok(xx,yy)&&mp[xx][yy]=='C')
add(xy[i][j],xy[xx][yy]),add(xy[xx][yy],xy[i][j]);
}
}
}
}
int sum=0;
for(int i=1;i<=cnt;i++)
{
memset(used,0,sizeof(used));
if(found(i)) sum++;
}
ans+=cnt-sum/2;
cout<
H - Paint solve czh&ym
题意
给你20w条线段,问你选其中若干条两两不相互覆盖的线段,最多能覆盖1-n这个区间中多少个点。
分析
ym:dp[i]:以第i个为结尾的最多可以覆盖的点,转移O(n),时间复杂度O(n^2),显然不可取,优化:dp[i]以第i个结尾最多可以覆盖的点,显然若按照每个线段尾端排序,二分第一个小于当前起点的区间,那么问题转化为,如何求每个区间前面的所有区间可以覆盖的最大区间,显然这个可以再单独开一个num,num[i]: 1~i区间的最大覆盖(每次从前往后考虑,不要一下天马行空,妄加推断)
czh:二分太好用了,将o(n)转化为lg(n),本来都要放弃的题,起死回生?
#include
using namespace std;
long long num[200010],n,ans;
long long dp[200010];
struct node1
{
long long s,o;
long long ma;
}node[200010];
bool cmp(node1 a,node1 b)
{
return a.onode[1].o)
{
long long maxx=num[fin(1,i-1,node[i].s)];
dp[i]=maxx+num[i];
num[i]=max(num[i-1],dp[i]);
}
else
{
dp[i]=num[i];
num[i]=max(num[i-1],dp[i]);
}
answer=max(answer,dp[i]);
}
printf("%lld\n", n-answer);
return 0;
}
I - Postman solved by ym
题意
有一个邮差员要去n家送信,他每次只能带k封信。每一家的坐标为xi,需要送mi封信,然后邮局在0点,问你最少走多少路能送完信。(n≤1e3),(k,xi,mi≤1e7)
分析
ym:排序后贪心的送即可
J - Shopping solve by czh&ym
题意
给了n件商品(n≤2e5n≤2e5),接下来有q个客户(q≤2e5q≤2e5),每个客户有一个金钱value(value≤1e18value≤1e18),和购买区间。它会从左到右挨个购买商品,一直尽可能的买每个商品,问最后剩下多少钱
分析
czh:
如果对于每个人都遍历一遍的话,最坏的情况超过了1e9。
由于是取模运算,可以找到这个数列的第一个比他小的数,模它,比它大则无视。一个数最多模lgn下。
接下来最重要的是找到第一个比V小的数的位置
ST表:用倍增的方法存储一段数的最小值,然后再以lgn的速度求出从a开始,比v小的第一个数
ym:由于一个数进行mod操作会减少至少会一半以上,所以最多会操作log次,ST表查找比当前值小的第一个值,查找区间第一个比V小的数的位置,二分常数大
#include
using namespace std;
const int maxn=200000+10;
#define LL long long
LL num[maxn];
LL Min[maxn][25];//Min[i][j]从第i个元素到i+2ej-1个元素的最小值
int fin(LL V,int s)
{
for(int i=20;i>=1;i--)
{
while(Min[s][i]<=V&&Min[s][i-1]>V)
{
s+=(1<<(i-1));
}
}
if(num[s]<=V)
return s;
else
return maxn;
}
int main()
{
int n,q,s,o;
LL V;
cin>>n>>q;
for(int i=1;i<=n;i++)
{
scanf("%I64d",&num[i]);
Min[i][0]=num[i];
}
for(int j=1;j<=20;j++)//建立倍增数组
for(int i=1;i<=n;i++)
{
if(i+(1<<(j-1))<=n)
Min[i][j]=min(Min[i][j-1],Min[i+(1<<(j-1))][j-1]);
else
Min[i][j]=Min[i][j-1];
}
for(int i=1;i<=q;i++)
{
scanf("%I64d %d %d",&V,&s,&o);
int k=fin(V,s);
while(k<=o)
{
V%=num[k];
k=fin(V,k+1);
}
printf("%I64d\n",V);
}
return 0;
}
K solved by ym
题意
给出k,和你的排名rank, 2^k个人进行比赛,进行淘汰制,每个人都有一个排名,已知两个人比赛总是排名高的人赢,问你赢的局数数量的期望值
分析
ym:留下了不会数学的眼泪==
首先设赢了i局,那么和他分到一组的2^i-1个人都比他菜,那么问题可以化简为从n个人中,已知有k个人比你菜,先要选出x个人,问这x个人都比你菜的概率
即为C(k,x)/C(n,x),问题得到解决,
由于结果过大,取log计算概率
L
Different
Summary
Ym:学弟好帅啊,帅气过三题,ym还在犯迷糊= =,该反思一下了,ym要背好自己的锅,努力提高思维啊
Czh: