1.日志统计
法1:
用双指针i,j枚举时间段,如果i-j>=d,就将超出范围的cnt[]--,且j++,这样就会始终有一个长度为d的区间
vector<int> f[t] 记录了在t时刻里获赞的所有编号
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e5+10;
vector<int> f[N];//f[t]记录了所有在t时刻获赞的编号
int cnt[N];//cnt[id]表示id的获赞数,是动态的,超过了时间长度d会--
bool st[N];//st[id]判断编号id是不是热帖
int main()
{
int n,d,k;
cin>>n>>d>>k;
for(int i=0;i<n;i++)
{
int ts,id;
cin>>ts>>id;
f[ts].push_back(id);
}
for(int i=0,j=0;i<=1e5;i++)//i,j维护一个长度为d的时间区间
{
if(i-j>=d)//长度超过d了,原来j时刻的那些获赞就会--
{
for(auto x:f[j])
{
cnt[x]--;
}
j++;
}
for(auto x:f[i])//遍历所有在i时刻获赞的编号
{
cnt[x]++;
if(cnt[x]>=k) st[x]=true;//如果长度一旦>=k,就说明曾经是热帖
}
}
for(int i=0;i<N;i++)
{
if(st[i]) cout<<i<<endl;
}
return 0;
}
法2:
pair<int,int> logs记录 ,获赞时刻和id
将所有logs排个序,pair会按照先first再second的顺序排,即先按时刻排序,时刻相同再按编号排序。枚举所有的logs记录,i在前j在后,是两条记录,如果这两条记录的时间跨度>=d了,则cnt[logs[j].id]--,j往后走,直到两条记录的时间跨度<d。
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e5+10;
int n,d,k;
typedef pair<int, int> PII;
#define x first
#define y second
PII logs[N];//记录每条获赞记录的时刻和编号
int cnt[N];//记录每条记录的当前的获赞数量,动态变化,一旦时间跨度>=d就--
bool st[N];//记录是否是热帖
int main()
{
cin>>n>>d>>k;
for(int i=0;i<n;i++)
{
int ts,id;
cin>>ts>>id;
logs[i]={ts,id};
}
sort(logs,logs+n);
for(int i=0,j=0;i<n;i++)//枚举每两条时间跨度<d的记录
{
while(logs[i].x-logs[j].x>=d)
{
cnt[logs[j].y]--;
j++;
}
cnt[logs[i].y]++;
if(cnt[logs[i].y]>=k)
{
st[logs[i].y]=true;
}
}
for(int i=0;i<N;i++)
{
if(st[i]) cout<<i<<endl;
}
return 0;
}
2.献给阿尔吉侬的花束
板子题,直接看代码注释即可
/*
从起点S开始走,走到E结束,看看最短距离,由于有障碍物的阻隔,可能会走不到E点
*/
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
#define x first
#define y second
typedef pair<int, int> PII;
const int N = 210;
char g[N][N];
int dist[N][N];//dist(i,j)表示(i,j)到起点的距离
int n,m;
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
int bfs(int sx,int sy,int ex,int ey)
{
memset(dist,-1,sizeof dist);//dist为-1表示还没更新或者根本到不了
dist[sx][sy]=0;
queue<PII> q;
q.push({sx,sy});
while(q.size())
{
PII t=q.front();
q.pop();
for(int i=0;i<4;i++)
{
int x=t.x+dx[i],y=t.y+dy[i];
if(x<0||x>=n||y<0||y>=m) continue;
if(g[x][y]=='#') continue;
if(dist[x][y]!=-1) continue;
dist[x][y]=dist[t.x][t.y]+1;
if(x==ex&y==ey) return dist[x][y];
q.push({x,y});
}
}
return -1;
}
int main()
{
int T;
cin>>T;
while(T--)
{
cin>>n>>m;
for(int i=0;i<n;i++) cin>>g[i];
//找起点终点
int sx,sy,ex,ey;
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
if(g[i][j]=='S') sx=i,sy=j;
if(g[i][j]=='E') ex=i,ey=j;
}
}
int d=bfs(sx,sy,ex,ey);
if(d==-1) cout<<"oop!"<<endl;
else cout<<d<<endl;
}
return 0;
}
3.红与黑
1.dfs代码
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 25;
int n,m;
char g[N][N];
bool st[N][N];
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
int dfs(int x,int y)
{
int cnt=1;
st[x][y]=true;
for(int i=0;i<4;i++)
{
int a=x+dx[i],b=y+dy[i];
if(a>=0 && a<m && b>=0 && b<n && g[a][b]=='.' && !st[a][b])
{
cnt+=dfs(a,b);
}
}
return cnt;
}
int main()
{
while (cin>>n>>m,m||n)
{
for(int i=0;i<m;i++) cin>>g[i];
//找起点
int sx,sy;
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)
{
if(g[i][j]=='@')
{
sx=i;
sy=j;
break;
}
}
}
memset(st, 0, sizeof st);//记得每次结束后更新st
cout<<dfs(sx,sy)<<endl;
}
return 0;
}
2.bfs代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
typedef pair<int, int> PII;
#define x first
#define y second
const int N = 25;
int n,m;
char g[N][N];
bool st[N][N];
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
int bfs(int sx,int sy)
{
int ans=1;
queue<PII> q;
q.push({sx,sy});
st[sx][sy]=true;
while(q.size())
{
PII t=q.front();
q.pop();
for(int i=0;i<4;i++)
{
int a=t.x+dx[i],b=t.y+dy[i];
if(a<0||a>=m||b<0||b>=n) continue;
if(g[a][b]=='#') continue;
if(st[a][b]) continue;
ans++;
q.push({a,b});
st[a][b]=true;
}
}
return ans;
}
int main()
{
while(cin>>n>>m,n||m)
{
for(int i=0;i<m;i++) cin>>g[i];
int sx,sy;
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)
{
if(g[i][j]=='@')
{
sx=i;
sy=j;
break;
}
}
}
cout<<bfs(sx,sy)<<endl;
memset(st,0,sizeof st);
}
return 0;
}
4.交换瓶子
置换群法:
每个瓶子向它应该在的位置连一条边
如图所示 瓶子3应该在位置3,当前位置3放的是瓶子2,所以3和2之间连边
瓶子1应该在位置1,但现在位置1放的是瓶子3,所以1和3连边
瓶子2本应该放在位置2,但现在位置2放的是瓶子1,所以2和1连边
4,5同理
一共n个点,连n条边
当前图的性质 n个点,n条边,每个点出度入度都为1
这样的图必然是一堆环,称为一个置换
我们希望最终变成的是n个自环,每个点指向自己
初始环k个。终点环n个
情况1:交换同一个环内的点
环外的点没有任何变化
环内会裂开成俩环(这是因为两个点的出边改变了),环的数量+1
如图:交换1,3
情况2:交换不同环内的点
结果会是将两个环合并
综上:
我们每次操作都会是把两个环合并成1个或者是把一个环分裂
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 10010;
bool st[N];//找环
int b[N];
int n;
int main()
{
cin>>n;
for(int i=1;i<=n;i++) cin>>b[i];
int cnt=0;
for(int i=1;i<=n;i++)
{
if(!st[i])//在一个新的环里面
{
cnt++;
for(int j=i;!st[j];j=b[j])//j指向的是j位置的这个瓶子b[j]
{
st[j]=true;
}
}
}
cout<<n-cnt<<endl;
return 0;
}
成2个
初始k个环,最终n个环,每次操作最多增加一个环,所以最少n-k次操作
5.完全二叉树的权值
完全二叉树:
第i层的开头下标是2^(i-1)次方,结尾是2^i-1
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e5+10;
int a[N];
int Log[N];
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
long long maxv=-1e18;
int i=1,d=1;
int ans=1;
while(i<=n)
{
long long s=0;
while(i <= ((1<<d)-1))
{
s+=a[i];
i++;
}
if(s>maxv)
{
maxv=s;
ans=d;
}
d++;
}
cout<<ans<<endl;
return 0;
}
6.地牢大师
和之前的题一样,只不过从2维变成了3维
结构体存坐标(x,y,z)
先找起点,终点,用bfs搜索路径
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 110;
char g[N][N][N];
int dist[N][N][N];//存储(x,y,z)到起点的最短距离
int dx[6]={1,-1,0,0,0,0};
int dy[6]={0,0,1,-1,0,0};
int dz[6]={0,0,0,0,1,-1};
struct Point{
int x,y,z;
};
int l,r,c;
int bfs(Point start,Point end)
{
memset(dist,-1,sizeof dist);//dist为-1说明还没更新,或者有阻挡到不了
dist[start.x][start.y][start.z]=0;
queue<Point> q;
q.push(start);
while(q.size())
{
Point t=q.front();
q.pop();
for(int i=0;i<6;i++)
{
int x=t.x+dx[i],y=t.y+dy[i],z=t.z+dz[i];
if(x<0||x>=l||y<0||y>=r||z<0||z>=c) continue;
if(g[x][y][z]=='#') continue;
if(dist[x][y][z]!=-1) continue;
dist[x][y][z]=dist[t.x][t.y][t.z]+1;
if(x==end.x&&y==end.y&&z==end.z) return dist[x][y][z];
q.push({x,y,z});
}
}
return -1;
}
int main()
{
Point start,end;
while(cin>>l>>r>>c,l||r||c)
{
for(int i=0;i<l;i++)
{
for(int j=0;j<r;j++)
{
for(int k=0;k<c;k++)
{
cin>>g[i][j][k];
if(g[i][j][k]=='S') start={i,j,k};
else if(g[i][j][k]=='E') end={i,j,k};
}
}
}
if(bfs(start,end)==-1) cout<<"Trapped!"<<endl;
else cout<<"Escaped in "<<bfs(start,end)<<" minute(s)."<<endl;
}
return 0;
}
7.全球变暖
用1个total来记录连通块中点的个数
用1个bound来记录被淹没的点的数量(一个点周围有'.'即被淹没)
如果bound==total则这个岛屿全部被淹没掉
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 1010;
typedef pair<int, int> PII;
#define x first
#define y second
int n;
char g[N][N];
bool st[N][N];//此处st标记已经遍历过的陆地'#'
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
void bfs(int x,int y,int& total,int& bound)
{
queue<PII> q;
q.push({x,y});
st[x][y]=true;
while(q.size())
{
PII t=q.front();
q.pop();
total++;//记录连通块中点的个数
bool flag=false;//表示这个岛屿是否被淹没
for(int i=0;i<4;i++)
{
int x=t.x+dx[i],y=t.y+dy[i];
if(x<0||x>=n||y<0||y>=n) continue;
if(g[x][y]=='.')//周围这个点是大海,标记一下已沉没
{
flag=true;
continue;
}
if(st[x][y]) continue;//如果周围这个点是陆地,且已经遍历过,就不用放在q中
q.push({x,y});
st[x][y]=true;
}
if(flag) bound++;
}
}
int main()
{
cin>>n;
int res=0;
for(int i=0;i<n;i++) cin>>g[i];
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
if(g[i][j]=='#'&&!st[i][j])
{
int total=0,bound=0;
bfs(i,j,total,bound);
//cout<<total<<" "<<bound<<endl;
if(total==bound) res++;
}
}
}
cout<<res<<endl;
return 0;
}
8.大臣的旅费
求树的直径:
方法:
1.任取一点x
2.找到距离x最远的点y
3.从y开始再进行一次搜索,找到距离y最远的点,此点距离y的距离就是直径
因为y是距离x最远的点,首先它一定是个端点
证明:y一定是树的直径上的端点
假设y不是树的直径的端点:
如下两种情况:
情况1:
xy这个路径不是树的直径但与树的直径uv有交点
因为y是距离x最远的点,所以边4+边1>边2+边1,所以边4>边1
所以边4+边3>边2+边3=uv=树的直径
所以存在>树的直径的边,显然不可能
情况2:
xy这个路径不是树的直径且与树的直径uv没有交点
y是距离x最远的点,所以边3+边5<边2
所以边5<边2 所以边5<边2+边3
所以边2+边3+边4>边5+边4=树的直径
所以存在>树的直径的边,显然不可能
因为y是直径一端的端点,所以第二遍距离y点最远的距离就是树的直径的距离
最终树的直径是dist的话花费就是
(1+10)+(2+10)+(3+10)+...+(dist+10)
=10*dist+(1+dist)*dist/2
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long LL;
const int N = 1e5+10,M = 2*N;
int h[N],e[M],ne[M],w[M],idx;
int n;
int dist[N];
void add(int a,int b,int c)
{
e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
void dfs(int u,int distance,int father)
{
dist[u]=distance;
for(int i=h[u];i!=-1;i=ne[i])
{
int j=e[i];
if(j!=father)
{
dfs(j,distance+w[i],u);
}
}
}
int main()
{
memset(h, -1, sizeof h);
cin>>n;
for(int i=0;i<n-1;i++)
{
int a,b,c;
cin>>a>>b>>c;
add(a, b, c);
add(b ,a, c);
}
int u=1;
dfs(u,0,-1);
for(int i=2;i<=n;i++)
{
if(dist[i]>dist[u]) u=i;
}
dfs(u,0,-1);
for(int i=1;i<=n;i++)
{
if(dist[i]>dist[u]) u=i;
}
printf("%lld",dist[u]*10+(LL)(dist[u]+1)*dist[u]/2);
return 0;
}