Tinkoff Internship Warmup Round 2018 and Codeforces Round #475 (Div. 2)

C. Alternating Sum:

题意:

求这个和式n:1e9 a:1e9 b:1e9然后有条件是a[i]=a[i-k](i>k)并且k整除n;

所以明显就是一个等比数列求和了,把前k个的和求出来,然后接着k个的和就是他的(b/a)^k。

然后用公式求和就好了。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int P=1e9+9;
char s[100005];
int n,a,b,k;
int ksm(int x,int k){
    int ans=1;
    for(;k;k>>=1,x=1LL*x*x%P) if(k&1) ans=1LL*ans*x%P;
    return ans;
}
int main(){
    scanf("%d%d%d%d",&n,&a,&b,&k);
    scanf("%s",s);
    int A=ksm(a,n),B=1,C=1LL*b*ksm(a,P-2)%P,len,ans=0;
    for(len=0;s[len];++len)  {
        if(s[len]=='-') {
            ans=(ans-1LL*A*B%P+P)%P;
        }
        else {
            ans=(ans+1LL*A*B%P+P)%P;
        }
        A=1LL*A*C%P;
    }
    int xn=(n+1)/k;
    if(a==b) {
        ans=((1LL*ans*xn%P)+P)%P;
        printf("%d\n",ans);
    }
    else {
        C=1LL*ksm(b,k)*(ksm(ksm(a,k),P-2))%P;
        if(C==1){
            ans=((1LL*ans*xn%P)+P)%P;
            printf("%d\n",ans);
            return 0;
        }
         long long t=1LL*C*(1-ksm(C,xn-1))%P;
        t=1LL*t*ksm(1-C+P,P-2)%P;
        t=1LL*t*ans%P;
        ans=((ans+t)%P+P)%P;
        printf("%d\n",ans);
    }
} 

D. Destruction of a Tree

题意:给你一个数,然后你可以不断删除度为偶数的点,直至所有的点都被删除为止,删除一个点的时候,它所连的边也都被删除掉。问你连续操作可不可以把所有点都删除。

从叶子节点向上删除,因为如果底层的节点本来可以删除的,但是你没有删除,反而对其父节点删除了,那么这个底层节点就可能永远不能删除了。也可以这么想,如果你不删除这个底层节点,而是删除其上面比较高的节点后遍历到这在删除,一种可能是没啥影响,但是第二种就是永远删除不了了。

 

也就是,如果从上向下删除可以删除完,那么从下向上也可以。

但是从下向上可以删除完,从上向下就不一定了。

例如1-2 1-3 2-4 3-5这样,如果先删除1显然是有问题的。

#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=2e5+88;
int H[N],du[N],A[N],to[N<<1],nxt[N<<1];
int x,n,rt,tot,ans;
void add(int u,int v){
    to[++tot]=v;nxt[tot]=H[u];H[u]=tot;
} 
queue<int>Q;
void dfs(int u,int fa){
    for(int i=H[u];i;i=nxt[i]) {
        int v=to[i];
        if(v==fa) continue;
        dfs(v,u);
    }
        if(du[u]==0) return;
    if(!(du[u]&1)){
        for(int i=H[u];i;i=nxt[i]) {
            int v=to[i];
            --du[v];
            if(du[v]<=0) continue;
            if(!(du[v]&1)&&v!=fa) Q.push(v);
        }
        du[u]=0;
        A[++ans]=u;
    }
    while(!Q.empty()) {
        int v=Q.front();
        Q.pop();
        if(du[v]&1||du[v]<=0) continue;
        for(int i=H[v];i;i=nxt[i]) {
            int vv=to[i];
            --du[vv];
            if(du[vv]&1||du[vv]<=0) continue;
            if(!(du[vv]&1)) Q.push(vv);
        }
        du[v]=0;
        A[++ans]=v;
    }
}
bool vis[N];
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;++i) {
        scanf("%d",&x);
        if(!x) rt=i;
        else ++du[x],++du[i],add(x,i),add(i,x);
    } 
    dfs(rt,-1);
    bool f=1;
    for(int i=1;f&&i<=n;++i) if(du[i]>0) f=0;
    if(f) {
        puts("YES");
        for(int i=1;i<=ans;++i) printf("%d\n",A[i]),vis[A[i]]=1;
        for(int i=1;i<=n;++i) if(!vis[i]) printf("%d\n",i);
    }
    else puts("NO");
}

E. Cutting Rectangle

题意:假设你有一个长方形,那么你可以用线条划分它,横线为p,竖线为q的话,那么你划分的块就是(p+1)(q+1)个,现在把这些块给你,各个块的子长方形不重复,不能旋转,问你有多少种原长方形。

块的个数为2e5,每个块中有1e12个长宽上线均为1e12的子长方形。(极限一个块的面积1e12*1e12*1e12).

然后分析一波(别人的代码)发现最终的块是由一个一个子长方形拓展而来的。

首先排序,排序规则是长度为第一关键字,宽度为第二,不考虑数量。

就是先不考虑数量,由于我们划的线条只能是直线,所以长度相同的必然是在一片(一片是指这些长度相同的子长方形堆成的是一个宽度是答案长方形的宽度大长方形)里面的,但是在大长方形的哪个区域是无所谓的,因为答案长方形是一样的,而且只需要求答案长方形。

原因是这些长度相同的长方形宽度是不同的,所以只能是x配y组成完整的长方形(x!=y)然而这样你划的线就只能是曲线了,拐了弯的样子。

那么解决完长度问题并得到一个相同长度的xn循环节,那么R[xn+k]的宽度必须要和R[k]相等的,还是因为你要画直线,不相等的话没办法画直线的。并且由此也可以得到这n个必须要能被xn整除的,因为你要对接的。

考虑数量,先说结论,只要每个循环节里面的C[k]/C[i]==C[k%xn]/C[1]就ok了,相等的话就说明其中一个可以进行缩放,就是x行排好几个的样子就可以了。然后可能是数据不够严谨的样子R[j+1].c*R[i].c!=R[i+j].c*R[1].c换成乘法没爆....

然后由刚才考虑数量的时候缩放就可以看出来这个答案是取决于每个块可以进行怎样的缩放,那每个块可以进行的缩放就是所有块的数量的最小公倍数的因子数了。然后就完成了。

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
struct Rec{
    long long w,h,c;
    bool operator < (const Rec &A)const{
       return w==A.w?h<A.h:w<A.w;
    }
}R[200005];
int main(){
    int n,xn;
    scanf("%d",&n);
    for(int i=1;i<=n;++i) {
        scanf("%lld%lld%lld",&R[i].w,&R[i].h,&R[i].c); 
    }
    sort(R+1,R+n+1);
    xn=n;
    for(int i=2;i<=n;++i) {
        if(R[i].w!=R[i-1].w) {
           xn=i-1;
           break;
        }
    }
    if(n%xn) return puts("0"),0;
    for(int i=xn+2;i<=n;++i) {
        if(i%xn!=1&&R[i].w!=R[i-1].w) return puts("0"),0;
    }
    for(int i=xn+1;i<=n;++i) {
        if(R[i-xn].h!=R[i].h) return puts("0"),0;
    }
    for(int i=xn+1;i<=n;i+=xn) {
        for(int j=1;j<xn;++j) {
            if(R[j+1].c*R[i].c!=R[i+j].c*R[1].c) return puts("0"),0;
        }
    }
    long long nc=R[1].c;
    for(int i=2;i<=n;++i) nc=__gcd(nc,R[i].c);
    int ans=0;
    for(long long i=1;i*i<=nc;++i) {
        if(nc%i==0) {
            ++ans;
            if(i*i!=nc) ++ans;
        }
    }
    printf("%d\n",ans);
} 

 F:Frequency of String

题意:给你一个1e5的字符串,然后1e5个询问,每个询问有一个number和str,问你最短的子串满足str至少出现了number次。str总长度不超过1e5.

哈希+unordered_map.

#include<bits/stdc++.h>
using namespace std;
const int INF=0x3f3f3f3f;
const int N=1e5+88;
#define ull unsigned long long
const ull base=131;
ull Hash[N],p[N];
unordered_map<ull,int>M;
int L[N],id[N],a[N];
vector<int>ans[N];
string str,op;
ull get(int l,int r){
    if(l==0) return Hash[r];
    return Hash[r]-Hash[l-1]*p[r-l+1];
}
inline bool cmp(const int &x,const int &y){
    return L[x]<L[y];
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin>>str;
    int n,len=str.length();
    Hash[0]=str[0];
    p[0]=1;
    for(int i=1;i<len;++i) {
        Hash[i]=Hash[i-1]*base+str[i];
        p[i]=p[i-1]*base;
    }
    cin>>n;
    for(int i=1;i<=n;++i) {
        cin>>a[i]>>op;
        id[i]=i,L[i]=op.length();
        ull u=0;
        for(int j=0;j<L[i];++j) u=u*base+op[j];
        M[u]=i;
    }
    sort(id+1,id+1+n,cmp);
    for(int i=1,k=1;i<=n;i+=k) {
        k=1;
        while(L[id[i+k]]==L[id[i]]) ++k;
        int pp=id[i];
        for(int j=0;j+L[pp]<=len;++j) {
            ull m=get(j,j+L[pp]-1);
            if(M.find(m)!=M.end()) ans[M[m]].push_back(j);
        }
    }
    for(int i=1;i<=n;++i) {
        if(ans[i].size()<a[i]) cout<<-1<<endl;
        else {
            int minn=INF;
            for(int j=a[i]-1;j<ans[i].size();++j) {
                minn=min(minn,ans[i][j]-ans[i][j-a[i]+1]+L[i]);
            }
            cout<<minn<<endl;
        }
    }
}

 

posted @ 2018-04-21 15:21  Billyshuai  阅读(189)  评论(0编辑  收藏  举报