罰時不好吃,一口都沒吃
形象理解這一場的 C
A.Seats \(\text{diff }20\)
對給定序列 \(S\) 找出 \(i\) 的個數,使得 \(S_{i}=0,S_{i+1}=1,S_{i+2}=0\)
#define int long long
string x;
signed main(){
int n;cin>>n;
cin>>x;
int ans=0;
for(int i=0;i<=(int)x.length()-3;++i){
if(x[i]=='#' and x[i+2]=='#' and x[i+1]=='.'){
ans++;
}
}
cout<<ans;
}
B.Traveling Takahashi Problem \(\text{diff }65\)
給定平面上 \(N\) 個點,求從原點出發按編號順序走完 \(N\) 個點並走回原點的直線距離之和
#include<bits/stdc++.h>
using namespace std;
#define int long long
long double dist(int x1,int y1,int x2,int y2){
return sqrtl((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
int n;
int nx=0,ny=0;
long double ans=0;
signed main(){
cin>>n;
for(int i=1;i<=n;++i){
int x,y;
cin>>x>>y;
ans+=dist(nx,ny,x,y);
nx=x;ny=y;
}
ans+=dist(nx,ny,0,0);
printf("%.20Lf",ans);
}
C.Spiral Rotation \(\text{diff }972\)
給定一個 \(N\times N\) 的 \(01\) 矩陣,\(N\) 為偶數,對每個 \(i\in[1,\frac{N}{2}]\) 執行如下操作
- 選擇所有 \(i\le x,y\le N-i+1\),將 \((y,N-x+1)\) 上的值替換為 \((x,y)\) 上的值
對於每一個 \(i\),對所有 \((x,y)\) 的替換同時發生,也即在每一個 \(i\) 內不存在覆蓋的情況
輸出最終的矩陣\(N\le 10^3\)
透過手摸可以發現,對單獨的一個 \(i\) 來說,相當於四個一組發生旋轉
點選檢視
但是這裡有一個 \(i\in[1,\frac{N}{2}]\),這其實相當於是分層的,先整體轉,再轉第二層和以內的,再轉第三層和以內的,相當於第一層轉一圈,第二層轉兩圈,以此類推
可以發現轉四圈不會影響結果,因此第 \(k\) 層轉 \(k\mod 4\) 圈就行了
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n;
char mp[3001][3001];
bool vis[3001][3001];
signed main(){
cin>>n;
for(int i=1;i<=n;++i){
for(int j=1;j<=n;++j){
cin>>mp[i][j];
}
}
for(int k=1;k<=n/2;++k){
for(int i=k;i<=n-k+1;++i){
if(!vis[i][k]){
if(k%4==1){
int x1=i,y1=k;
int x2=y1,y2=n-x1+1;
int x3=y2,y3=n-x2+1;
int x4=y3,y4=n-x3+1;
char tmp=mp[x4][y4];
mp[x4][y4]=mp[x3][y3];
mp[x3][y3]=mp[x2][y2];
mp[x2][y2]=mp[x1][y1];
mp[x1][y1]=tmp;
vis[x1][y1]=true;
vis[x2][y2]=true;
vis[x3][y3]=true;
vis[x4][y4]=true;
}
else if(k%4==2){
int x1=i,y1=k;
int x2=y1,y2=n-x1+1;
int x3=y2,y3=n-x2+1;
int x4=y3,y4=n-x3+1;
char tmp=mp[x4][y4];
mp[x4][y4]=mp[x3][y3];
mp[x3][y3]=mp[x2][y2];
mp[x2][y2]=mp[x1][y1];
mp[x1][y1]=tmp;
tmp=mp[x4][y4];
mp[x4][y4]=mp[x3][y3];
mp[x3][y3]=mp[x2][y2];
mp[x2][y2]=mp[x1][y1];
mp[x1][y1]=tmp;
vis[x1][y1]=true;
vis[x2][y2]=true;
vis[x3][y3]=true;
vis[x4][y4]=true;
}
else if(k%4==3){
int x1=i,y1=k;
int x2=y1,y2=n-x1+1;
int x3=y2,y3=n-x2+1;
int x4=y3,y4=n-x3+1;
char tmp=mp[x4][y4];
mp[x4][y4]=mp[x3][y3];
mp[x3][y3]=mp[x2][y2];
mp[x2][y2]=mp[x1][y1];
mp[x1][y1]=tmp;
tmp=mp[x4][y4];
mp[x4][y4]=mp[x3][y3];
mp[x3][y3]=mp[x2][y2];
mp[x2][y2]=mp[x1][y1];
mp[x1][y1]=tmp;
tmp=mp[x4][y4];
mp[x4][y4]=mp[x3][y3];
mp[x3][y3]=mp[x2][y2];
mp[x2][y2]=mp[x1][y1];
mp[x1][y1]=tmp;
vis[x1][y1]=true;
vis[x2][y2]=true;
vis[x3][y3]=true;
vis[x4][y4]=true;
}
}
}
for(int j=k+1;j<=n-k;++j){
if(!vis[k][j]){
if(k%4==1){
int x1=k,y1=j;
int x2=y1,y2=n-x1+1;
int x3=y2,y3=n-x2+1;
int x4=y3,y4=n-x3+1;
char tmp=mp[x4][y4];
mp[x4][y4]=mp[x3][y3];
mp[x3][y3]=mp[x2][y2];
mp[x2][y2]=mp[x1][y1];
mp[x1][y1]=tmp;
vis[x1][y1]=true;
vis[x2][y2]=true;
vis[x3][y3]=true;
vis[x4][y4]=true;
}
else if(k%4==2){
int x1=k,y1=j;
int x2=y1,y2=n-x1+1;
int x3=y2,y3=n-x2+1;
int x4=y3,y4=n-x3+1;
char tmp=mp[x4][y4];
mp[x4][y4]=mp[x3][y3];
mp[x3][y3]=mp[x2][y2];
mp[x2][y2]=mp[x1][y1];
mp[x1][y1]=tmp;
tmp=mp[x4][y4];
mp[x4][y4]=mp[x3][y3];
mp[x3][y3]=mp[x2][y2];
mp[x2][y2]=mp[x1][y1];
mp[x1][y1]=tmp;
vis[x1][y1]=true;
vis[x2][y2]=true;
vis[x3][y3]=true;
vis[x4][y4]=true;
}
else if(k%4==3){
int x1=k,y1=j;
int x2=y1,y2=n-x1+1;
int x3=y2,y3=n-x2+1;
int x4=y3,y4=n-x3+1;
char tmp=mp[x4][y4];
mp[x4][y4]=mp[x3][y3];
mp[x3][y3]=mp[x2][y2];
mp[x2][y2]=mp[x1][y1];
mp[x1][y1]=tmp;
tmp=mp[x4][y4];
mp[x4][y4]=mp[x3][y3];
mp[x3][y3]=mp[x2][y2];
mp[x2][y2]=mp[x1][y1];
mp[x1][y1]=tmp;
tmp=mp[x4][y4];
mp[x4][y4]=mp[x3][y3];
mp[x3][y3]=mp[x2][y2];
mp[x2][y2]=mp[x1][y1];
mp[x1][y1]=tmp;
vis[x1][y1]=true;
vis[x2][y2]=true;
vis[x3][y3]=true;
vis[x4][y4]=true;
}
}
}
}
for(int i=1;i<=n;++i){
for(int j=1;j<=n;++j){
cout<<mp[i][j];
}
cout<<'\n';
}
}
D.ABA \(\text{diff }658\)
給定一個字串 \(S\),求滿足下列條件的 \((i,j,k)\) 對數
- \(i\lt j\lt k\)
- \(S_i,S_j,S_k\) 能構成迴文
\(|S| \le 2\times 10^5\)
其實也就是找 \(S_i=S_k\) 的對數,然後中間填什麼都行
注意到兩個相等的數,如果位置分別為 \(i,j\),那麼它們中間夾著的任何一個數都能造成貢獻,也即貢獻為 \(j-i-1\)
如果列舉到一個數 \(S_i\) 時,有 \(k\) 個數(\(S_{j1},S_{j2}\cdots\))與其相等,則對答案的貢獻應該是 \((S_i-S_{j1}-1)+(S_i-S_{j2}-1)\cdots=k(S_i-1)-\sum S_{j}\)
因此維護字首和即可
#include<bits/stdc++.h>
using namespace std;
#define int long long
int possum[27];
int poscnt[27];
string x;
int ans=0;
signed main(){
cin>>x;
for(int i=0;i<=(int)x.length()-1;++i){
ans+=max(0ll,(i-1)*poscnt[x[i]-'A']-possum[x[i]-'A']);
poscnt[x[i]-'A']++;
possum[x[i]-'A']+=i;
}
cout<<ans;
}
E.3 Team Division \(\text{diff }1424\)
有三支隊伍和 \(N\) 個人,每支隊伍初始有一定人數,每個人有權值 \(B_i\),可以移動任意隊伍中的人,問三支隊伍的權值和相等所需的最小移動次數
\(N\le 100,\sum B_i\le 1500\)
首先注意到
- \(\sum B_i \not \equiv 0\pmod 3\) 無解
考慮換種思路做,清空所有隊裡的人,然後一個一個往裡填,如果當前填的人本來就在這個隊裡,則對移動次數無貢獻,否則有 \(1\) 的貢獻
然後,注意到最終每個隊的總和不超過 \(\frac{\sum B_i}{3}=500\),因此設計 \(f_{i,j,k}\) 表示放到第 \(i\) 個人,第一隊有 \(j\) 的權值和,第二隊有 \(k\) 的權值和的最少交換次數(第三隊可以用 \(\sum B_i-j-k\) 算出來)
直接列舉每個人填數即可
最後從 \(f_{n,\frac{\sum}{3},\frac{\sum}{3},\frac{\sum}{3}}\) 統計答案
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n;
int a[101],b[101];
int f[101][501][501];
int sum[5];
int tot;
signed main(){
cin>>n;
for(int i=1;i<=n;++i){
cin>>a[i]>>b[i];
sum[a[i]]+=b[i];
tot+=b[i];
}
if(tot%3!=0){
cout<<-1;
return 0;
}
memset(f,0x3f,sizeof f);
f[0][0][0]=0;
for(int i=1;i<=n;++i){
for(int j=0;j<=tot/3;++j){
for(int k=0;k<=tot/3;++k){
if(j>=b[i]) f[i][j][k]=min(f[i][j][k],f[i-1][j-b[i]][k]+(a[i]!=1));
if(k>=b[i]) f[i][j][k]=min(f[i][j][k],f[i-1][j][k-b[i]]+(a[i]!=2));
if(tot-j-k>=b[i]) f[i][j][k]=min(f[i][j][k],f[i-1][j][k]+(a[i]!=3));
}
}
}
if(f[n][tot/3][tot/3]>=0x3f3f3f3f3f3f3f3f){
cout<<-1;
return 0;
}
cout<<f[n][tot/3][tot/3]<<endl;
}
F.Road Blocked \(\text{diff }1546\)
無向圖中實現如下操作
1 i
刪除第 \(i\) 條邊2 x y
查詢 \(x,y\) 兩點的最短路徑長度\(N\le 300,Q\le 2\times 10^5\)
有刪邊,應該倒過來處理詢問
然後考慮加邊怎麼處理變化
用 DP 的思路,加了邊之後,只有經過這條邊兩個端點的路徑才能有可能改變,因為 \(N\) 比較小,因此考慮將這條新加的邊的兩個端點當中轉點,列舉所有點對的路徑來更新
類似弗洛伊德(其實就是弗洛伊德)
#include<bits/stdc++.h>
using namespace std;
#define int long long
int f[301][301];
int n,m,q;
struct edge{
int l,r,w;
}e[300*300+1];
struct opera{
int op;
int id;
int x,y;
}op[200001];
bool vis[300*300+1];
vector<int>ans;
signed main(){
ios::sync_with_stdio(false);
cin>>n>>m>>q;
for(int i=1;i<=m;++i){
int x,y,z;cin>>x>>y>>z;
e[i]={x,y,z};
}
for(int i=1;i<=q;++i){
cin>>op[i].op;
if(op[i].op==1){
cin>>op[i].id;
vis[op[i].id]=true;
}
else{
cin>>op[i].x>>op[i].y;
}
}
memset(f,0x3f,sizeof f);
for(int i=1;i<=m;++i){
if(!vis[i]){
f[e[i].l][e[i].r]=min(f[e[i].l][e[i].r],e[i].w);
f[e[i].r][e[i].l]=min(f[e[i].r][e[i].l],e[i].w);
}
}
for(int k=1;k<=n;++k){
for(int i=1;i<=n;++i){
for(int j=1;j<=n;++j){
f[i][j]=min(f[i][j],f[i][k]+f[k][j]);
}
}
}
for(int t=q;t>=1;--t){
if(op[t].op==1){
f[e[op[t].id].l][e[op[t].id].r]=min(f[e[op[t].id].l][e[op[t].id].r],e[op[t].id].w);
f[e[op[t].id].r][e[op[t].id].l]=min(f[e[op[t].id].r][e[op[t].id].l],e[op[t].id].w);
// cout<<"add "<<e[op[t].id].l<<" "<<e[op[t].id].r<<endl;
for(int i=1;i<=n;++i){
for(int j=1;j<=n;++j){
f[i][j]=min(f[i][j],f[i][e[op[t].id].l]+f[e[op[t].id].l][j]);
}
}
for(int i=1;i<=n;++i){
for(int j=1;j<=n;++j){
f[i][j]=min(f[i][j],f[i][e[op[t].id].r]+f[e[op[t].id].r][j]);
}
}
}
if(op[t].op==2){
if(f[op[t].x][op[t].y]>=0x3f3f3f3f3f3f3f3f){
ans.push_back(-1);
}
else ans.push_back(f[op[t].x][op[t].y]);
}
}
std::reverse(ans.begin(),ans.end());
for(int i:ans){
cout<<i<<"\n";
}
}
G.Road Blocked 2 \(\text{diff }2014\)
對給定無向圖判斷:
- 對所有 \(i\in[1,m]\),如果將第 \(i\) 條邊刪去,\(1\) 至 \(N\) 的最短路徑是否發生變化
\(N,M\le 2\times 10^5\)
看的第二篇題解
判斷這個其實就是判斷一條邊是不是會被所有 \(1\) 到 \(N\) 的最短路經過
我們可以根據 \(dis\) 判一下邊 \((x,y)\) 是否在最短路上:
或者反著來也行,但是這樣我們就能判斷其是否在最短路上
然後我們需要判斷一條邊是否為所有最短路的必經邊,判斷一條邊被幾條最短路徑經過是不容易的,但是判斷 \(1\) 到一個節點有幾條最短路徑是容易的
因此我們可以這樣判斷:
- 如果 \(1\) 到 \(x\) 有 \(c_x\) 條最短路徑,到 \(N\) 有 \(c_N\) 條最短路徑,\(N\) 到 \(y\) 有 \(c_y\) 條最短路徑,則如果 \(c_x\times c_y=c_N\),那麼就說明邊 \((x,y)\) 是最短路必經邊
這麼做有一個好處就是分別拿 \(1,N\) 跑兩邊最短路就能統計答案
#include <bits/stdc++.h>
using namespace std;
#define int long long
int n,m;
const int p=1e9+7;
struct __edge{
int to,w;
};
vector<__edge>e[200001];
struct ___edge{
int from,to,w;
};
___edge edge[200001];
int dis[2][200001];
bool vis[2][200001];
int cnt[2][200001];
struct node{
int id,dis;
bool operator <(const node &A)const{
return dis>A.dis;
}
};
priority_queue<node>q;
void dij(int s,int id){
memset(dis[id],0x3f,sizeof dis[id]);
memset(vis[id],0,sizeof vis[id]);
memset(cnt[id],0,sizeof cnt[id]);
dis[id][s]=0;
cnt[id][s]=1;
q.push({s,dis[id][s]});
while(!q.empty()){
node u=q.top();q.pop();
if(dis[id][u.id]<u.dis) continue;
if(vis[id][u.id]) continue;
vis[id][u.id]=true;
for(__edge i:e[u.id]){
if(dis[id][i.to]>dis[id][u.id]+i.w){
dis[id][i.to]=dis[id][u.id]+i.w;
q.push({i.to,dis[id][i.to]});
cnt[id][i.to]=cnt[id][u.id];
}
else if(dis[id][i.to]==dis[id][u.id]+i.w){
cnt[id][i.to]=(cnt[id][i.to]+cnt[id][u.id])%p;
}
}
}
}
signed main(){
cin>>n>>m;
for (int i=1;i<=m;i++){
int u,v,w;
cin>>u>>v>>w;
e[u].push_back({v,w});
e[v].push_back({u,w});
edge[i]={u,v,w};
}
dij(1,0);
dij(n,1);
for(int i=1;i<=m;++i){
int u=edge[i].from,v=edge[i].to,w=edge[i].w;
if((dis[0][u]+w+dis[1][v]==dis[0][n] and cnt[0][u]*cnt[1][v]%p==cnt[0][n])
or (dis[0][v]+w+dis[1][u]==dis[0][n] and cnt[0][v]*cnt[1][u]%p==cnt[0][n])){
cout<<"Yes\n";
}
else{
cout<<"No\n";
}
}
}