E.Three Substrings
现在贪心到自闭(题解说的很对,依赖直觉使用贪心是很危险的,应该有所证明[至少应该多构思一些样例,验证贪心是否是伪算法])。
题解的方法为:基于a的位置完全搜索b和c的相对位置来解决该问题。
1.我们可以发现,假定一个a和b的匹配情况,相应位置可以表示为[i-j](很神奇的一种思路,字符串方面比较弱,确实是第一次接触这种操作)。
2.再次基础上,我们可以预处理ab,ac,bc串所有的匹配情况(复杂度O(n2))。
3.在次之后,枚举b和c相对于a的位置,然后就可以在(O(n2))复杂度内求解。
PS:感觉真的是暴力的艺术。。。感觉和当初学八皇后的时候,用(x-y)表示斜边的方式有异曲同工之妙,吹爆。
#include
#define lowbit(x) x&(-x)
using namespace std;
const int N = 5e4;
const int M = 2e3;
bool ab[N << 1],ac[N << 1],bc[N << 1];
inline bool check(char x,char y){
return (x == '?' || y == '?' || x == y);
}
void init(string a,string b,int n,int m,bool *vis){
for(int i = 0;i < n;++i)
for(int j = 0;j < m;++j)
if(!check(a[i],b[j])) vis[i-j+N] = 1;
return ;
}
int main(){
int A,B,C;
string a,b,c;
cin >> a;A = a.size();
cin >> b;B = b.size();
cin >> c;C = c.size();
init(a,b,A,B,ab);
init(a,c,A,C,ac);
init(b,c,B,C,bc);
int ans = 3*M;
for(int i = -(M << 1);i <= (M << 1);++i){ // 要考虑bc都在a前面-4000距离以及bc都在a后面4000距离
for(int j = -(M << 1);j <= (M << 1);++j){
if(!ab[i+N] && !ac[j+N] && !bc[j-i+N]){
int l = min(0,min(i,j));
int r = max(A,max(B+i,C+j));
ans = min(ans,r-l);
}
}
}
printf("%d\n",ans);
return 0;
}
F.Fractal Shortest Path
粗看感觉想递归,但确实一点思路没有,看完题解应该算半懂。。。
1.首先明确一点,因为我们算的是曼哈顿距离,所以在没有黑方块阻挡的情况下,距离直接可算。
2.考虑有黑方块阻挡的情况,我们可以发现,无论中间有多少个黑方块,我们都可以把它们连在一起,当做一个黑方块来看,对答案结果并不会有影响。
3.对于非水平线上的位置,因为可以通过边角连接,所以答案必为曼哈顿距离。
4.对于水平线上的位置,有可能要考虑黑色方块阻挡导致绕路的情况。
5.检验是否有必须绕路的黑方块,可以通过等比例缩小问题来解决(我也不知道这里有没有理解错)
#include
#define lowbit(x) x&(-x)
using namespace std;
typedef long long ll;
const int N = 5e4;
const int M = 2e3;
ll f[35];
void f_table(){
f[0] = 1;
for(int i = 1;i <= 30;++i) f[i] = f[i-1]*3;
return ;
}
int main(){
f_table();
int n;
ll x1,y1,x2,y2;
scanf("%d",&n);
for(int t = 0;t < n;++t){
scanf("%lld%lld%lld%lld",&x1,&y1,&x2,&y2);
x1--;y1--;x2--;y2--;
ll ans = 0,flag = 0,sx,sy,ex,ey;
for(int i = 30;i >= 0;--i){
sx = x1/f[i],sy = y1/f[i],ex = x2/f[i],ey = y2/f[i];
if(sx != ex && sy != ey) break;
if(sx == ex && sx % 3 == 1 && abs(sy-ey) > 1){
ll xl = x1/f[i]*f[i]-1;
ll xr = xl+f[i]+1;
ll dx = min(abs(xl-x1)+abs(xl-x2),abs(xr-x1)+abs(xr-x2));
ll dy = abs(y1-y2);
ans = dx+dy;
flag = 1;
break;
}
if(sy == ey && sy % 3 == 1 && abs(sx-ex) > 1){
ll yl = y1/f[i]*f[i]-1;
ll yr = yl+f[i]+1;
ll dx = abs(x1-x2);
ll dy = min(abs(yl-y1)+abs(yl-y2),abs(yr-y1)+abs(yr-y2));
ans = dx+dy;
flag = 1;
break;
}
}
if(flag) printf("%lld\n",ans);
else printf("%lld\n",abs(x1-x2)+abs(y1-y2));
}
return 0;
}
E.Divisible Substring
打的时候想到了对前缀取模,但处理失误处理成了前缀和以及没有考虑到前缀取模的方式,只适用于P
和10互质的情况,所以2和5要单独考虑。
1.当P和10互质时,我们可以发现当两个前缀的模相同时,后者去掉前者可以使模等于0(即整除P),所以我们可以在O(n)的时间复杂度内解决问题(注意:此处前缀应该理解为后缀,考虑数字逐渐变大)。
2.当P = 2或5时,我们直接根据子串最右端的值即可计算,时间复杂度也是O(n)。
PS1:个人理解互质与非互质的不同情况,是因为字符串转化为整数是以10进制为基础的,因此,与10互质的P,不会影响数字的整除情况,而P = 2或5时,则会造成影响。
此处以4为例子,如果4*10再加上任意一个个位数,他本质上不会因为与10互质的P导致除法有误,而如果P = 4,在44的情况下,他会把前面的4当做40处理,导致结果多算了1次贡献。
(很奇妙的互质,当然我现场写的时候,考虑的比这少多了。。。水平不够)
PS2:有大佬用优化常数的dp过了,大佬tql,感觉自己dp都白学了(因为理解不了,先暂时做一个搬运。。。)
#include
using namespace std;
typedef long long ll;
const int N = 1e4+5;
const int M = 2e3;
int num[N];
int main(){
int n,p;
ll ans = 0;
string s;
scanf("%d%d",&n,&p);
cin >> s;
if(p == 2 || p == 5){
for(int i = 0;i < n;++i){
if((s[i]-'0') % p == 0) ans += i+1;
}
}
else{
int last = 0,base = 1;num[0]++;
for(int i = n-1;i >= 0;--i){
int t = (s[i]-'0')%p*base%p;
(base *= 10) %= p;
(last += t) %= p;
ans += num[last];
num[last]++;
}
}
printf("%lld\n",ans);
return 0;
}
// 搬运大佬的dp,还没懂
#include
using namespace std;
#define ll long long
#pragma GCC optimize("O3")
#pragma GCC optimize("Ofast")
#pragma GCC target("avx,avx2,fma")
#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,tune=native")
#define usecppio ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define all(x) ((x).begin()),((x).end())
#define eps 1e-7
#pragma GCC optimize ("unroll-loops")
string s;
int transition[10][10101], n, p;
ll ans;
int dp[2][10101];
ll modpow(ll x, ll y)
{
ll res = 1;
x = x % p;
while (y > 0)
{
if (y & 1)
res = (res*x) % p;
y = y>>1;
x = (x*x) % p;
}
return res;
}
int32_t main()
{
usecppio
cin >> n >> p >> s;
int inv = modpow(10, p-2);
if (p < 10)
{
for (int j = 0; j<10; j++)
for (int i = 0; i<p; i++)
transition[j][i] = ((i*10)+j)%p;
int r = 0;
for (int i = 0; i<n; i++)
{
int u = s[i]-'0';
r = 1-r;
memset(dp[r],0,sizeof(dp[r]));
for (int j = 0; j<p; j++)
{
int k = transition[u][j];
dp[r][k] += dp[1-r][j];
}
dp[r][transition[u][0]]++;
ans += dp[r][0];
}
cout << ans << '\n';
}
else
{
for (int j = 0; j<10; j++)
for (int i = 0; i<p; i++)
transition[j][i] = (((i-j+p)%p)*(inv))%p;
int r = 0;
for (int i = 0; i<n; i++)
{
register int u = s[i]-'0';
r = 1-r;
for (int j = 0; j<p; j++)
{
int k = transition[u][j];
dp[r][j] = dp[1-r][k];
}
dp[r][u]++;
ans += dp[r][0];
}
cout << ans << '\n';
}
}
F.Removing Robots
题意很简单,问最后残留集合的方案数,感觉非常像线性dp的模型,但现场没写这题。
1.考虑设置一个dp数组,记为从后往前考虑到机器人i时的方案数。
2.假设该机器人不会被启动,那么dp[i]的方案数可以直接从dp[i+1]处继承。
3.假设该机器人被启动,那么之后一系列被影响的机器人将被启动,我们可以找到第一个没被启动的机器人,设为R[i],从dp[R[i]]处继承。
4.题解中给出了线段树的方式来预处理R[i](这块真没想到。。。Orz)
所以本质上,这题就转化成了一个维护区间最值的线段树和一个简单线性dp。
PS:下面还有一个看到的奆佬写的不用线段树的写法,感觉很妙就贴上来(感觉是用栈的思路,很奇妙)。
#include
#pragma GCC optimize(2)
#define PAIR pair
#define x first
#define d second
#define lson rt << 1,l,m
#define rson rt << 1|1,m+1,r
using namespace std;
typedef long long ll;
const int N = 2e5+5;
const ll M = 998244353;
ll dp[N];
int R[N],b[N],t[N << 2];
PAIR a[N];
inline void pushup(int rt){
t[rt] = max(t[rt << 1],t[rt << 1|1]);
return ;
}
inline void build(int rt,int l,int r){
if(l == r){
t[rt] = l;
return ;
}
int m = (l+r) >> 1;
build(lson);build(rson);
pushup(rt);
return ;
}
inline int query(int rt,int l,int r,int L,int R){
if(L <= l && r <= R) return t[rt];
int res = 0,m = (l+r) >> 1;
if(L <= m) res = max(res,query(lson,L,R));
if(R > m) res = max(res,query(rson,L,R));
return res;
}
inline void add(int rt,int l,int r,int pos,int val){
t[rt] = max(t[rt],val);
if(l == r){
t[rt] = val;
return ;
}
int m = (l+r) >> 1;
if(pos <= m) add(lson,pos,val);
else add(rson,pos,val);
return ;
}
int main(){
int n,cnt;
scanf("%d",&n);
for(int i = 1;i <= n;++i) scanf("%d%d",&a[i].x,&a[i].d),b[i] = a[i].x;
sort(a+1,a+n+1);build(1,1,n);
sort(b+1,b+n+1);cnt = unique(b+1,b+n+1)-b-1;
for(int i = n;i;--i){
int pos = upper_bound(b+1,b+cnt+1,a[i].x+a[i].d-1)-b-1;
R[i] = query(1,1,n,i,pos);
add(1,1,n,i,R[i]);
}
dp[n+1] = 1;
for(int i = n;i;--i) dp[i] = (dp[i+1]+dp[R[i]+1])%M;
printf("%lld\n",dp[1]);
return 0;
}
// 奆佬代码
#include
#define fi first
#define se second
#define ll long long
#define pb push_back
#define mp make_pair
#define mt make_tuple
using namespace std;
const ll MOD = 998244353;
ll dp[200010];
pair<ll,ll> a[200010];
vector<pair<int,ll> > stek;
ll INF = 1e12;
bool sortiraj(pair<ll,ll> x1, pair<ll,ll> x2)
{
return x1.fi < x2.fi;
}
int main()
{
ios_base::sync_with_stdio(false);
cin.tie(NULL);
int n;
cin >> n;
for(int i = 0; i < n; i++)
cin >> a[i].fi >> a[i].se;
sort(a, a + n, sortiraj);
dp[n] = 1;
stek.pb(mp(n, INF));
for(int i = n - 1; i >= 0; i--)
{
int index;
while(stek[stek.size()-1].se < a[i].fi + a[i].se) stek.pop_back();
index = stek[stek.size()-1].fi;
dp[i] = (dp[i+1] + dp[index])%MOD;
stek.pb(mp(i, a[i].fi));
}
cout << dp[0];
return 0;
}
F.Yakiniku Optimization Problem
自己真的写懵逼了,看了题解发现是真的秒
solution1:
因为答案求的是最小时间,所以我们可以用二分答案的方式求解:
1.如果某个点c为X,那么可以将其转化为圆心为(x,y),半径为 T X \frac{T}{X} XT的圆,那么问题就变成了,在n个圆的集合中,是否存在一个点,使得其能满足大于等于k个点的时间。
2.这个点只有可能再两种地方
(1)就是n个点中的其中一个点。
(2)是某两个圆的交点。
(个人感觉这类似于一种贪心的思想,但没想明白怎么证明)。
3.采用二分答案T的方式,找出最小值即可。
PS1:偷到了奆佬求两圆交点的模板,自己稍微改了改。
PS2:求两圆相交的点(翻到的大佬blog)
#include
using namespace std;
typedef long double ld;
const int N = 1e2+5;
const ld eps = 1e-10;
struct P{
ld x,y;
P(){x = 0;y = 0;}
P(ld _x,ld _y){x = _x;y = _y;}
ld dis(){return sqrt(x*x+y*y);}
P norm(){ld t = (*this).dis();return P(x/t,y/t);}
P rot(){return P(-y,x);}
// P add(P t){return P(x+t.x,y+t.y);}
// P sub(P t){return P(x-t.x,y-t.y);}
// P mul(ld t){return P(x*t,y*t);}
P operator + (const P& t){return P(this -> x+t.x,this -> y+t.y);}
P operator - (const P& t){return P(this -> x-t.x,this -> y-t.y);}
P operator * (const ld& t){return P(this -> x*t,this -> y*t);}
} p[N];
ld c[N],r[N];
int n,k;
inline bool OK(P now){
int res = 0;
for(int i = 0;i < n;++i)
if((now-p[i]).dis() < r[i]+eps) res++;
return res >= k;
}
inline bool get_P(P &p1,P &p2,int i,int j){
ld dv = (p[i]-p[j]).dis(); // L = sqrt( (x1-x2)^2+(y1-y2)^2 )
if (dv-abs(r[i]-r[j]) < eps) return 0; // 包含关系
if (r[i]+r[j]-dv < eps) return 0; // 分离关系
ld nv = r[i]*r[i]-r[j]*r[j]; // r1^2-r2^2
ld pv = nv/dv; // (r1^2 - r2^2) / L
ld av = (dv+pv)/2; // AE = (r1^2 - r2^2 + L^2) / 2*L
P dir = (p[j]-p[i]).norm(); // ( (x2-x1)/L , (y2-y1)/L )
P pg = (p[i]+dir*av); // P E = ( x1+AE/L*(x2-x1) , y1+AE/L*(y2-y1) )
ld ov = sqrt(r[i]*r[i]-av*av); // CE = sqrt( r1^2-AE^2 )
p1 = (pg+dir.rot()*ov);
p2 = (pg+dir.rot()*(-ov));
return 1;
}
inline bool check(ld T){
for(int i = 0;i < n;++i) r[i] = T/c[i];
for(int i = 0;i < n;++i)
if(OK(p[i])) return 1;
P p1,p2;
for(int i = 0;i < n;++i){
for(int j = i+1;j < n;++j){
if(get_P(p1,p2,i,j)){
if(OK(p1) || OK(p2)) return 1;
}
}
}
return 0;
}
inline ld bin_search(ld l,ld r){
if(k == 1) return 0.0;
for(int i = 0;i < 100;++i){
ld m = (l+r)/2.0;
if(check(m)) r = m;
else l = m;
}
return l;
}
int main()
{
std::ios::sync_with_stdio(0);cin.tie(0);
cin >> n >> k;
for(int i = 0;i < n;++i){
cin >> p[i].x >> p[i].y >> c[i];
}
printf("%.8Lf\n",bin_search(0,1e6));
return 0;
}
solution2:
(还没理解透,但貌似找到一个代码,先放一放)
#define _USE_MATH_DEFINES
#include
using namespace std;
//template
#define rep(i,a,b) for(int i=(a);i<(b);i++)
#define rrep(i,a,b) for(int i=(a);i>(b);i--)
#define ALL(v) (v).begin(),(v).end()
typedef long long int ll;
const int inf = 0x3fffffff; const ll INF = 0x1fffffffffffffff; const double eps=1e-12;
void tostr(ll x,string& res){while(x)res+=('0'+(x%10)),x/=10; reverse(ALL(res)); return;}
template<class T> inline bool chmax(T& a,T b){ if(a<b){a=b;return 1;}return 0; }
template<class T> inline bool chmin(T& a,T b){ if(a>b){a=b;return 1;}return 0; }
//end
using point=complex<double>;
struct circle{point p; double r;};
bool check(circle c1,circle c2){
if(c1.r<c2.r)swap(c1,c2);
double d=abs(c1.p-c2.p); if(c1.r+c2.r<d)return 0;
if(abs(c1.r+c2.r-d)<eps||c1.r-c2.r<d||abs(c1.r-c2.r-d)<eps)return 1;
return 0;
}
vector<point> intersect(circle c1,circle c2){
if(!check(c1,c2))return {};
double d=abs(c1.p-c2.p);
double a=acos((c1.r*c1.r+d*d-c2.r*c2.r)/(2*c1.r*d));
double t=atan2(c2.p.imag()-c1.p.imag(),c2.p.real()-c1.p.real());
auto r1=polar(c1.r,t+a)+c1.p,r2=polar(c1.r,t-a)+c1.p;
return {r1,r2};
}
int n,k; double x[61],y[61],c[61];
circle cs[61];
int cnt(point p){
int res=0;
rep(i,0,n){if(abs(cs[i].p-p)<=cs[i].r+eps)res++;}
return res;
}
int main(){
scanf("%d%d",&n,&k);
rep(i,0,n)scanf("%lf%lf%lf",&x[i],&y[i],&c[i]);
double lb=0,rb=1e6;
rep(_,0,100){
double mid=(lb+rb)/2.0;
rep(i,0,n)cs[i].p=point(x[i],y[i]),cs[i].r=mid/c[i];
auto f=[&](){
vector<point> cand;
rep(i,0,n){
cand.push_back(cs[i].p);
rep(j,i+1,n){
auto v=intersect(cs[i],cs[j]);
for(auto p:v)cand.push_back(p);
}
}
for(point p:cand)if(cnt(p)>=k)return 1;
return 0;
};
if(f())rb=mid; else lb=mid;
}
printf("%.12f\n",rb);
return 0;
}