Codeforces round 1111

CF Div 2 537

感觉题目难度OK,五个题都能做,后俩题考察人的翻译水平...

另外,$Claris$太强了...

A

直接按照题意模拟,不知道为啥有人会被×

代码:

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <iostream>
#include <bitset>
using namespace std;
#define N 1005
char s[N],t[N];
int main()
{
    int n,m;scanf("%s%s",s+1,t+1);n=strlen(s+1),m=strlen(t+1);
    if(n!=m)return puts("No"),0;
    for(int i=1;i<=n;i++)
    {
        if(s[i]==t[i])continue;
        if((s[i]=='a'||s[i]=='u'||s[i]=='e'||s[i]=='i'||s[i]=='o')&&(t[i]=='a'||t[i]=='u'||t[i]=='e'||t[i]=='i'||t[i]=='o'))continue;
        if((s[i]!='a'&&s[i]!='u'&&s[i]!='e'&&s[i]!='i'&&s[i]!='o')&&(t[i]!='a'&&t[i]!='u'&&t[i]!='e'&&t[i]!='i'&&t[i]!='o'))continue;
        puts("No");return 0;
    }puts("Yes");
}

B

一开始以为是贪心,后来觉得,可以二分,最后发现,连二分都不用,直接枚举就行...

非常简单,枚举也需要用到一些贪心思想的...

显然,留下的一定是最大的几个,所以先从小到大排个序,然后剩下的随便加就行...

那么枚举删掉几个...

剩下的$O(1)$求答案,需要维护一个后缀和...

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <iostream>
#include <bitset>
using namespace std;
#define N 100005
#define ll long long
int a[N],n,m,k;ll s[N];long double ans;
int main()
{
    scanf("%d%d%d",&n,&k,&m);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);sort(a+1,a+n+1);
    for(int i=n;i;i--)s[i]=a[i]+s[i+1];
    for(int i=1;i<=n&&i<=m+1;i++)
    {
        long double tmp=min((ll)m-i+1,(ll)k*(n-i+1));
        ans=max(ans,(tmp+s[i])/(n-i+1));
    }
    printf("%.7lf\n",(double)ans);
}

最后发现,除了枚举之外的做法都被×了...

C

感觉比B要简单?

直接动态开点线段树+暴力分治即可...

因为每段区间直接是互相不影响的...

时间复杂度为线段树节点个数,也就是$O(kn)$

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <iostream>
#include <bitset>
using namespace std;
#define N 100005
#define ll long long
#define lson l,m,tr[rt].ls
#define rson m+1,r,tr[rt].rs
struct Segment{int ls,rs,siz;}tr[N*31];
int n,k,A,B,rot,cnt;
void insert(int x,int l,int r,int &rt)
{
    if(!rt)rt=++cnt;tr[rt].siz++;if(l==r)return ;int m=(l+r)>>1;
    if(x<=m)insert(x,lson);else insert(x,rson);
}
ll solve(int l,int r,int rt)
{
    if(!rt)return A;if(l==r)return (ll)B*tr[rt].siz;int m=(l+r)>>1;
    return min((ll)B*(r-l+1)*tr[rt].siz,solve(lson)+solve(rson));
}
int main()
{
    scanf("%d%d%d%d",&n,&k,&A,&B);
    for(int i=1,x;i<=k;i++)scanf("%d",&x),insert(x,1,1<<n,rot);
    printf("%lld\n",solve(1,1<<n,rot));
}

D

这个题真的是看不懂题意是啥,多亏了Claris的题意.jpg

可以发现,不钦定相同集合的话,就是裸的背包。

那么钦定相同集合的话...也是裸的背包。

差别就是,需要把背包中钦定的部分回退,然后更新答案,再加回来...

记忆化搜索真快!

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <iostream>
#include <bitset>
#include <map>
using namespace std;
#define N 100005
#define ll long long
#define mod 1000000007
map<pair<int ,int > ,int >mp;
int fac[N],inv[N],f[N],t[N],n,Q,w;char s[N];
int q_pow(int x,int n){int ret=1;for(;n;n>>=1,x=(ll)x*x%mod)if(n&1)ret=(ll)ret*x%mod;return ret;}
void ins(int x){for(int i=n;i>=x;i--)(f[i]+=f[i-x])%=mod;}
void del(int x){for(int i=x;i<=n;i++)(f[i]+=mod-f[i-x])%=mod;}
int calc(int x,int y)
{
    if(x>y)swap(x,y);if(x+y>(n>>1))return 0;if(mp.find(make_pair(x,y))!=mp.end())return mp[make_pair(x,y)];int ret=0;
    del(x),del(y);ret=f[(n>>1)-x-y];ins(x);ins(y);return mp[make_pair(x,y)]=(((ret<<1)%mod)+mod)%mod;
}
int main()
{
    scanf("%s%d",s+1,&Q);n=strlen(s+1);f[0]=fac[0]=1;
    for(int i=1;i<=n;i++)t[s[i]]++,fac[i]=(ll)i*fac[i-1]%mod;inv[n]=q_pow(fac[n],mod-2);
    for(int i=n;i;i--)inv[i-1]=(ll)i*inv[i]%mod;w=(ll)fac[n>>1]*fac[n>>1]%mod;
    for(int i=0;i<=128;i++)if(t[i])ins(t[i]),w=(ll)w*inv[t[i]]%mod;
    while(Q--)
    {
        int x,y;scanf("%d%d",&x,&y);
        if(s[x]==s[y])printf("%lld\n",(ll)w*f[n>>1]%mod);
        else printf("%lld\n",(ll)w*calc(t[s[x]],t[s[y]])%mod);
    }
}

E

这个题真的是有意思...

首先$\sum k\le 10^5$就一眼虚树...

然后钦定$r$的话就直接把$r$也建在虚树里,然后建双向边,以$r$为根遍历...

然后的话...

考试的时候,我写了一个$O(nm^2)$的树形背包,没调出来,不知道哪里GG了...

考完发现自己强行把插板问题转化为了背包做,就很绝望...

那么就直接说正解了...

我们考虑,枚举一个集合数量。

然后每次转移的时候,只需要这个节点和它的所有祖先不在同一个集合即可。

那么不论它的祖先怎么分配,它的方案数一定是$m-siz$,其中$m$是枚举的集合大小,$siz$是祖先个数...

每次转移的话,要么新建一个集合,也就是$f[i]+=f[i-1]$,要么$f[i]=f[i]\times (m-siz)$

附上代码:

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <iostream>
#include <bitset>
using namespace std;
#define N 100005
#define ll long long
#define mod 1000000007
struct node{int to,next;}e[N<<1];
int head[N],cnt,fa[N],anc[N],dep[N],siz[N],son[N],idx[N],tims,n,Q;
void add(int x,int y){e[cnt]=(node){y,head[x]};head[x]=cnt++;}
void dfs1(int x,int from)
{
    siz[x]=1,fa[x]=from,dep[x]=dep[from]+1;
    for(int i=head[x];i!=-1;i=e[i].next)
    {
        int to1=e[i].to;
        if(to1!=from)dfs1(to1,x),siz[x]+=siz[to1],siz[son[x]]<siz[to1]?son[x]=to1:0;
    }
}
void dfs2(int x,int top)
{
    anc[x]=top;idx[x]=++tims;if(son[x])dfs2(son[x],top);
    for(int i=head[x];i!=-1;i=e[i].next)if(e[i].to!=fa[x]&&e[i].to!=son[x])dfs2(e[i].to,e[i].to);
}
int get_lca(int x,int y)
{
    while(anc[x]!=anc[y])
    {
        if(dep[anc[x]]<dep[anc[y]])swap(x,y);
        x=fa[anc[x]];
    }return dep[x]<dep[y]?x:y;
}
bool cmp(const int &a,const int &b){return idx[a]<idx[b];}
int q[N],size,vis[N],sta[N],top,m,now;
int f[305];
void dfs(int x,int from)
{
    if(vis[x]){for(int i=m;i>=now;i--)(f[i+1]+=f[i])%=mod,f[i]=(ll)f[i]*(i-now)%mod;now++;}
    for(int i=head[x];i!=-1;i=e[i].next)if(e[i].to!=from)dfs(e[i].to,x);
    head[x]=-1;if(vis[x])now--;vis[x]=0;
    // printf("%d\n",x);
    // for(int i=0;i<=m;i++)printf("%d ",f[x][i]);puts("");
}
void build(int r)
{
    f[0]=1;for(int i=1;i<=m;i++)f[i]=0;
    bool used_r=0;top=0;cnt=0;
    for(int i=1;i<=size;i++)if(q[i]==r){used_r=1;break;}if(!used_r)q[++size]=r;sort(q+1,q+size+1,cmp);
    sta[++top]=1;
    for(int i=1;i<=size;i++)
    {
        int x=q[i],lca=get_lca(sta[top],x);if(!used_r&&r==q[i])vis[x]=0;else vis[x]=1;
        while(top>1&&dep[sta[top-1]]>=dep[lca])add(sta[top-1],sta[top]),add(sta[top],sta[top-1]),top--;
        if(sta[top]!=lca)add(lca,sta[top]),add(sta[top],lca),sta[top]=lca;if(x!=sta[top])sta[++top]=x;
    }while(top>1)add(sta[top-1],sta[top]),add(sta[top],sta[top-1]),top--;
    dfs(r,0);int ans=0;
    for(int i=1;i<=m;i++)ans=(ans+f[i])%mod;printf("%d\n",(ans+mod)%mod);
}
int main()
{
    scanf("%d%d",&n,&Q);memset(head,-1,sizeof(head));
    for(int i=1,x,y;i<n;i++)scanf("%d%d",&x,&y),add(x,y),add(y,x);dfs1(1,0);dfs2(1,1);memset(head,-1,sizeof(head));
    while(Q--)
    {
        int r;
        scanf("%d%d%d",&size,&m,&r);
        for(int i=1;i<=size;i++)scanf("%d",&q[i]);
        build(r);
    }
}

原文地址:https://www.cnblogs.com/Winniechen/p/10351812.html

时间: 2024-11-04 07:21:36

Codeforces round 1111的相关文章

Codeforces Round #354 (Div. 2) ABCD

Codeforces Round #354 (Div. 2) Problems # Name     A Nicholas and Permutation standard input/output 1 s, 256 MB    x3384 B Pyramid of Glasses standard input/output 1 s, 256 MB    x1462 C Vasya and String standard input/output 1 s, 256 MB    x1393 D T

CodeForces 441E(Codeforces Round #252 (Div. 2))

思路:dp[i][now][mark][len]   i 表示当前第i 次now存的是后8位,mark为第9位为0还是1 len第九位往高位还有几位和第9位相等.  只存后8位的原因:操作只有200次每次都为加法的话后8位可以表示,如果为乘法第八位已知再加上第九位 和往前的长度已知,所以可以表示所有状态. 所存在问题就是 10 1111 1111 此时加上1之后 会变成 11 0000 0000 但这样并处影响结果 如果之后操作都为加法,只有200次,他不可能影响到前面的1, 乘法相当于左移也不

Educational Codeforces Round 80 (Rated for Div. 2)

\[Educational\ Codeforces\ Round\ 80\ (Rated\ for\ Div.\ 2)\] A.Deadline 打勾函数找最小值,在\(\sqrt{d}\)邻域里找\(x\)最小化\(x+\lceil\frac{d}{x+1}\rceil\)即可 //#pragma comment(linker, "/STACK:1024000000,1024000000") #include<bits/stdc++.h> using namespace

Educational Codeforces Round 21 G. Anthem of Berland(dp+kmp)

题目链接:Educational Codeforces Round 21 G. Anthem of Berland 题意: 给你两个字符串,第一个字符串包含问号,问号可以变成任意字符串. 问你第一个字符串最多包含多少个第二个字符串. 题解: 考虑dp[i][j],表示当前考虑到第一个串的第i位,已经匹配到第二个字符串的第j位. 这样的话复杂度为26*n*m*O(fail). fail可以用kmp进行预处理,将26个字母全部处理出来,这样复杂度就变成了26*n*m. 状态转移看代码(就是一个kmp

Codeforces Round #428 (Div. 2)

Codeforces Round #428 (Div. 2) A    看懂题目意思就知道做了 #include<bits/stdc++.h> using namespace std; #pragma comment(linker, "/STACK:102400000,102400000") #define rep(i,a,b) for (int i=a; i<=b; ++i) #define per(i,b,a) for (int i=b; i>=a; --i

Codeforces Round #424 (Div. 2) D. Office Keys(dp)

题目链接:Codeforces Round #424 (Div. 2) D. Office Keys 题意: 在一条轴上有n个人,和m个钥匙,门在s位置. 现在每个人走单位距离需要单位时间. 每个钥匙只能被一个人拿. 求全部的人拿到钥匙并且走到门的最短时间. 题解: 显然没有交叉的情况,因为如果交叉的话可能不是最优解. 然后考虑dp[i][j]表示第i个人拿了第j把钥匙,然后 dp[i][j]=max(val(i,j),min(dp[i-1][i-1~j]))   val(i,j)表示第i个人拿

Codeforces Round #424 (Div. 2) C. Jury Marks(乱搞)

题目链接:Codeforces Round #424 (Div. 2) C. Jury Marks 题意: 给你一个有n个数序列,现在让你确定一个x,使得x通过挨着加这个序列的每一个数能出现所有给出的k个数. 问合法的x有多少个.题目保证这k个数完全不同. 题解: 显然,要将这n个数求一下前缀和,并且排一下序,这样,能出现的数就可以表示为x+a,x+b,x+c了. 这里 x+a,x+b,x+c是递增的.这里我把这个序列叫做A序列 然后对于给出的k个数,我们也排一下序,这里我把它叫做B序列,如果我

Codeforces Round #400 C 前缀和,思维

ICM Technex 2017 and Codeforces Round #400 (Div. 1 + Div. 2, combined) C. Molly's Chemicals 题意:n个数,问有多少个区间的和是k的次方数,即sum([l, r])=k^x, x>=0. abs(k)<=10. tags:一开始O(n^2)统计,果然炸了.. 这题要在统计到第 i 个数时,看s[i]-k^x是否在前面出现过.因为k指数增长很快,这样就是O(n). // #400 #include<b

[Codeforces] Round #352 (Div. 2)

人生不止眼前的狗血,还有远方的狗带 A题B题一如既往的丝帛题 A题题意:询问按照12345678910111213...的顺序排列下去第n(n<=10^3)个数是多少 题解:打表,输出 1 #include<bits/stdc++.h> 2 using namespace std; 3 int dig[10],A[1005]; 4 int main(){ 5 int aa=0; 6 for(int i=1;;i++){ 7 int x=i,dd=0; 8 while(x)dig[++dd