Atcoder补题记录

Atcoder补题记录

  • Panasonic Programming Contest 2020
  • AtCoder Beginner Contest 158
  • AtCoder Beginner Contest 157

Panasonic Programming Contest 2020

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;
}

AtCoder Beginner Contest 158

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;
}

AtCoder Beginner Contest 157

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;
}

你可能感兴趣的:(萌新级)