这场难度真大了好多QWQ
A.贪心,只要首尾其中一个位置是空的就代表可以平移(即相似)。
#include<bits/stdc++.h>
using namespace std;
int n,m,a[1000010];
int main(){
cin>>n>>m;
for(int i=0;i<n;i++){
cin>>a[i];
}
sort(a,a+n);
if(a[0]!=1||a[n-1]!=m){
cout<<"Yes";
}else cout<<"No";
return 0;
}
B.贪心+前缀和思想,先从小到大排序,对负数进行累加并求和,再让每个正数加上这部分后求和。具体见注释。
#include<bits/stdc++.h>
using namespace std;
#define int long long//记得开long long
int t,a[1000010],b[1000010];
signed main(){
cin>>t;
while(t--){
int n,sum=0,ans=0;//sum代表总的累加,ans维护到当前元素时负数的累加值
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
sort(a+1,a+1+n);
for(int i=1;i<=n;i++){
if(a[i]<0){
ans+=a[i-1];//累加
sum+=(a[i]+ans)*(n-i-1);//此时a[i]+ans即为累加后的a[i]真实值,将后面的(n-i-1)元素都加上当前值。
}else {
sum+=a[i]+ans;//将正数加上负数的累加值后求和
}
}
cout<<sum<<'\n';
}
return 0;
}
C.bfs+博弈论。只有当场上全是蓝色区域是蓝方才能赢。否则,如果红方能通过一次操作将所有蓝色区域染红则红方赢,否则平局。(bfs的具体操作不做赘述)
#include<bits/stdc++.h>
using namespace std;
#define int long long
int t,book[2010][2010],ans,cnt;//cnt记录红色联通块的数量,值不同即为不同的连通块
int fx[4]={0,0,1,-1};
int fy[4]={1,-1,0,0};//两个方向数组
char s[2010][2010];
signed main(){
cin>>t;
while(t--){
int n,m,cc=0;//cc为蓝色格子的数量
cin>>n>>m;
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
book[i][j]=0;
cin>>s[i][j];
if(s[i][j]=='.')cc++;
}
}
ans=0;
if(cc==n*m){
cout<<"Blue\n";
}else {
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
if(ans)break;//如果已经找到能将蓝色染完的红色联通块就不用再遍历,直接跳出
if(s[i][j]=='#'&&book[i][j]==0){
int cns=0;//cns记录当前红色联通块的染色数量
map<pair<int,int>,int>kk;//标记蓝色方块,避免重复染色
queue<pair<int,int>>q;
q.push({i,j});
book[i][j]=++cnt;
while(!q.empty()){
int x=q.front().first;
int y=q.front().second;
q.pop();
for(int ii=0;ii<4;ii++){
int newx=x+fx[ii];
int newy=y+fy[ii];
if(newx>=0&&newx<n&&newy>=0&&newy<m&&s[newx][newy]=='.'&&kk[{newx,newy}]==0){
kk[{newx,newy}]=1;
cns++;
}else if(newx>=0&&newx<n&&newy>=0&&newy<m&&s[newx][newy]=='#'&&book[newx][newy]==0){
book[newx][newy]=cnt;
q.push({newx,newy});
}
}
}
if(cns==cc){//如果全染完就跳出
ans=1;
break;
}
}
}
}
if(ans){
cout<<"Red\n";
}else {
cout<<"Draw\n";
}
}
}
return 0;
}
D.单源最短路(dij)+dp.具体见注释(迪杰斯特拉算法不再赘述,纯板子)
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod=1e9+7,N=1e6+10;
int n,m,q,x,dp[5010][5010],bk[5010],dis[1000010],book[1000010];
vector<int>V[N];
void dij(){
for(int i=1;i<=n;i++){
dis[i]=1e18;
book[i]=0;
}
dis[x]=0;
queue<pair<int,int>>q;
q.push({0,x});
while(!q.empty()){
int u=q.front().second;
q.pop();
for(int i=0;i<V[u].size();i++){
int v=V[u][i];
if(dis[v]>dis[u]+1){
dis[v]=dis[u]+1;
q.push({dis[v],v});
}
}
}
}
signed main(){
cin>>n>>m>>q>>x;
for(int i=0;i<m;i++){
int u,v;
cin>>u>>v;
V[u].push_back(v);
V[v].push_back(u);
}
dij();
for(int i=1;i<=n;i++){
bk[dis[i]]++;
}
for(int i=1;i<=5000;i++){//dp[i][j]表示到距离i为止,选了j个点的方案数
dp[i][1]=dp[i-1][1];//不选
dp[i][1]+=bk[i];//选,加上当前距离结点数
dp[i][1]%=mod;
for(int j=2;j<=i;j++){
dp[i][j]=dp[i-1][j];//不选的状态转移
dp[i][j]+=dp[i-1][j-1]*bk[i];//选的状态转移,记得乘上结点数
dp[i][j]%=mod;//取模
}
}
while(q--){
int k;
cin>>k;
cout<<dp[5000][k]<<'\n';
}
return 0;
}
E.dp。要使路径只有一条,则相邻列的连续格子必须在横向方向上只重合一格。
设a[i]表示第i列的连续格子长度
如果a[i]不为1,这只有下图两种情况(画的丑见谅QWQ)
当a[i]为1时,无论怎样都只有一个格子重合,不用考虑长度是否越界,发现与差分性质相同,于是利用差分数组维护这种情况,具体见注释。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=5e3+5;
int n,m,a[N],dp[N][N],cf[N];//cf为差分数组
void solve(){
cin>>n>>m;
for(int i=1;i<=m;i++){
cin>>a[i];
}
//dp[i][j]表示到第i列时,以j位置为开头的合法连续串是否存在,值为1就是存在,为0就是不存在
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
dp[i][j]=0;//初始化为0
}
}
for(int i=1;i<=n;i++){
dp[1][i]=1;//将第一列单独处理下
}
for(int i=2;i<=m;i++){
if(a[i]==1){//值为1的情况
for(int j=1;j<=n;j++){
cf[j]=0;//初始化差分数组
}
for(int j=1;j+a[i-1]-1<=n;j++){
if(dp[i-1][j]){//差分操作
cf[j]++;
cf[j+a[i-1]]--;
}
}
for(int j=1;j<=n;j++){
cf[j]+=cf[j-1];//将差分累加得到当前位置的长度为1的串存在信息,1表示存在,0表示不存在
if(cf[j]){
dp[i][j]=1;//转换到dp数组中去
}
}
}else {
for(int j=1;j+a[i]-1<=n;j++){
dp[i][j]|=dp[i-1][j+a[i]-1];//头部与上一列重合
if(j-a[i-1]+1>=1)dp[i][j]|=dp[i-1][j-a[i-1]+1];//尾部与上一列重合
//由于只要知道每个位置存在与否,我这里直接用了或运算,实际上加也可以
}
}
}
int ans=0;//第m列的合法位置数
for(int i=1;i<=n;i++){
if(dp[m][i])ans++;
}
cout<<ans<<'\n';
}
signed main(){
int t=1;
cin>>t;
while(t--){
solve();
}
return 0;
}
F.bfs
#include<bits/stdc++.h>
using namespace std;
int n,m,bk[2000010];//bk[i]表示第i个结点最先出现生物的时间,只要出现过就代表此后的任意时间点该位置都有可能存在生物
vector<int>V[2000010];//存图
vector<int>book[2000010];//book[i]存储了所有i时间出生的生物的出生结点
signed main(){
cin>>n>>m;
for(int i=1;i<n;i++){
int u,v;
cin>>u>>v;
V[u].push_back(v);
V[v].push_back(u);
}
for(int i=1;i<=m;i++){
int aa,tt;
cin>>aa>>tt;
book[tt].push_back(aa);
}
for(int i=0;i<=n;i++)bk[i]=-1;//初始化为-1,因为时间从0开始
queue<pair<int,int>>q,p;//定义两个队列交替操作,同一队列中的结点全部为同一时间点有生物出现在该结点,pair第一部分存结点,第二部分存父结点
for(int i=0;i<=1e8;i++){//开大点,出生时间到1e6不代表遍历时间只到1e6,中途会跳出,不会超时
if(i&1){
for(int j=0;j<book[i].size();j++){//遍历当前时间出生的所有生物
if(bk[book[i][j]]!=-1){//如果该结点被访问过,表示此时该结点可能有生物,可能碰面,输出并跳出
cout<<i*2;
return 0;
}else {//否则说明该结点此前没有可能出现生物,将该节点压入另一队列,并标记。
bk[book[i][j]]=i;
p.push({book[i][j],-1});
}
}
while(!p.empty()){//bfs,将队列中的结点
int xx=p.front().first;
int yy=p.front().second;
p.pop();
for(int j=0;j<V[xx].size();j++){
if(yy==V[xx][j])continue;
if(bk[V[xx][j]]!=-1){
if(bk[V[xx][j]]==i){
cout<<i*2+1;
}else {
cout<<(i+1)*2;
}
return 0;
}else {
bk[V[xx][j]]=i+1;
q.push({V[xx][j],xx});
}
}
}
}else {//与上半部分相同,只改变p,q;
for(int j=0;j<book[i].size();j++){
if(bk[book[i][j]]!=-1){
cout<<i*2;
return 0;
}else {
bk[book[i][j]]=i;
q.push({book[i][j],-1});
}
}
while(!q.empty()){
int xx=q.front().first;
int yy=q.front().second;
q.pop();
for(int j=0;j<V[xx].size();j++){
if(yy==V[xx][j])continue;
if(bk[V[xx][j]]!=-1){
if(bk[V[xx][j]]==i){
cout<<i*2+1;
}else {
cout<<(i+1)*2;
}
return 0;
}else {
bk[V[xx][j]]=i+1;
p.push({V[xx][j],xx});
}
}
}
}
}
return 0;
}