A Villages: Landlines
可以转化为最少点覆盖线段问题,签到题。
#include <bits/stdc++.h>
#define ls rt<<1
#define rs rt<<1|1
#define int long long
#define ll long long
#define FOR(i,a,b) for(int i=a;i<=b;++i)
using namespace std;
const int _=3e5+7;
const int mod=1e9+7;
int read() {
int x=0,f=1;char s=getchar();
for(;s>'9'||s<'0';s=getchar()) if(s=='-') f=-1;
for(;s>='0'&&s<='9';s=getchar()) x=x*10+s-'0';
return x*f;
}
int n;
pair<int,int> b[_],a[_];
signed main() {
n=read();
int s=read(),r=read();
for(int i=1;i<n;++i) {
int x=read(),y=read();
a[i].first=x-y;
a[i].second=x+y;
}
a[n]=make_pair(s-r,s+r);
sort(a+1,a+n+1);
int js=0;
for(int i=1;i<=n;) {
int l=a[i].first,r=a[i].second;
int j=i+1;
while(j<=n&&a[j].first<=r) {
r=max(r,a[j].second);
j++;
}
b[++js]=make_pair(l,r);
i=j;
}
// int L=s-r,R=s+r;
int ans=0;
for(int i=2;i<=js;++i)
ans+=b[i].first-b[i-1].second;
cout<<ans;
return 0;
}
B Spirit Circle Observation
这个题就比较有意思,如果两个字符串a,b相差1并且长度相同,假设a=b+1,那么一定满足这样的性质: a = p x 00 … b = p ( x + 1 ) 99... ( 其中 p 表示 a , b 的公共前缀且 p 可以是空串 ) a=px00… ~~~~b=p(x+1)99...(其中p表示a,b的公共前缀且p可以是空串) a=px00… b=p(x+1)99...(其中p表示a,b的公共前缀且p可以是空串),那么只需要在 S A M SAM SAM上枚举 p p p的位置,同时延伸 a , b a,b a,b串即可,对于每个点的 p p p串可以用等价类的长度作差计算,对于 99.. 和 00.. 99..和00.. 99..和00..串可以用子树叶结点的即 p a r e n t parent parent树上的 s i z e size size大小维护,注意根节点也就是 p p p为空串的特判。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<algorithm>
#include<map>
#include<cmath>
#include<queue>
#define clean(x) memset(x,0,sizeof(x))
#define maxn 1000005
#define int long long
using namespace std;
int read()
{
int x=1,res=0;
char c=getchar();
while(c<'0'||c>'9')
{
if(c=='-')
x=-1;
c=getchar();
}
while(c>='0'&&c<='9')
{
res=res*10+(c-'0');
c=getchar();
}
return res*x;
}
struct edge{
int next,to;
}g[maxn];
int tot=1,last[maxn],ch[maxn][10],sz[maxn],l[maxn],lt=1,f[maxn],num,ans;
char a[maxn];
void add(int from,int to)
{
g[++num].next=last[from];
g[num].to=to;
last[from]=num;
}
void insert(int c)
{
int v=++tot,u=lt;lt=tot;
sz[v]=1;l[v]=l[u]+1;
while(u&&!ch[u][c]) {ch[u][c]=v;u=f[u];}
if(!u) {f[v]=1;return;}
int x=ch[u][c];
if(l[x]==l[u]+1) {f[v]=x;return;}
int y=++tot;
l[y]=l[u]+1;f[y]=f[x];f[x]=f[v]=y;
memcpy(ch[y],ch[x],sizeof(ch[x]));
while(u&&ch[u][c]==x) {ch[u][c]=y;u=f[u];}
}
void dfs(int x)
{
for(int i=last[x];i;i=g[i].next)
{
int v=g[i].to;
dfs(v);
sz[x]+=sz[v];
}
}
signed main()
{
int len=read();
cin>>a+1;
for(int i=1;i<=len;i++)
insert(a[i]-'0');
for(int i=2;i<=tot;i++)
add(f[i],i);
dfs(1);
// cout<<sz[1];
for(int i=1;i<=tot;i++){
for(int j=0;j<=8;j++){
int x=ch[i][j],y=ch[i][j+1];
while(x&&y){
if(i==1)ans+=sz[x]*sz[y];
else ans+=(l[i]-l[f[i]])*sz[x]*sz[y];
y=ch[y][0];
x=ch[x][9];
}
}
}
cout<<ans<<endl;
return 0;
}
C Grab the Seat!
这个题也很有意思,显而易见,对于每一行,我们只需要考虑左边的第一个有人的位置,因为这个位置一定覆盖的范围大于靠右的位置,如果从下往上看的话,那么最左端的能够额外覆盖位置人与屏幕下端点的连线斜率一定是递增的,不然就会被其他人所覆盖,同理,从上往下也是如此,这样对于每一行,取两次扫描的最小值就是这一行的答案。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<algorithm>
#include<map>
#include<cmath>
#include<queue>
#define clean(x) memset(x,0,sizeof(x))
#define maxn 200005
#define int long long
#define eps 1e-9
using namespace std;
int read()
{
int x=1,res=0;
char c=getchar();
while(c<'0'||c>'9')
{
if(c=='-')
x=-1;
c=getchar();
}
while(c>='0'&&c<='9')
{
res=res*10+(c-'0');
c=getchar();
}
return res*x;
}
struct point{
int x,y;
}g[maxn];
int n,m,k,q,aa,bb,cc,x[maxn],ans[maxn];
signed main()
{
n=read();m=read();k=read();q=read();
for(int i=1;i<=k;i++){
g[i].x=read();g[i].y=read();
}
while(q--){
aa=read();bb=read();cc=read();
g[aa].x=bb;g[aa].y=cc;
for(int i=1;i<=m;i++){
ans[i]=n;
}
memset(x,0x3f,sizeof(x));
for(int i=1;i<=k;i++){
int xx=g[i].x,yy=g[i].y;
x[yy]=min(x[yy],xx);
}
double kk=0;
for(int i=1;i<=m;i++){
if(i==1||i==m){
ans[i]=min(ans[i],x[i]-1);
}else {
kk=max(kk,1.0*(i-1)/x[i]);
ans[i]=min(ans[i],(int)floor((i-1)*1.0/kk-eps));
}
if(i==m){
kk=max(kk,1.0*(i-1)/x[i]);
ans[i]=min(ans[i],(int)floor((i-1)*1.0/kk-eps));
}
}
kk=0;
for(int i=m;i>=1;i--){
if(i==1||i==m){
ans[i]=min(ans[i],x[i]-1);
}else {
kk=min(kk,1.0*(i-m)/x[i]);
ans[i]=min(ans[i],(int)floor((i-m)*1.0/kk-eps));
}
if(i==1){
kk=min(kk,1.0*(i-m)/x[i]);
ans[i]=min(ans[i],(int)floor((i-m)*1.0/kk-eps));
}
}
int tot=0;
for(int i=1;i<=m;i++){
tot+=ans[i];
}
printf("%lld\n",tot);
}
}
D Mocha and Railgun
初中几何数学题,比较签。
#include <bits/stdc++.h>
#define ls rt<<1
#define rs rt<<1|1
// #define int long long
#define ll long long
#define FOR(i,a,b) for(int i=a;i<=b;++i)
using namespace std;
const int _=3e5+7;
const int mod=1e9+7;
const double eps=1e-10;
int read() {
int x=0,f=1;char s=getchar();
for(;s>'9'||s<'0';s=getchar()) if(s=='-') f=-1;
for(;s>='0'&&s<='9';s=getchar()) x=x*10+s-'0';
return x*f;
}
void solve() {
double R,x,y,r;
cin>>R>>x>>y>>r;
double d=sqrt(x*x+y*y);
double dsr=acos((d-r)/R)-acos((d+r)/R);
// cout<<dsr*R;
printf("%.12f\n",dsr*R);
}
int main() {
int T=read();
while(T-->0) {
solve();
}
return 0;
}
G Lexicographical Maximum
签到。
#include <bits/stdc++.h>
#define ls rt<<1
#define rs rt<<1|1
#define ll long long
#define FOR(i,a,b) for(int i=a;i<=b;++i)
using namespace std;
const int _=1e6+7;
const int mod=1e9+7;
int read() {
int x=0,f=1;char s=getchar();
for(;s>'9'||s<'0';s=getchar()) if(s=='-') f=-1;
for(;s>='0'&&s<='9';s=getchar()) x=x*10+s-'0';
return x*f;
}
int n;
char s[_];
int main() {
// freopen("a.in","r",stdin);
cin>>(s+1);
n=strlen(s+1);
if(n==1) {
cout<<s+1;
return 0;
}
string a,b,c;
for(int i=1;i<n;++i) a+='9';
for(int i=1;i<=n;++i) b+=s[i];
cout<<max(a,b);
return 0;
}
H Fly
队友写的 NTT优化背包,看不懂。
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <vector>
#define int long long
using namespace std ;
const int N = 1e6 + 5 , mod = 998244353 , G = 3 , B = 61 , M = 20 ;
int inv[N] , fac[N] , ifac[N] ;
void init(int N)
{
fac[0] = ifac[0] = inv[1] = 1 ;
for (int i = 2 ; i < N ; ++ i) inv[i] = (mod - mod / i) * inv[mod % i] % mod ;
for (int i = 1 ; i < N ; ++ i)
{
fac[i] = fac[i - 1] * i % mod ;
ifac[i] = ifac[i - 1] * inv[i] % mod ;
}
}
long long qmi(long long a, long long b = mod - 2)
{
long long res = 1;
while (b)
{
if (b & 1) res = (__int128)res * a % mod;
a = (__int128)a * a % mod, b >>= 1;
}
return res;
}
namespace POLY {
#define SZ(a) (a.begin() , a.end())
#define L(i, j, k) for(int i = (j); i <= (k); ++i)
#define R(i, j, k) for(int i = (j); i >= (k); --i)
#define ll long long
#define sz(a) ((int) (a).size())
#define vi vector < int >
#define me(a, x) memset(a, x, sizeof(a))
#define ull unsigned long long
#define add(a, b) (a + b >= mod ? a + b - mod : a + b)
#define dec(a, b) (a < b ? a - b + mod : a - b)
const int _G = G , MOD = mod , inv2 = (MOD + 1) / 2 ;
int rt[N], Lim;
void Pinit(int x) {
for(Lim = 1; Lim <= x; Lim <<= 1) ;
for(int i = 1; i < Lim; i <<= 1) {
int sG = qmi (_G, (MOD - 1) / (i << 1));
rt[i] = 1;
L(j, i + 1, i * 2 - 1) rt[j] = (ll) rt[j - 1] * sG % MOD;
}
}
struct poly {
vector<int> a;
int size() { return sz(a); }
int & operator [] (int x) { return a[x]; }
int v(int x) { return x < 0 || x >= sz(a) ? 0 : a[x]; }
void clear() { vector<int> ().swap(a); }
void rs(int x = 0) { a.resize(x); }
poly (int n = 0) { rs(n); }
poly (vector<int> o) { a = o; }
poly (const poly &o) { a = o.a; }
poly Rs(int x = 0) { vi res = a; res.resize(x); return res; }
inline void dif() {
int n = sz(a);
for (int l = n >> 1; l >= 1; l >>= 1)
for(int j = 0; j < n; j += l << 1)
for(int k = 0, *w = rt + l; k < l; k++, w++) {
int x = a[j + k], y = a[j + k + l];
a[j + k] = add(x, y);
a[j + k + l] = (ll) * w * dec(x, y) % MOD;
}
}
void dit () {
int n = sz(a);
for(int i = 2; i <= n; i <<= 1)
for(int j = 0, l = (i >> 1); j < n; j += i)
for(int k = 0, *w = rt + l; k < l; k++, w++) {
int pa = a[j + k], pb = (ll) a[j + k + l] * *w % MOD;
a[j + k] = add(pa, pb), a[j + k + l] = dec(pa, pb);
}
reverse(a.begin() + 1, a.end());
for(int i = 0, iv = qmi(n); i < n; i++) a[i] = (ll) a[i] * iv % MOD;
}
friend poly operator * (poly aa, poly bb) {
if(!sz(aa) || !sz(bb)) return {};
int lim, all = sz(aa) + sz(bb) - 1;
for(lim = 1; lim < all; lim <<= 1);
aa.rs(lim), bb.rs(lim), aa.dif(), bb.dif();
L(i, 0, lim - 1) aa[i] = (ll) aa[i] * bb[i] % MOD;
aa.dit(), aa.a.resize(all);
return aa;
}
poly Inv() {
poly res, f, g;
res.rs(1), res[0] = qmi(a[0]);
for(int m = 1, pn; m < sz(a); m <<= 1) {
pn = m << 1, f = res, g.rs(pn), f.rs(pn);
for(int i = 0; i < pn; i++) g[i] = (*this).v(i);
f.dif(), g.dif();
for(int i = 0; i < pn; i++) g[i] = (ll) f[i] * g[i] % MOD;
g.dit();
for(int i = 0; i < m; i++) g[i] = 0;
g.dif();
for(int i = 0; i < pn; i++) g[i] = (ll) f[i] * g[i] % MOD;
g.dit(), res.rs(pn);
for(int i = m; i < min(pn, sz(a)); i++) res[i] = (MOD - g[i]) % MOD;
}
return res.rs(sz(a)), res;
}
poly Shift (int x) {
poly zm (sz(a) + x);
L(i, 0, sz(a) - 1) zm[i + x] = a[i];
return zm;
}
friend poly operator * (poly aa, int bb) {
poly res(sz(aa));
L(i, 0, sz(aa) - 1) res[i] = (ll) aa[i] * bb % MOD;
return res;
}
friend poly operator + (poly aa, poly bb) {
vector<int> res(max(sz(aa), sz(bb)));
L(i, 0, sz(res) - 1) res[i] = add(aa.v(i), bb.v(i));
return poly(res);
}
friend poly operator - (poly aa, poly bb) {
vector<int> res(max(sz(aa), sz(bb)));
L(i, 0, sz(res) - 1) res[i] = dec(aa.v(i), bb.v(i));
return poly(res);
}
poly & operator += (poly o) {
rs(max(sz(a), sz(o)));
L(i, 0, sz(a) - 1) (a[i] += o.v(i)) %= MOD;
return (*this);
}
poly & operator -= (poly o) {
rs(max(sz(a), sz(o)));
L(i, 0, sz(a) - 1) (a[i] += MOD - o.v(i)) %= MOD;
return (*this);
}
poly & operator *= (poly o) {
return (*this) = (*this) * o;
}
poly Integ() {
if(!sz(a)) return poly();
poly res(sz(a) + 1);
L(i, 1, sz(a)) res[i] = (ll) a[i - 1] * inv[i] % MOD;
return res;
}
poly Deriv() {
if(!sz(a)) return poly();
poly res(sz(a) - 1);
L(i, 1, sz(a) - 1) res[i - 1] = (ll) a[i] * i % MOD;
return res;
}
poly Ln() {
poly g = ((*this).Inv() * (*this).Deriv()).Integ();
return g.rs(sz(a)), g;
}
poly Exp() {
poly res(1), f;
res[0] = 1;
for(int m = 1, pn; m < sz(a); m <<= 1) {
pn = min(m << 1, sz(a)), f.rs(pn), res.rs(pn);
for(int i = 0; i < pn; i++) f[i] = (*this).v(i);
f -= res.Ln(), (f[0] += 1) %= MOD, res *= f, res.rs(pn);
}
return res.rs(sz(a)), res;
}
poly pow(int x, int rx = -1) { // x : the power % MOD; rx : the power % (MOD - 1)
if(rx == -1) rx = x;
int cnt = 0;
while (a[cnt] == 0 && cnt < sz(a)) cnt += 1;
poly res = (*this);
L(i, cnt, sz(a) - 1) res[i - cnt] = res[i];
L(i, sz(a) - cnt, sz(a) - 1) res[i] = 0;
int c = res[0], w = qmi (res[0]);
L(i, 0, sz(res) - 1) res[i] = (ll) res[i] * w % MOD;
res = res.Ln();
L(i, 0, sz(res) - 1) res[i] = (ll) res[i] * x % MOD;
res = res.Exp();
c = qmi (c, rx);
L(i, 0, sz(res) - 1) res[i] = (ll) res[i] * c % MOD;
if((ll) cnt * x > sz(a)) L(i, 0, sz(a) - 1) res[i] = 0;
else if(cnt) {
R(i, sz(a) - cnt * x - 1, 0) res[i + cnt * x] = res[i];
L(i, 0, cnt * x - 1) res[i] = 0;
}
return res;
}
poly sqrt(int rt = 1) {
poly res(1), f;
res[0] = rt;
for(int m = 1, pn; m < sz(a); m <<= 1) {
pn = min(m << 1, sz(a)), f.rs(pn);
for(int i = 0; i < pn; i++) f[i] = (*this).v(i);
f += res * res, f.rs(pn), res.rs(pn), res = f * res.Inv(), res.rs(pn);
for(int i = 0; i < pn; i++) res[i] = (ll) res[i] * inv2 % MOD;
}
return res;
}
void Rev() {
reverse(a.begin(), a.end());
}
} ;
#undef SZ
#undef L
#undef R
#undef ll
#undef sz
#undef vi
#undef me
#undef ull
#undef add
#undef dec
} using namespace POLY ;
void read(int & x) { scanf("%lld" , &x) ; }
void print(int x , char c = '\n') { printf("%lld%c" , x , c) ; }
int n , m , a[N] , k ;
bool st[N][B] ;
int q[N] , top ;
poly divide(int l , int r) {
if (l == r)
{
poly g(q[l] + 1) ;
g[0] = g[q[l]] = 1 ;
return g ;
}
int mid = l + r >> 1 ;
return divide(l , mid) * divide(mid + 1 , r) ;
}
signed main()
{
read(n) , read(m) , read(k) ;
for (int i = 1 ; i <= n ; ++ i) read(a[i]) ;
init(N / 2) , Pinit(N / 2) ;
while (k--)
{
int x , y ;
read(x) , read(y) ;
st[x][y] = 1 ;
}
poly f(1) ; f[0] = 1 ;
for (int k = 0 ; k < B ; ++ k)
{
q[top = 1] = 1 ;
for (int i = 1 ; i <= n ; ++ i)
{
if (!st[i][k])
q[++top] = a[i] ;
}
f *= divide(1 , top) ;
int sz = f.size() ;
for (int i = 0 ; i * 2 <= sz; ++ i)
f[i] = (i * 2 + (m >> k & 1) < sz ? f[i * 2 + (m >> k & 1)] : 0) ;
f.rs(sz / 2 + 1) ;
}
print(f[0]) ;
return 0 ;
}
I Chiitoitsu
不难看出每次如果摸到的牌能配对,就打出一张不能配对的牌,否则就打出刚刚摸到的牌,考虑概率dp,设
f
i
j
f_{ij}
fij表示摸
i
i
i张牌凑出
j
j
j个对子的概率,那么:
f
i
j
=
f
i
j
+
f
i
−
1
,
j
−
1
∗
n
n
∗
3
/
n
u
m
f_{ij}=f_{ij}+f_{i-1,j-1}*nn*3/num
fij=fij+fi−1,j−1∗nn∗3/num
f
i
j
=
f
i
j
+
f
i
−
1
,
j
−
1
∗
(
n
u
m
−
(
n
n
−
2
)
∗
3
)
/
n
u
m
f_{ij}=f_{ij}+f_{i-1,j-1}*(num-(nn-2)*3)/num
fij=fij+fi−1,j−1∗(num−(nn−2)∗3)/num
其中,
n
u
m
num
num表示牌堆中剩余的牌数,
n
n
nn
nn表示手牌中剩余的未配对的张数。
注意到答案只有七种,预处理一下
O
(
1
)
O(1)
O(1)查询即可。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<string>
#include<algorithm>
#include<map>
#include<cmath>
#include<queue>
#define clean(x) memset(x,0,sizeof(x))
#define maxn 130
#define int long long
#define mod 1000000007
#include<cstdlib>
#include<ctime>
using namespace std;
int read()
{
int x=1,res=0;
char c=getchar();
while(c<'0'||c>'9')
{
if(c=='-')
x=-1;
c=getchar();
}
while(c>='0'&&c<='9')
{
res=res*10+(c-'0');
c=getchar();
}
return res*x;
}
char a[maxn];
int f[maxn][8];
int qpow(int a,int b){
int ans=1;
while(b){
if(b&1) ans=ans*a%mod;
a=a*a%mod;
b>>=1;
}
return ans;
}
int solve1(int tt){
memset(f,0,sizeof(f));
f[0][tt]=1;
for(int i=1;i<=123;i++){
for(int j=tt;j<=7;j++){
// if(i>(34-(13-2*j))*4) continue;
int num=123-i+1,nn=13-2*(j-1);
if(j>=1) f[i][j]=(f[i][j]+f[i-1][j-1]*(3*nn)%mod*qpow(num,mod-2)%mod)%mod;
if(j<=6) f[i][j]=(f[i][j]+f[i-1][j]*(num-(nn-2)*3)%mod*qpow(num,mod-2)%mod)%mod;
}
}
int ans=0;
for(int i=1;i<=123;i++)
ans=(ans+f[i][7]*i)%mod;
return ans;
}
void solve(int tt,int ans[])
{
scanf("%s",a+1);
int len=strlen(a+1);
map<int,int>s;int cnt=0;
for(int i=1;i<=len;i+=2){
s[a[i]*100+a[i+1]]++;
if(s[a[i]*100+a[i+1]]==2) cnt++;
}
printf("Case #%d: %d\n",tt,ans[cnt]);
}
signed main()
{
int ans[10];
for(int i=0;i<=6;i++){
ans[i]=solve1(i);
}
int t=read();
for(int i=1;i<=t;i++)
solve(i,ans);
return 0;
}
J Serval and Essay
考虑通过“x确定y”这种关系将x和y进行合并,因为如果确定了x就能确定y,那么贪心地想肯定是确定x能得到更大的答案,那么就没有必要再去考虑从y出发了,所有从y连出去的边改成从x连出去。
当“x to y”这条边被合并之后,原本由y指向的点就变成了由x指向,在不断合并的过程中,如果从x出发最终能在t汇聚,那么最后一定会使得t仅由x指向(因为会合并出很多个“x to t”的边,如果维护集合的话就可以自动去重),即出现了“x决定t”,这个时候就需要继续合并。
合并的总次数显然是
O
(
n
)
O(n)
O(n)的,由于合并的是两个点连出去的点的集合,所以考虑启发式合并,复杂度约为
O
(
n
l
o
g
2
n
+
k
l
o
g
n
)
O(nlog^2n+klogn)
O(nlog2n+klogn)
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <set>
#include <vector>
using namespace std ;
using pii = pair<int,int> ;
const int N = 2e5 + 5 ;
void read(int & x) { scanf("%d" , &x) ; }
void print(int x , char c = '\n') { printf("%d%c" , x , c) ; }
int n , m ;
int p[N] , cnt[N] ;
int find(int x) { return x == p[x] ? x : p[x] = find(p[x]) ; }
set<int> e[N] , pre[N] ;
void merge(int x , int y)
{
x = find(x) , y = find(y) ;
if (x == y) return ;
if (e[x].size() > e[y].size()) swap(x , y) ;
vector<pii> q ;
for (int son : e[x])
{
e[y].insert(son) , pre[son].insert(y) ;
if (pre[son].find(x) != pre[son].end())
{
pre[son].erase(x) ;
if (pre[son].size() == 1)
q.push_back({*(pre[son].begin()) , son}) ;
}
}
p[x] = y ;
for (auto [u , v] : q) merge(u , v) ;
}
void solve()
{
read(n) ;
for (int i = 1 ; i <= n ; ++ i)
{
e[i].clear() , pre[i].clear() ;
p[i] = i , cnt[i] = 0 ;
}
for (int i = 1 ; i <= n ; ++ i)
{
int k ; read(k) ;
while (k--)
{
int fa ; read(fa) ;
e[fa].insert(i) ;
pre[i].insert(fa) ;
}
}
for (int i = 1 ; i <= n ; ++ i)
if (pre[i].size() == 1)
merge(*(pre[i].begin()) , i) ;
int res = 0 ;
for (int i = 1 ; i <= n ; ++ i)
res = max(res , ++ cnt[find(i)]) ;
print(res) ;
}
int main()
{
int t ; read(t) ;
for (int i = 1 ; i <= t ; ++ i)
{
printf("Case #%d: " , i) ;
solve() ;
}
return 0 ;
}