AtCoder Regular Contest 099
C - Minimization
题意:给出一个n的排列。每次操作可以使一段长度为K的连续子序列变成该序列的最小数。
求最少几次使得
分析:枚举包含1的序列是哪个,然后左右O(1)求答案。
代码:
#include <cstdio> #include <cstring> #include <algorithm> #include <cmath> using namespace std; #define N 100050 int a[N],n,k,pos; int main() { scanf("%d%d",&n,&k); int i; for(i=1;i<=n;i++) { scanf("%d",&a[i]); if(a[i]==1) pos=i; } int ans=1<<30; for(i=max(1,pos-k+1);i<=pos;i++) { int j=i+k-1; ans=min(ans,(i-1+k-2)/(k-1)+(n-j+k-2)/(k-1)); } printf("%d ",ans+1); }
D - Snuke Numbers
题意:令S(n)表示n这个数各位之和。定义一个数n合法:所有m>n,都有n/S(n)<=m/S(m)。
输出前K个合法的数。
分析:显然所有99999这样的数都合法。
假设现在有一个数xyz999,这个数显然比xy9z99优,故在数字后面加的9的个数不降。
也就是是说每次加上$10^i$,其中i不降。需要判断这个数$+10^i$和$+10^{i+1}$谁更优,如果$10^{i+1}$更优就替换。
代码:
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; typedef long long ll; int s(ll x) { if(!x) return 0; return x%10+s(x/10); } int main() { int K; scanf("%d",&K); ll n=1,d=1; printf("%d ",1); K--; while(K--) { if(s(n+10*d)*(n+d)>(n+10*d)*s(n+d)) d*=10; printf("%lld ",n+=d); } }
E - Independence
题意:给出一个无向图,让你将它分成两部分使得,每部分的点互相有边相连。
问最少有几条相连的边。
分析:设分成S,T两个集合,答案等于siz(S)*(siz(S)-1)/2+siz(T)*(siz(T)-1)/2。
这个式子的意思是让我们最小化siz(S)和siz(T)的差的绝对值。
考虑对补图进行操作。补图上有边相连的两个点一定分别属于两个不同的集合。
对每个连通块进行染色,同时求出来每个连通块的两个集合分别有多少点。
然后就是个DP了,F[i][j]表示考虑前i个连通块,siz(S)-siz(T)的差为j是否合法。
代码:
#include <cstdio> #include <cstring> #include <algorithm> #include <cstdlib> using namespace std; #define GG puts("FUCK") #define maxn 750 int map[750][750]; int n,m,a[750],b[750],tot; int f[750][1550],vis[750]; void dfs(int x,int opt) { int i; vis[x]=opt; if(!opt) a[tot]++; else b[tot]++; for(i=1;i<=n;i++) { if(map[x][i]) { if(vis[i]==-1) { dfs(i,opt^1); }else if(vis[i]==opt) { puts("-1"); exit(0); } } } } int main() { scanf("%d%d",&n,&m); int i,j,x,y; memset(vis,-1,sizeof(vis)); for(i=1;i<=n;i++) for(j=1;j<=n;j++) if(i!=j) map[i][j]=1; for(i=1;i<=m;i++) { scanf("%d%d",&x,&y); map[x][y]=map[y][x]=0; } for(i=1;i<=n;i++) { if(vis[i]==-1) { tot++; dfs(i,0); } } f[0][maxn]=1; for(i=0;i<tot;i++) { int del=a[i+1]-b[i+1]; for(j=0;j<=maxn+maxn;j++) { if(f[i][j]) { if(j>=del) f[i+1][j-del]=1; if(j+del<=maxn+maxn) f[i+1][j+del]=1; } } } int mn=1<<30; for(i=maxn;i<=maxn+maxn;i++) { if(f[tot][i]) { mn=i-maxn; break; } } for(i=maxn;i>=0;i--) { if(f[tot][i]) { mn=min(mn,maxn-i); break; } } int s=(n+mn)>>1,t=n-s; printf("%d ",s*(s-1)/2+t*(t-1)/2); }
F - Eating Symbols Hard
题意:给你一个字符串表示操作。
+表示在指针对应位置上的值+1
-表示在指针对应位置上的值-1
>表示把指针右移1位。
<表示把指针左移1位。
假设按原串操作后序列为A。
求有多少个子串使得操作后的序列B等于A。
分析:
观察到操作的$S$序列可以用哈希的方法变成一个多项式。
其中设$M$为一个大质数,$X$为一个$base$。对于字符串$S$,定义$t(S)$为其哈希值。
有$t(S)=(sumlimits_{i}A_iX^{i})modM$
在S前加入一个字符后,有:
$t(0)=0$,为一个空串。
$t(+S)=t(S)+1$
$t(-S)=t(S)-1$$t(>S)=t(S)X$
$t(<S)=t(S)X^{-1}$
令$C$为整个序列的哈希值,也就是我们要统计有多少$(i,j)$使得$i$到$j$的哈希值为$C$
处理出来前缀的系数,逆元的i次方和base的i次方。
然后map一下。
代码:
#include <cstdio> #include <cstring> #include <algorithm> #include <cstdlib> #include <map> using namespace std; typedef long long ll; #define GG puts("FUCK") #define N 500050 #define mr(x,y) make_pair(x,y) inline char nc() { static char buf[100000],*p1,*p2; return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++; } int rd() { int x=0; char s=nc(); while(s<'0'||s>'9') s=nc(); while(s>='0'&&s<='9') x=(x<<3)+(x<<1)+s-'0',s=nc(); return x; } char rc() { char s=nc(); while(s!='+'&&s!='-'&&s!='>'&&s!='<') s=nc(); return s; } int mod1=998244353,base1=19260817,inv1; int mod2=353448299,base2=20000003,inv2; int h1[N],mi1[N],imi1[N],p[N],n,C; int h2[N],mi2[N],imi2[N]; map<pair<int,int>,int>mp; int qp(int x,int y,int p) { int re=1; for(;y;y>>=1,x=ll(x)*x%p) if(y&1) re=ll(re)*x%p; return re; } int main() { inv1=qp(base1,mod1-2,mod1); inv2=qp(base2,mod2-2,mod2); n=rd(); char str; int i; for(mi1[0]=imi1[0]=mi2[0]=imi2[0]=i=1;i<=(n<<1);i++) { mi1[i]=ll(mi1[i-1])*base1%mod1; imi1[i]=ll(imi1[i-1])*inv1%mod1; mi2[i]=ll(mi2[i-1])*base2%mod2; imi2[i]=ll(imi2[i-1])*inv2%mod2; } p[0]=n; for(i=1;i<=n;i++) { str=rc(); if(str=='+') { p[i]=p[i-1]; h1[i]=(h1[i-1]+mi1[p[i]])%mod1; h2[i]=(h2[i-1]+mi2[p[i]])%mod2; }else if(str=='-') { p[i]=p[i-1]; h1[i]=(h1[i-1]-mi1[p[i]]+mod1)%mod1; h2[i]=(h2[i-1]-mi2[p[i]]+mod2)%mod2; }else if(str=='>') { p[i]=p[i-1]+1; h1[i]=h1[i-1]; h2[i]=h2[i-1]; }else { p[i]=p[i-1]-1; h1[i]=h1[i-1]; h2[i]=h2[i-1]; } mp[mr(h1[i],h2[i])]++; } ll ans=0; for(i=1;i<=n;i++) { int d=p[i-1]-n,t1=h1[n],t2=h2[n]; if(d>=0) t1=ll(t1)*mi1[d]%mod1,t2=ll(t2)*mi2[d]%mod2; else t1=ll(t1)*imi1[-d]%mod1,t2=ll(t2)*imi2[-d]%mod2; t1=(t1+h1[i-1])%mod1; t2=(t2+h2[i-1])%mod2; ans+=mp[mr(t1,t2)]; mp[mr(h1[i],h2[i])]--; } printf("%lld ",ans); }