比赛链接:https://atcoder.jp/contests/agc039/tasks。
A - Connection and Disconnection
找到两个不同的字符切开,每一段就不影响了。
如果没有就特判下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| #include<bits/stdc++.h> using namespace std;
void read(int &x) { x=0;int f=1;char ch=getchar(); for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f; for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f; }
void print(int x) { if(x<0) putchar('-'),x=-x; if(!x) return ;print(x/10),putchar(x%10+48); } void write(int x) {if(!x) putchar('0');else print(x);putchar('\n');}
#define lf double #define ll long long
#define pii pair<int,int > #define vec vector<int >
#define pb push_back #define mp make_pair #define fr first #define sc second
#define FOR(i,l,r) for(int i=l,i##_r=r;i<=i##_r;i++)
const int maxn = 1e6+10; const int inf = 1e9; const lf eps = 1e-8; const int mod = 1e9+7;
int n,k; char s[maxn];
int solve(int l,int r) { int t=1,res=0; for(int i=l+1;i<=r;i++) if(s[i]==s[i-1]) t++; else res+=t/2,t=1; res+=t/2;return res; }
int main() { scanf("%s",s+1);read(k);n=strlen(s+1);int p=0; for(int i=2;i<=n;i++) if(s[i]!=s[i-1]) p=i; if(!p) return printf("%lld\n",1ll*k*n/2),0; for(int i=n+1;i<=n+p-1;i++) s[i]=s[i-n]; printf("%lld\n",1ll*solve(p,p+n-1)*(k-1)+solve(1,n)); return 0; }
|
B - Graph Partition
以每个点为起点分层图染色就行了,用到颜色的数量就是答案,注意判不合法情况。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
| #include<bits/stdc++.h> using namespace std;
void read(int &x) { x=0;int f=1;char ch=getchar(); for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f; for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f; }
void print(int x) { if(x<0) putchar('-'),x=-x; if(!x) return ;print(x/10),putchar(x%10+48); } void write(int x) {if(!x) putchar('0');else print(x);putchar('\n');}
#define lf double #define ll long long
#define pii pair<int,int > #define vec vector<int >
#define pb push_back #define mp make_pair #define fr first #define sc second
#define FOR(i,l,r) for(int i=l,i##_r=r;i<=i##_r;i++)
const int maxn = 1e6+10; const int inf = 1e9; const lf eps = 1e-8; const int mod = 1e9+7;
char s[maxn]; int head[maxn],tot,n,col[maxn],ans,c; struct edge{int to,nxt;}e[maxn<<1];
void ins(int u,int v) { e[++tot]=(edge){v,head[u]},head[u]=tot; }
int bfs(int s) { queue<int > q;q.push(s); while(!q.empty()) { int x=q.front();q.pop();c=max(c,col[x]); for(int i=head[x];i;i=e[i].nxt) if(!col[e[i].to]) col[e[i].to]=col[x]+1,q.push(e[i].to); else if(abs(col[e[i].to]-col[x])!=1) return 0; }return 1; }
int main() { read(n); for(int i=1;i<=n;i++) { scanf("%s",s+1); for(int j=i+1;j<=n;j++) if(s[j]=='1') ins(i,j),ins(j,i); } int ans=-1; for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) col[j]=0; col[i]=1;c=0; if(bfs(i)) ans=max(ans,c); }write(ans); return 0; }
|
C - Division by Two with Something
手动模拟下小样例或者推一下可以发现,一个串操作$x$次之后能按位取反当且仅当对于任意$i$满足$s_i=s_{i-x}~{\rm xor}~1$。
并且显然$x$要满足$x|n$,并且$n/x$为奇数,否则操作之前的串最后一截就会等于操作之后前面一截,不符合条件。
那么就可以$O(n)$的算出有多少种小于给定串的串可以在至多$x$次操作后按位取反。
那么容斥一下就可以得到答案了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| #include<bits/stdc++.h> using namespace std;
void read(int &x) { x=0;int f=1;char ch=getchar(); for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f; for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f; }
void print(int x) { if(x<0) putchar('-'),x=-x; if(!x) return ;print(x/10),putchar(x%10+48); } void write(int x) {if(!x) putchar('0');else print(x);putchar('\n');}
#define lf double #define ll long long
#define pii pair<int,int > #define vec vector<int >
#define pb push_back #define mp make_pair #define fr first #define sc second
#define FOR(i,l,r) for(int i=l,i##_r=r;i<=i##_r;i++)
const int maxn = 1e6+10; const int inf = 1e9; const lf eps = 1e-8; const int mod = 998244353;
char s[maxn]; int vis[maxn],f[maxn],n,t[maxn],cnt[maxn];
int calc(int x) { int ans=0; for(int i=1;i<=x;i++) ans=((ans<<1)|s[i])%mod,t[i]=s[i]; for(int i=x+1;i<=n;i++) t[i]=t[i-x]^1; for(int i=1;i<=n;i++) { if(s[i]>t[i]) return ans+1; if(s[i]<t[i]) return ans; }return ans+1; }
int main() { read(n);scanf("%s",s+1); for(int i=1;i<=n;i++) s[i]-='0'; for(int i=1;i<=n;i++) if(n%i==0&&((n/i)&1)) vis[n/i]=1,f[n/i]=calc(i); int ans=0; for(int i=n;i;i--) { if(!vis[i]) continue; for(int j=i<<1;j<=n;j+=i) if(vis[j]) f[i]=(f[i]-f[j]+mod)%mod; ans=(ans+1ll*n/i*2*f[i]%mod)%mod; }write(ans); return 0; }
|
D - Incenters
我完全不会的几何题。。题解都看了好久才看懂。。
对于$\triangle ABC$,设$A^\prime$为弧$BC$不经过$A$的中点,$B’,C’$同理,那么可以发现$\triangle A’B’C’$的垂心和$\triangle ABC$的内心重合。
根据欧拉线定理(可以上百度搜,讲的还挺对的我也是第一次知道这玩意),设$\triangle ABC$的垂心,重心,外心为$H,G,O$,那么有$H,G,O$共线,并且$2|GO|=|HG|$。
注意到这题中外心就是$(0,0)$,而重心是三点坐标加起来除以二,所以垂心就是三点坐标之和。
接下来的事就很简单了,我们只需要统计每个$A’$点出现的次数,加起来就可以了,因为$A’$点有$O(n^2)$个,所以复杂度为$O(n^2)$。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| #include<bits/stdc++.h> using namespace std;
void read(int &x) { x=0;int f=1;char ch=getchar(); for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f; for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f; }
void print(int x) { if(x<0) putchar('-'),x=-x; if(!x) return ;print(x/10),putchar(x%10+48); } void write(int x) {if(!x) putchar('0');else print(x);putchar('\n');}
#define lf double #define ll long long
#define pii pair<int,int > #define vec vector<int >
#define pb push_back #define mp make_pair #define fr first #define sc second
#define FOR(i,l,r) for(int i=l,i##_r=r;i<=i##_r;i++)
const int maxn = 1e6+10; const int inf = 1e9; const lf eps = 1e-8; const int mod = 1e9+7; const lf pi = acos(-1);
int n,L,t[maxn]; lf x,y;
void get(int a,int b) { if(a>b) { lf p=(t[b]+L-t[a])/2.0; lf w=1.0*t[a]+p;if(w>L) w-=L; x+=cos(2.0*pi*w/L)*(a-b-1); y+=sin(2.0*pi*w/L)*(a-b-1); } else { lf w=(t[a]+t[b])/2.0; x+=cos(2.0*pi*w/L)*(n-(b-a+1)); y+=sin(2.0*pi*w/L)*(n-(b-a+1)); } }
int main() { read(n),read(L); for(int i=1;i<=n;i++) read(t[i]); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if(i!=j) get(i,j); lf pps=1.0*n*(n-1)*(n-2)/6.0; printf("%.10lf %.10lf\n",x/pps,y/pps); return 0; }
|
E - Pairing Points
又是我不会的神仙题。。不过比前一题小清新多了。
首先枚举$1$连的点,假设是$i$。
注意到必然有一个点对会横跨$(1,i)$这条边,否则不连通,那么我们枚举最上面那个,假设是$(j,k)$ $(j<k)$。
因为我们枚举的是最上面那条边,所以$[2,j-1]$不会和$[i,n]$连边,同理$[j+1,n-1]$不会和$[2,i]$连边。
所以$[2,j-1]$只会在自己内连边或连向$[j+1,i-1]$,并且$[j+1,i-1]$一定会有一个分界点,假设是$s$,那么$[2,j-1]$只会向$[j+1,s]$连边,而$[s+1,i-1]$也只会向另一边连边。
假设另一边的分界点为$t$,那么问题被分成了三个子问题:$[2,j-1]\sim [j+1,s]$,$[s+1,i-1]\sim [i+1,t-1]$,$[t,k-1]\sim [k+1,n]$。
所以写个记忆化搜索就行了,复杂度应该是常数非常小的$O(n^7)$,足以通过本题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| #include<bits/stdc++.h> using namespace std;
void read(int &x) { x=0;int f=1;char ch=getchar(); for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f; for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f; }
void print(int x) { if(x<0) putchar('-'),x=-x; if(!x) return ;print(x/10),putchar(x%10+48); } void write(int x) {if(!x) putchar('0');else print(x);putchar('\n');}
#define lf double #define ll long long
#define pii pair<int,int > #define vec vector<int >
#define pb push_back #define mp make_pair #define fr first #define sc second
#define FOR(i,l,r) for(int i=l,i##_r=r;i<=i##_r;i++)
const int maxn = 40+10; const int inf = 1e9; const lf eps = 1e-8; const int mod = 1e9+7;
char c[maxn][maxn]; int n; ll f[maxn][maxn][maxn];
ll dfs(int l,int r,int mid) { if(f[l][r][mid]!=-1) return f[l][r][mid]; if(l==r) return 1; if(l==mid||r==mid) return 0; ll ans=0; for(int j=l;j<mid;j++) for(int k=mid+1;k<=r;k++) { if(c[j][k]=='0') continue; for(int s=j;s<mid;s++) for(int t=mid+1;t<=k;t++) ans+=dfs(l,s,j)*dfs(t,r,k)*dfs(s+1,t-1,mid); } return f[l][r][mid]=ans; }
int main() { read(n);n<<=1;for(int i=1;i<=n;i++) scanf("%s",c[i]+1); ll ans=0;memset(f,-1,sizeof f); for(int i=2;i<=n;i++) if(c[1][i]=='1') ans+=dfs(2,n,i); printf("%lld\n",ans); return 0; }
|