“山大地緯杯”第十二屆山東省ICPC大學生程式設計競賽部分個人題解
A - Seventeen
顯然\(n=1,2,3\)時無解,先手算出\(n=4,5,6,7\)時的解,然後根據\(s[i]=s[i-4]+(i-3)+i-(i-2)-(i-1)\)遞推即可
code
#include<bits/stdc++.h> using namespace std; typedef double db; const int N=50+10; string s[N]; int n; string i2s(int x) { string s; for(; x; x/=10)s.push_back(x%10+'0'); reverse(s.begin(),s.end()); return s; } int main() { s[1]=s[2]=s[3]="-1"; s[4]="3*(1+4)+2"; s[5]="(2*5+3+4)*1"; s[6]="6+5+4+3-2+1"; s[7]="(2*5+3+4)+1+6-7"; for(int i=8; i<=50; ++i)s[i]=s[i-4]+"+"+i2s(i-3)+"+"+i2s(i)+"-"+i2s(i-2)+"-"+i2s(i-1); scanf("%d",&n); cout<<s[n]<<endl; return 0; }
B - Minimum Expression
dp+大數
\(dp[i][j]\)表示前\(i\)個數分成\(j\)段的最小值,從前往後遞推即可
由於最優解的長度一般在\(\frac{n}{m}\)左右,因此可以只取\(\frac{n}{m}\)附近的幾個長度進行遞推(我取的是\([\frac{n}{m}-2,\frac{n}{m}+2]\))
這樣乍一看好像複雜度是\(O(n^3)\)的,但實際上\(m\)越大最優解的長度就越小,因此總複雜度還是\(O(n^2)\)的
程式碼用的python
code
n,m=input().split() n=int(n) m=int(m) m+=1 s=input() s='1'+s dp=[[-1 for j in range(m+1)] for i in range(n+1)] l1=n//m l2=l1+1 l=[n//m-2,n//m-1,n//m,n//m+1,n//m+2] dp[0][0]=0 for i in range(1,n+1): for j in range(1,m+1): for l1 in l: if l1>0 and i-l1>=0 and dp[i-l1][j-1]!=-1: if dp[i][j]==-1 or dp[i][j]>dp[i-l1][j-1]+int(s[i-l1+1:i+1]): dp[i][j]=dp[i-l1][j-1]+int(s[i-l1+1:i+1]) print(dp[n][m])
C - Convex
列舉多邊形上任意兩點之間的連線對A進行切分即可
code
#include<bits/stdc++.h> using namespace std; typedef double db; typedef long long ll; const int N=100+10; const db eps=1e-10; int n,m; int sign(db x) {return fabs(x)<eps?0:x<0?-1:1;} struct P { db x,y; P operator+(P b) {return {x+b.x,y+b.y};} P operator-(P b) {return {x-b.x,y-b.y};} P operator*(db b) {return {x*b,y*b};} bool operator==(P b) {return sign(x-b.x)==0&&sign(y-b.y)==0;} bool operator!=(P b) {return !(*this==b);} }; db cross(P a,P b) {return {a.x*b.y-a.y*b.x};} struct Seg {P p,q;} a[N],b[N]; vector<P> Points; bool LSI(Seg a,Seg b) { if(sign(cross({a.q-a.p}, {b.q-b.p})==0))return false; return sign(cross({a.q-a.p}, {b.p-a.p})*cross({a.q-a.p}, {b.q-a.p}))!=1; } bool LSI_S(Seg a,Seg b) {return sign(cross((P) {a.q-a.p},(P) {b.p-a.p})*cross((P) {a.q-a.p},(P) {b.q-a.p}))==-1;} P Intersection(Seg a,Seg b) { P v=a.q-a.p,w=b.q-b.p,u=a.p-b.p; db t=cross(w,u)/cross(v,w); return a.p+v*t; } bool is_valiable(Seg s) { int f=0; for(int i=1; i<=m; ++i) { db crs=sign(cross(s.q-s.p,b[i].p-s.p)); if(crs==-1)f|=1; else if(crs==1)f|=2; if(f==3)return false; } return true; } db solve(Seg s) { vector<int> vec; if(!is_valiable(s))return 0; for(int i=1; i<=n; ++i)if(LSI(s,a[i])) { P p=Intersection(s,a[i]); if(p!=a[i].p)vec.push_back(i); } if(vec.size()<2)return 0; int l=vec[0],r=vec[1]; P A=Intersection(s,a[l]),B=Intersection(s,a[r]); vector<P> poly; poly.push_back(A); for(int i=l; i!=r; i=i%n+1)poly.push_back(a[i].q); poly.push_back(B); db S=0; for(int i=2; i<=n-1; ++i)S+=cross(a[i].p-a[1].p,a[i].q-a[1].p); S/=2; db S2=0; for(int i=1; i<poly.size()-1; ++i)S2+=cross(poly[i]-poly[0],poly[i+1]-poly[0]); S2/=2; int f=0; for(int i=1; i<=m; ++i) { db crs=sign(cross(s.q-s.p,b[i].p-s.p)); if(crs==-1)f|=1; else if(crs==1)f|=2; } for(int i=0; i<poly.size(); ++i) { db crs=sign(cross(s.q-s.p,poly[i]-s.p)); if(crs==-1)f|=1; else if(crs==1)f|=2; } return f==3?S2:S-S2; } int main() { scanf("%d",&n); for(int i=1; i<=n; ++i) { db x,y; scanf("%lf%lf",&x,&y); a[i].p.x=a[(i-2+n)%n+1].q.x=x; a[i].p.y=a[(i-2+n)%n+1].q.y=y; Points.push_back({x,y}); } scanf("%d",&m); for(int i=1; i<=m; ++i) { db x,y; scanf("%lf%lf",&x,&y); b[i].p.x=b[(i-2+m)%m+1].q.x=x; b[i].p.y=b[(i-2+m)%m+1].q.y=y; Points.push_back({x,y}); } db ans=0; for(int i=0; i<Points.size(); ++i) for(int j=i+1; j<Points.size(); ++j) if(Points[i]!=Points[j])ans=max(ans,solve({Points[i],Points[j]})); printf("%f\n",ans); return 0; }
D - The Matrix
把全部的k個圖看成一個圖跑kruskal演算法即可,區別是每加入一條邊需要將邊權乘上在整個大圖上需要連線的點對數,也就相當於除了要加入的邊所在的圖以外的其他所有圖的連通塊乘積之和(因為大圖中在某張小圖裡同屬於一個聯通塊的點,在對應的維度上是互相可達的,因此它們可以縮成一個點)
code
#include<bits/stdc++.h>
using namespace std;
typedef double db;
typedef long long ll;
const int N=1e6+10,mod=998244353;
int k,n,m,siz[N],inv[N],fa[N],tot,ans;
struct E {
int u,v,c,id;
bool operator<(const E& b)const {return c<b.c;}
} e[N];
int fd(int x) {return fa[x]?fa[x]=fd(fa[x]):x;}
bool mg(int x,int y) {
int fx=fd(x),fy=fd(y);
if(fx==fy)return false;
fa[fx]=fy;
return true;
}
int main() {
inv[1]=1;
for(int i=2; i<N; ++i)inv[i]=(ll)(mod-mod/i)*inv[mod%i]%mod;
scanf("%d",&k);
for(int i=1; i<=k; ++i) {
int _n,_m;
scanf("%d %d",&_n,&_m);
for(int j=1; j<=_m; ++j) {
int u,v,c;
scanf("%d %d %d",&u,&v,&c);
u+=n,v+=n;
e[++m]= {u,v,c,i};
}
n+=_n;
siz[i]=_n;
}
tot=1;
for(int i=1; i<=k; ++i)tot=(ll)tot*siz[i]%mod;
ans=0;
sort(e+1,e+1+m);
for(int i=1; i<=m; ++i) {
int u=e[i].u,v=e[i].v,c=e[i].c,id=e[i].id;
if(!mg(u,v))continue;
ans=(ans+(ll)c*tot%mod*inv[siz[id]]%mod)%mod;
tot=(ll)tot*inv[siz[id]]%mod*(siz[id]-1)%mod;
siz[id]--;
}
printf("%d\n",ans);
return 0;
}
E - Subsegments
字首積+逆元,注意有0的情況需要分段處理
code
#include<bits/stdc++.h>
using namespace std;
typedef double db;
typedef long long ll;
const int N=5e5+10;
const ll mod=998244353;
int n;
ll a[N],b[N],x;
ll Pow(ll x,ll p) {
ll ret=1;
for(; p; p>>=1,x=x*x%mod)if(p&1)ret=ret*x%mod;
return ret;
}
ll inv(ll x) {return Pow(x,mod-2);}
ll solve(int l,int r) {
if(x==0)return (ll)(r-l+1)*(r-l+2)/2;
ll ret=0;
map<ll,int> mp;
b[l-1]=1;
for(int i=l; i<=r; ++i)b[i]=(a[i]*b[i-1])%mod;
mp[1]++;
for(int i=l; i<=r; ++i) {
ret+=mp[x*inv(b[i])%mod];
mp[inv(b[i])]++;
}
return ret;
}
int main() {
scanf("%d%lld",&n,&x);
a[0]=1;
for(int i=1; i<=n; ++i)scanf("%lld",&a[i]);
ll ans=0;
for(int i=1,j; i<=n; i=j) {
if(a[i]==0) {j=i+1; continue;}
for(j=i+1; j<=n&&a[j]!=0; ++j);
ans+=solve(i,j-1);
}
if(x==0)ans=(ll)n*(n+1)/2-ans;
printf("%lld\n",ans);
return 0;
}
G - Corona Virus
樹形dp,設\(dp[u][i]\)表示將結點\(u\)所在的子樹進行分割,且分割後結點\(u\)所在的連通塊直徑為\(i\)的方案數,自底向上依次考慮每條邊是否被割掉即可,需要字首和優化,複雜度\(O(nk)\)
code
#include<bits/stdc++.h>
using namespace std;
typedef double db;
typedef long long ll;
const int N=1e5+10,mod=1e9+7;
int n,ne,hd[N],k,f[N][101],g[N][101],buf[101];
struct E {int v,nxt;} e[N<<1];
void link(int u,int v) {e[ne]= {v,hd[u]},hd[u]=ne++;}
void dfs(int u,int fa) {
f[u][0]=1;
for(int i=0; i<=k; ++i)g[u][i]=1;
for(int i=hd[u]; ~i; i=e[i].nxt) {
int v=e[i].v;
if(v==fa)continue;
dfs(v,u);
for(int i=0; i<=k; ++i)buf[i]=0;
for(int i=0; i<=k; ++i)buf[i]=(buf[i]+(ll)f[u][i]*g[v][k]%mod)%mod;
for(int i=1; i<k; ++i)buf[i]=(buf[i]+(ll)f[u][i]*g[v][min(i-1,k-i-1)]%mod)%mod;//i>=j+1,i+j<k;
for(int j=0; j<k; ++j)buf[j+1]=(buf[j+1]+(ll)g[u][min(j,k-j-1)]*f[v][j]%mod)%mod;//i<=j,i+j<k;
for(int i=0; i<=k; ++i)f[u][i]=buf[i];
g[u][0]=f[u][0];
for(int i=1; i<=k; ++i)g[u][i]=(g[u][i-1]+f[u][i])%mod;
}
}
int main() {
memset(hd,-1,sizeof hd),ne=0;
scanf("%d %d",&n,&k);
for(int i=1; i<n; ++i) {
int u,v;
scanf("%d%d",&u,&v);
link(u,v);
link(v,u);
}
dfs(1,0);
printf("%d\n",g[1][k]);
return 0;
}
H - Counting
暴力即可
審題很關鍵
code
#include<bits/stdc++.h>
using namespace std;
typedef double db;
const int N=2000+10;
int n,m,k,T,g[N][N],vis[N][N],ans;
struct P {int x,y;} a[N];
string s[N];
int main() {
scanf("%d%d%d%d",&n,&m,&k,&T);
for(int i=1; i<=k; ++i)scanf("%d%d",&a[i].x,&a[i].y);
for(int i=1; i<=k; ++i)cin>>s[i];
for(int i=1; i<=k; ++i)g[a[i].x][a[i].y]++;
ans=0;
for(int i=1; i<=k; ++i) {
int x=a[i].x,y=a[i].y;
if(!vis[x][y]) {
vis[x][y]=1;
ans+=(g[x][y]*(g[x][y]-1))/2;
}
}
for(int i=1; i<=k; ++i) {
int x=a[i].x,y=a[i].y;
vis[x][y]=0;
}
printf("%d\n",ans);
for(int j=0; j<T; ++j) {
for(int i=1; i<=k; ++i) {
char c=s[i][j];
int dx,dy;
if(c=='L')dx=0,dy=-1;
if(c=='R')dx=0,dy=1;
if(c=='U')dx=-1,dy=0;
if(c=='D')dx=1,dy=0;
int old_x=a[i].x,old_y=a[i].y;
int new_x=a[i].x,new_y=a[i].y;
new_x+=dx;
new_y+=dy;
g[old_x][old_y]--;
g[new_x][new_y]++;
a[i].x=new_x;
a[i].y=new_y;
}
ans=0;
for(int i=1; i<=k; ++i) {
int x=a[i].x,y=a[i].y;
if(!vis[x][y]) {
vis[x][y]=1;
ans+=(g[x][y]*(g[x][y]-1))/2;
}
}
for(int i=1; i<=k; ++i) {
int x=a[i].x,y=a[i].y;
vis[x][y]=0;
}
printf("%d\n",ans);
}
return 0;
}
J - Football Match
解析幾何+座標系變換,關鍵是求出F點的座標,解方程或者二分都可以,然後通過旋轉求出其他點的座標,最後對整體進行縮放、旋轉和平移
code
#include<bits/stdc++.h>
using namespace std;
typedef double db;
typedef long long ll;
const int N=1e5+10,mod=1e9+7;
const db pi=acos(-1);
struct P {
db x,y;
P mov(db dx,db dy) {return {x+=dx,y+=dy};}
P rot(db r) {return {x*cos(r)-y*sin(r),x*sin(r)+y*cos(r)};}
P scale(db p) {return {x*p,y*p};}
} p[N],p2[N];
db dis(P a,P b) {return hypot(abs(a.x-b.x),abs(a.y-b.y));}
db bi(db l,db r) {
for(int i=1; i<200; ++i) {
db m=(l+r)/2;
P t= {m,p[4].y};
if(dis(t,p[2])<dis(t,p[4]))l=m;
else r=m;
}
return l;
}
int main() {
p[0]= {15.0,10.0}; //C
p[1]= {15.0,-10.0}; //D
p[2]= {0.0,6.0}; //E
p[4]=p[2].rot(-2*pi/5);//G
p[3]= {bi(0,p[4].x),p[4].y};//F
p[5]=p[3].rot(-2*pi/5);//H
p[7]=p[5].rot(-2*pi/5);//J
p[9]=p[7].rot(-2*pi/5);//L
p[11]=p[9].rot(-2*pi/5);//N
p[6]=p[4].rot(-2*pi/5);//I
p[8]=p[6].rot(-2*pi/5);//K
p[10]=p[8].rot(-2*pi/5);//M
for(int i=0; i<12; ++i)p[i]=p[i].mov(15,10);
int T;
for(scanf("%d",&T); T--;) {
db x1,y1,x2,y2;
scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
db scale=hypot(abs(x1-x2),abs(y1-y2))/20;
db dx=x1,dy=y1;
db r=atan2(y2-y1,x2-x1)-atan2(1,0);
for(int i=0; i<12; ++i)p2[i]=p[i].scale(scale).rot(r).mov(dx,dy);
for(int i=0; i<12; ++i)printf("%f %f%c",p2[i].x,p2[i].y," \n"[i==11]);
}
return 0;
}
K - Coins
x小的時候dp,x大的時候直接輸出A
code
#include<bits/stdc++.h>
using namespace std;
typedef double db;
const int N=1e6+50;
int dpa[N],dpb[N],n;
const int a[]= {2,3,17,19};
const int b[]= {5,7,11,13};
int main() {
memset(dpa,0x3f,sizeof dpa);
memset(dpb,0x3f,sizeof dpb);
dpa[0]=dpb[0]=0;
for(int i=0; i<4; ++i)
for(int j=0; j<N-50; ++j) {
dpa[j+a[i]]=min(dpa[j+a[i]],dpa[j]+1);
dpb[j+b[i]]=min(dpb[j+b[i]],dpb[j]+1);
}
int T;
for(scanf("%d",&T); T--;) {
int x;
scanf("%d",&x);
if(x==1)printf("-1");
else if(x>100000)printf("A");
else {
int t=dpa[x]-dpb[x];
if(t<0)printf("A");
else if(t==0)printf("both");
else printf("B");
}
printf("\n");
}
return 0;
}
M - Travel Round the Grid
毒瘤題,思路不難,但實現起來巨麻煩,有多種邊界情況需要考慮
先假設起點在左上角,逐行往下擴充,之後平移到合法的位置即可(可能還需要上下翻轉)
code
#include<bits/stdc++.h>
using namespace std;
typedef double db;
typedef long long ll;
const int N=1e6+10;
int n,m,X,Y,k,tl,row,offset;
char s[N];
void mov() {
int x=X,y=Y,lx=X,rx=X,ly=Y,ry=Y;
for(int i=0; i<tl; ++i) {
if(s[i]=='L')y--;
else if(s[i]=='R')y++;
else if(s[i]=='U')x--;
else if(s[i]=='D')x++;
lx=min(lx,x);
rx=max(rx,x);
ly=min(ly,y);
ry=max(ry,y);
}
int dx=max(0,1-lx)-max(0,rx-n);
int dy=max(0,1-ly)-max(0,ry-m);
x=X+dx,y=Y+dy;
offset=-1;
for(int i=0; i<tl; ++i) {
if(s[i]=='L')y--;
else if(s[i]=='R')y++;
else if(s[i]=='U')x--;
else if(s[i]=='D')x++;
if(x==X&&y==Y) {
offset=(i+1)%tl;
break;
}
}
if(!~offset) {
for(int i=0; i<tl; ++i) {
if(s[i]=='U')s[i]='D';
else if(s[i]=='D')s[i]='U';
}
x=X,y=Y,lx=X,rx=X,ly=Y,ry=Y;
for(int i=0; i<tl; ++i) {
if(s[i]=='L')y--;
else if(s[i]=='R')y++;
else if(s[i]=='U')x--;
else if(s[i]=='D')x++;
lx=min(lx,x);
rx=max(rx,x);
ly=min(ly,y);
ry=max(ry,y);
}
int dx=max(0,1-lx)-max(0,rx-n);
int dy=max(0,1-ly)-max(0,ry-m);
x=X+dx,y=Y+dy;
for(int i=0; i<tl; ++i) {
if(s[i]=='L')y--;
else if(s[i]=='R')y++;
else if(s[i]=='U')x--;
else if(s[i]=='D')x++;
if(x==X&&y==Y) {
offset=(i+1)%tl;
break;
}
}
}
}
int main() {
int T;
for(scanf("%d",&T); T--;) {
scanf("%d%d%d%d%d",&n,&m,&X,&Y,&k);
tl=0;
row=(k+m-1)%m;
if(k==2) {
if(m>1)s[tl++]='R',s[tl++]='L';
else s[tl++]='D',s[tl++]='U';
} else if(m==2) {
s[tl++]='R';
for(int i=1; i<=k/m-1; ++i)s[tl++]='D';
s[tl++]='L';
for(int i=1; i<=k/m-1; ++i)s[tl++]='U';
} else if((n&1)&&(k+m-1)/m==n) {
s[tl++]='R';
for(int t=0; t<k/(2*m)-1; ++t) {
for(int i=1; i<=m-2; ++i)s[tl++]='R';
s[tl++]='D';
for(int i=1; i<=m-2; ++i)s[tl++]='L';
s[tl++]='D';
}
for(int i=1; i<=m-2; ++i)s[tl++]='R';
s[tl++]='D';
if(k==n*m) {
s[tl++]='D';
for(int t=0; t<m/2-1; ++t) {
s[tl++]='L';
s[tl++]='U';
s[tl++]='L';
s[tl++]='D';
}
s[tl++]='L';
s[tl++]='U';
} else {
for(int t=0; t<m-1-k%m; ++t)s[tl++]='L';
for(int t=0; t<k%m/2; ++t) {
s[tl++]='L';
s[tl++]='D';
s[tl++]='L';
s[tl++]='U';
}
}
for(int i=1; i<=n-2; ++i)s[tl++]='U';
} else if(k%(2*m)==2) {
s[tl++]='R';
int cnt=0;
for(int t=0; t<(k-1)/(2*m); ++t) {
for(int i=1; i<=m-2; ++i)s[tl++]='R';
s[tl++]='D';
for(int i=1; i<=m-2; ++i)s[tl++]='L';
s[tl++]='D';
cnt++;
}
s[tl++]='L';
for(int i=1; i<=k/m; ++i)s[tl++]='U';
} else {
s[tl++]='R';
for(int t=0; t<(k-1)/(2*m); ++t) {
for(int i=1; i<=m-2; ++i)s[tl++]='R';
s[tl++]='D';
for(int i=1; i<=m-2; ++i)s[tl++]='L';
s[tl++]='D';
}
for(int i=1; i<=((k-1)%(2*m)+1)/2-2; ++i)s[tl++]='R';
s[tl++]='D';
for(int i=1; i<=((k-1)%(2*m)+1)/2-2; ++i)s[tl++]='L';
s[tl++]='L';
for(int i=1; i<=((k-1)/(2*m))*2+1; ++i)s[tl++]='U';
}
mov();
for(int t=offset; t<offset+tl; ++t)putchar(s[t%tl]);
puts("");
}
return 0;
}