题目链接http://codeforces.com/contest/1139
A. Even Substrings
判断个位是否是奇数即可。
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int mx = 1e5 + 10;
ll dp[mx][2];
char str[mx];
int main()
{
int n;
scanf("%d%s",&n,str+1);
ll ans = 0;
for(int i=1;i<=n;i++){
int v = str[i] - '0';
if(v%2==0) ans += i;
}
printf("%I64d\n",ans);
return 0;
}
B. Chocolates
倒过来看的话,直接贪心就OK了。
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int mx = 2e5 + 10;
int a[mx];
int main()
{
int n;
ll ans = 0;
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",a+i);
ans += a[n];
for(int i=n-1,v=a[n];i;i--)
{
v = max(0,min(v-1,a[i]));
ans += v;
}
printf("%I64d\n",ans);
return 0;
}
C. Edgy Trees
总共有n^k种选法,那么如果我们能找出不合法的方案数ret,那么最终的答案就是n^k-ret。
可以用并查集或者dfs将黑色边把图分成若干的块,那么肯定如果k个点都在某个块中,那么所有路径肯定都不会经过黑色边了。
所以无效的方案数就是枚举所有块,然后所有块的个数的k次方累加就是无效方案数了。
#include
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef pair pa;
const int mx = 1e5 + 10;
const int mod = 1e9 + 7;
int n,m,fa[mx],cnt[mx];
bool vis[mx];
int find(int x){
return x==fa[x]? x:fa[x]=find(fa[x]);
}
ll qpow(ll x,ll y)
{
ll ans = 1;
while(y){
if(y&1) ans = ans*x%mod;
x = x*x%mod;
y >>= 1;
}
return ans;
}
int main()
{
scanf("%d%d",&n,&m);
int u,v,w;
for(int i=1;i<=n;i++) cnt[i] = 1,fa[i] = i;
for(int i=1;i
D. Steps to One
解法一:期望DP
设dp[x]表示当前序列的gcd是x的情况下,还有可以写多长到达1的期望。所以很明显式子就是:
如果当当这样的话是O(n*2)会超时,但是我们从式子中可以看出dp[x]只会从它的因子转移,所以我们就直接去找x的因子就好了,那么就有:
其中d是不等x的x的因子,c[d]表示[1,m]与x的gcd是d的个数。而因子我们可以预处理,然后可以用容斥可以求出c[d]。
因为x的因子不会超过2^6个,所以我们可以从大到小枚举因子,然后容斥,时间复杂度O(2^6*2^6)。
还有一个问题,上面说的dp[x]表示之后的长度期望,那么之前的就不用算了嘛?事实上我们只要确定序列的第一个数不就好了吗?所以最后会是1/m(1+dp[1])+1/m(1+dp[2])+1/m(1+dp[3])+...+1/m(1+dp[m])。
#include
using namespace std;
typedef long long int ll;
const int mod = 1e9 + 7;
const int mx = 1e5 + 10;
int m,cnt[mx];
vector g[mx];
ll dp[mx];
bool vis[mx];
void init()
{
for(int i=mx/2;i>1;i--){
for(int j=i*2;j>= 1;
x = x*x%mod;
}
return ans;
}
void cut(int x){
for(int i:g[x])
cnt[i] -= cnt[x];
}
int main(){
scanf("%d",&m);
init();
ll invm = qpow(m,mod-2);
for(int i=2;i<=m;i++){
ll ret = 1;
cnt[i] = m / i;
for(int j:g[i]) cnt[j] = m / j;
cut(i);
for(int j:g[i]){
cut(j);
ret = (ret + cnt[j]*invm%mod*dp[j]%mod)%mod;
}
dp[i] = ret * m % mod * qpow(m-m/i,mod-2)%mod;
}
ll ans = 1;
for(int i=2;i<=m;i++){
ans = (ans + dp[i]*invm%mod)%mod;
}
printf("%lld\n",ans);
return 0;
}
解法二:负二项分布期望
现在我们换一种思路,假设现在序列的gcd是x,那么我接下来选一个数,使得该序列的gcd还是x的选择有n / x,反之不是的选择有n - n / x,因此我们可以设一个E(x}表示为现在gcd是x直到gcd小于x的期望,那么这个期望也就符合负二项分布的概率期望。一个·事件有概率p成功,实验进行失败r次后就结束得期望次数,这里r = 1,p = n / x / n。接下来是我的个人理解:
对于原问题来说,肯定至少长度为1.所以我们假设第一个已经取完了,这里也不用考虑它取的是什么值。接下来考虑一个序列的gcd等于1,那么它的gcd肯定是越变越小小到了1.所以也就是,但是对于上面的求负二项的概率期望的公式来说,当求x = 2时,实际求得是概率期望是2的倍数的不是2的,也就是包含了4,6,8...等的,所以我们这要算质数就可以了,最后还要加个容斥,因为6即被2包括了也被3包括了。
#include
using namespace std;
typedef long long int ll;
const int mod = 1e9 + 7;
const int mx = 1e5 + 10;
int m,pri[mx],top,mu[mx];
vector g[mx];
bool vis[mx];
ll qpow(ll x,ll y){
ll ans = 1;
while(y){
if(y&1) ans = ans*x%mod;
y >>= 1;
x = x*x%mod;
}
return ans;
}
void init(){
for(int i=2;i
E. Maximize Mex
根据题目意思可以想到二分匹配来做,但是有d,n^3肯定是不行的,所以离线一下不就是O(n^2)的吗。
#include
#define fi first
#define se second
using namespace std;
typedef long long int ll;
typedef pair pa;
const int mod = 1e9 + 7;
const int mx = 5e3 + 10;
const int N = 5000;
int n,m,a[mx],cp[mx];
int b[mx],d[mx],ans[mx];
bool vis[mx];
vector g[mx];
bool pipei(int u){
for(int v:g[u]){
if(!vis[v]){
vis[v] = 1;
if(cp[v]==-1||pipei(cp[v]))
{
cp[v] = u;
return 1;
}
}
}
return 0;
}
int main(){
scanf("%d%d",&n,&m);
int v,q;
memset(cp,-1,sizeof(cp));
for(int i=1;i<=n;i++) scanf("%d",a+i);
for(int i=1;i<=n;i++){
scanf("%d",b+i);
}
scanf("%d",&q);
for(int i=1;i<=q;i++) scanf("%d",d+i),vis[d[i]] = 1;
for(int i=1;i<=n;i++)
if(!vis[i]) g[a[i]].push_back(b[i]);
int j = 0;
for(int i=q;i>=1;i--){
for(;j<5000;j++){
memset(vis,0,sizeof(vis));
if(!pipei(j)) break;
}
g[a[d[i]]].push_back(b[d[i]]);
ans[i] = j;
}
for(int i=1;i<=q;i++) printf("%d\n",ans[i]);
return 0;
}
F. Dish Shopping
如果把人的price和beauty放在二维直角坐标系上,那么此时加入一个dish(p,s,b).坐标系上被更新的点就是(p,b),(s,b-s+p),(s,b+s-p)这三个点形成的三角范围内的所有点,而且这个三角形是关于y = b对称。
对称性的特点就是三角形内点的分布(p,b),(p+1,b-1),(p+1,b),(p+1,b+1)...就是每一列都比上一列左右+1。O(n)更新时不可能的,一定会超时。所以我们想怎么在log的复杂度内更新一个dish。那么就考虑这个关于y = b对称的三角区域的特性。
用两个数组la,ra来解决这个问题。当我们更新(p,b)这个点时,两个数组操作为la[p+b]++,ra[b+1-p]--。有点像数组的差分。
那么更新区域等于到二维直角坐标系上就是这样的:
不就是关于y = b对称的这个三角形区域吗?只不过他是无穷大的而已,那么我们就可以按x轴排序,对于一个dish(p,s,b)到达p时更新,到达s时消除更新就可以了,这个可以用树状数组来做,至于坐标系上的点,离散化一下就可以了。
#include
using namespace std;
typedef pair pa;
typedef long long ll;
const int mx = 1e5 + 10;
int n,m,p[mx],s[mx],b[mx];
int c[mx],f[mx],is,tot,ans[mx];
int a[mx<<2],sum[mx<<2][2];
struct node{
int v,id;
int u;
bool operator < (node A)const{
if(v==A.v) return u < A.u;
return v < A.v;
}
}mp[mx<<2];
int getP(int x){
return lower_bound(a+1,a+is,x) - a;
}
void add(int x,int id,int v){
x = getP(x);
while(x