zoukankan      html  css  js  c++  java
  • 2020 CCPC Wannafly Winter Camp Day1 解题报告

    B


    简单题,已知原文和秘钥、加密得到密文的方法、密文和秘钥。可以轻松的解密出原文:只要用密文对应的数字减去秘钥对应的数字就可以了。


    #include <bits/stdc++.h>
    
    using namespace std;
    
    #define ll long long
    ll input(){
    	ll x=0,f=0;char ch=getchar();
    	while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
    	while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    	return f? -x:x;
    }
    
    char getch(int x){
    	if(x>25) return (char)(x-26+'A');
    	else return (char)('a'+x);
    }
    
    int getid(char ch){
    	if(ch>='a'&&ch<='z') return (int)(ch-'a');
    	else return (int)(ch-'A'+26);
    }
    
    const int N=1e3+7;
    const int mod=52;
    
    char s[N][N];
    int a[N][N];
    int opr[N][2];
    int n,m;
    
    void antiopr(int x,int y){
    	int lenx=strlen(s[x]),leny=strlen(s[y]);
    	int tmpx[N];
    	for(int i=0;i<lenx;i++)
    		tmpx[i]=a[x][i];
    
    	for(int i=0;i<leny;i++){
    		a[y][i]=(a[y][i]-tmpx[i%lenx]+mod)%mod;
    	}
    }
    
    int main(){
    	n=input(),m=input();
    	for(int i=1;i<=m;i++){
    		opr[i][0]=input(),opr[i][1]=input();
    	}
    
    	for(int i=1;i<=n;i++){
    		scanf("%s",s[i]);
    		int len=strlen(s[i]);
    		for(int j=0;j<len;j++){
    			a[i][j]=getid(s[i][j]);
    		}
    	}
    
    	for(int i=m;i>=1;i--){
    		antiopr(opr[i][0],opr[i][1]);
    	}
    	for(int i=1;i<=n;i++){
    		int len=strlen(s[i]);
    		for(int j=0;j<len;j++){
    			printf("%c",getch(a[i][j]));
    		}printf("
    ");
    	}
    }
    

    F


    二分答案(K)就行了,算小于等于(K)的乘积有多少个。但是问题是当数为0或者负数时需要讨论,比较麻烦。赛中没有调出来。


    #include <bits/stdc++.h>
    
    using namespace std;
    
    #define ll long long
    ll input(){
    	ll x=0,f=0;char ch=getchar();
    	while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
    	while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    	return f? -x:x;
    }
    
    const int N=1e5+7;
    const ll inf=1e12+7;
    
    ll a[N],b[N];
    ll n,m;
    ll k;
    
    ll check(ll x){
    	ll cnt=0;
    	for(int i=1;i<=n;i++){
    		if(a[i]==0){
    			if(x<=0) cnt+=m;
    			continue;
    		}
    		ll tmp=x/a[i];
    		if(tmp*a[i]<x){
    			if((tmp+1)*a[i]>=x) tmp++;
    			else tmp--;
    		}
    
    		if((tmp+1)*a[i]>=x)
    			cnt+=m+1-(lower_bound(b+1,b+m+1,tmp)-b);
    		else
    			cnt+=upper_bound(b+1,b+m+1,tmp)-b-1;
    	}
    	return cnt>=k;
    }
    
    int main(){
    	n=input(),m=input();
    	k=input();
    
    	for(int i=1;i<=n;i++)
    		a[i]=input();
    	for(int i=1;i<=m;i++)
    		b[i]=input();
    
    	sort(a+1,a+1+n);
    	sort(b+1,b+1+m);
    
    	ll l=-inf,r=inf,Ans=0;
    	while(l<=r){
    		ll mid=(l+r)/2;
    		if(check(mid)) Ans=mid,l=mid+1;
    		else r=mid-1;
    	}
    	cout<<Ans<<endl;
    }
    

    H


    (y)要满足以下两个条件:

    1. (y)必须是(k)的倍数,因为我们要区分(gcd(y,k))(k)
    2. 对于([1, lfloor frac{n}{k} floor])中的每一个质数(p),(y)必须是(p)的倍数,因为我们要区分(p)(pk)

    所以答案就是([1, lfloor frac{n}{k} floor])中的每一个质数与k的乘积。


    ss = []
    def work():
    	maxl = 500
    	count = 0        
    	check = [] 
    	for x in range(0, maxl+1):
    		check.append(0)
    	for i in range(2, maxl):
    		if check[i] == 0:
    			ss.append(i)
    			count += 1
    		for j in range(0, count):
    			if ss[j]*i > maxl:
    				break
    			check[ss[j]*i] = 1 
    			if i%ss[j] == 0:
    				break
    	return count
    
    cnt = work()
    T = int(input())
    for i in range(1,T+1):
    	n,k = map(int, input().split())
    
    	tmp = n//k;
    
    	Ans = 1
    	
    	for i in range(0,cnt):
    		if ss[i] > tmp :
    			break
    		Ans = Ans * ss[i]
    
    	print(Ans*k)
    
    

    I


    补这个题花了我好大的功夫学了树套树和线段树的分离与合并。本题就是势能线段树套权值线段树,真的写出来还是有点代码量的,考场上肯定就直接分块刚了。

    这个题暴力是可以过的,我自己在牛客上还拜托了去了现场的盆友在PTA(现场的数据和环境)上交都AC了。某知名选手翻车现场


    暴力:

    #include <bits/stdc++.h>
    
    using namespace std;
    
    #define ll long long
    ll input(){
    	ll x=0,f=0;char ch=getchar();
    	while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
    	while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    	return f? -x:x;
    }
    
    const int N=8e4+7;
    
    int n,m;
    int a[N],p[N];
    
    int main(){
    		freopen("in.txt","r",stdin);
    
    	n=input(),m=input();
    	for(int i=1;i<=n;i++)
    		a[i]=input();
    
    	for(int j=1;j<=m;j++){
    		int opr=input(),l=input(),r=input(),x=input();
    		if(opr==1){
    			for(int i=l;i<=r;i++)
    				a[i]=min(x,a[i]);
    		}else{
    			for(int i=1;i<=n;i++) p[i]=0;
    			for(int i=l;i<=r;i++) p[a[i]]++;
    			int tmp=0;
    			for(int i=1;i<=n;i++){
    				tmp+=p[i];
    				cout<<tmp<<endl;
    				if(tmp>=x){
    					printf("%d: %d
    ",j,i);
    					break;
    				}
    			}
    		}
    	}
    }
    

    树套树:

    #include <bits/stdc++.h>
    
    using namespace std;
    
    #define ll long long
    ll input(){
    	ll x=0,f=0;char ch=getchar();
    	while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
    	while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    	return f? -x:x;
    }
    
    const int N=8e4+7,MN=N*20*20;
    
    vector <int> roots;
    int n,m,top,rec[MN];
    
    namespace Value_Tree{
    	
    	#define ls(x) t[x].ch[0]
    	#define rs(x) t[x].ch[1]
    
    	struct node{
    		int ch[2],s;
    		void clear(){ch[0]=ch[1]=s=0;}
    		node(){clear();}
    	}t[MN];
    
    	int size=0;
    
    	int newnode(){return !top?++size:rec[top--];}
    	void remove(int rt){rec[++top]=rt;t[rt].clear();}
    
    	int insert(int rt,int l,int r,int pos,int x){
    		if(!rt) rt=newnode();
    		t[rt].s+=x;
    		if(l==r) return rt;
    		int mid=(l+r)>>1;
    		if(pos<=mid) ls(rt)=insert(ls(rt),l,mid,pos,x);
    		else rs(rt)=insert(rs(rt),mid+1,r,pos,x);
    		return rt;
    	}
    
    	int insert(int rt,int val,int x=1){return insert(rt,1,n,val,x);}
    
    	int merge(int l,int r){
    		if(l==0&&r==0) return l;
    		int rt=newnode();
    		t[rt].s=t[l].s+t[r].s;
    		ls(rt)=merge(ls(l),ls(r));
    		rs(rt)=merge(rs(l),rs(r));
    		return rt;
    	}
    
    	int merge2(int l,int r,int del=0){
    		if(r==0) return l;
    		if(l==0) l=newnode();
    		t[l].s+=t[r].s;
    		ls(l)=merge2(ls(l),ls(r),del);
    		rs(l)=merge2(rs(l),rs(r),del);
    		if(del) remove(r);
    		if(!del&&t[l].s==0){remove(l);return 0;}
    		return l;
    	}
    
    	void recycle(int rt){
    		if(!rt) return;
    		remove(rt);
    		recycle(ls(rt)),
    		recycle(rs(rt));
    	}
    
    	int query(int l,int r,int val){
    		if(l==r) return l;
    		int mid=(l+r)>>1;
    		int lsum=0;
    		for(auto rt:roots) lsum+=t[ls(rt)].s;
    		if(lsum>=val){
    			for(auto &rt:roots) rt=ls(rt);
    			return query(l,mid,val);
    		}else{
    			for(auto &rt:roots) rt=rs(rt);
    			return query(mid+1,r,val-lsum);
    		}
    	}
    }
    
    int a[N];
    
    namespace seg{
    	struct node{
    		int mx,cnt,sc,lazy,rt;
    		node(){mx=cnt=sc=rt=0;lazy=INT_MAX;}
    	}t[N<<2];
    
    	void put(int rt){
    		if(t[rt<<1].mx>t[rt<<1|1].mx){
    			t[rt].mx=t[rt<<1].mx;
    			t[rt].sc=max(t[rt<<1|1].mx,t[rt<<1].sc);
    			t[rt].cnt=t[rt<<1].cnt;
    		}else if(t[rt<<1].mx<t[rt<<1|1].mx){
    			t[rt].mx=t[rt<<1|1].mx;
    			t[rt].sc=max(t[rt<<1].mx,t[rt<<1|1].sc);
    			t[rt].cnt=t[rt<<1|1].cnt;
    		}else{
    			t[rt].mx=t[rt<<1].mx;
    			t[rt].sc=max(t[rt<<1].sc,t[rt<<1|1].sc);
    			t[rt].cnt=t[rt<<1].cnt+t[rt<<1|1].cnt;
    		}
    	}
    
    	void build(int rt,int l,int r){
    		if(l==r){
    			t[rt].mx=a[l];
    			t[rt].cnt=1;
    			t[rt].rt=Value_Tree::insert(t[rt].rt,a[l]);
    			return;
    		}
    		int mid=(l+r)>>1;
    		build(rt<<1,l,mid);
    		build(rt<<1|1,mid+1,r);
    		t[rt].rt=Value_Tree::merge(t[rt<<1].rt,t[rt<<1|1].rt);
    		put(rt);
    	}
    
    	void modify(int rt,int val){
    		if(t[rt].mx<=val) return;
    		Value_Tree::insert(t[rt].rt,t[rt].mx,-t[rt].cnt);
    		Value_Tree::insert(t[rt].rt,val,t[rt].cnt);
    		t[rt].mx=val;
    		t[rt].lazy=val;
    	}
    
    	void pushdown(int rt){
    		if(t[rt].lazy==INT_MAX) return;
    		modify(rt<<1,t[rt].lazy);
    		modify(rt<<1|1,t[rt].lazy);
    		t[rt].lazy=INT_MAX;
    	}
    
    	int modify(int rt,int l,int r,int ql,int qr,int val){
    		if(r<ql||qr<l||t[rt].mx<val) return 0;
    		if(ql<=l&&r<=qr&&t[rt].sc<val){
    			int root;
    			root=Value_Tree::insert(0,t[rt].mx,-t[rt].cnt);
    			root=Value_Tree::insert(root,val,t[rt].cnt);
    			modify(rt,val);
    			return root;
    		}
    		pushdown(rt);
    		int mid=(l+r)>>1,root;
    		root=Value_Tree::merge2(modify(rt<<1,l,mid,ql,qr,val),modify(rt<<1|1,mid+1,r,ql,qr,val),1);
    		t[rt].rt=Value_Tree::merge2(t[rt].rt,root);
    		put(rt);
    		return root;
    	}
    
    	void query(int rt,int l,int r,int ql,int qr){
    		if(ql<=l&&r<=qr){
    			roots.push_back(t[rt].rt);
    			return;
    		}
    		pushdown(rt);
    		int mid=(l+r)>>1;
    		if(ql<=mid) query(rt<<1,l,mid,ql,qr);
    		if(mid<qr) query(rt<<1|1,mid+1,r,ql,qr);
    	}
    }
    
    int main(){
    	n=input(),m=input();
    	for(int i=1;i<=n;i++) a[i]=input();
    	seg::build(1,1,n);
    	for(int i=1;i<=m;i++){
    		int opr=input(),l=input(),r=input(),x=input();
    		if(opr==1) Value_Tree::recycle(seg::modify(1,1,n,l,r,x));
    		else{
    			roots.clear();
    			seg::query(1,1,n,l,r);
    			printf("%d
    ",Value_Tree::query(1,n,x));
    		}
    	}
    }
    

    A


    把所有区间按照中点从小到大排序,我们就可以得到最优的序列。然后问题转化为了求一个序列的逆序对个数的期望。根据期望的可加性,我们可以枚举每一对位置,答案就是它们形成逆序对的概率加起来。对于一对随机变量([l_1,r_1]),([l_2,r_2])左边右边大的期望为:

    [frac{sum_{i=l_1}^{r_1}max(0,min(i,r_2)-l_2+1)}{(r_1-l_1+1)*(r_2-l_2+1)} ]

    最后(O(n^2))计算所有的答案即可。


    #include <bits/stdc++.h>
    
    using namespace std;
    
    #define ll long long
    ll input(){
    	ll x=0,f=0;char ch=getchar();
    	while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
    	while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    	return f? -x:x;
    }
    
    #define PII pair<ll,ll>
    #define fr first
    #define sc second
    #define mp make_pair
    
    const int N=5e3+7;
    const ll mod=998244353;
    
    PII a[N];
    int n;
    
    bool cmp(PII x,PII y){
    	return (x.fr+x.sc)<(y.fr+y.sc);
    }
    
    ll powmod(ll a,ll b){
    	ll res=1;
    	while(b){
    		if(b&1) res=res*a%mod;
    		a=a*a%mod;
    		b>>=1ll;
    	}
    	return res;
    }
    
    PII cal(PII x,PII y){
    	ll sum=(x.sc-x.fr+1)*(y.sc-y.fr+1)%mod;
    	ll cnt=0;
    	if(x.sc>y.sc)
    		cnt=(cnt+((x.sc-y.sc)*(y.sc-y.fr+1)%mod+((y.sc-y.fr+1)*(y.sc-y.fr)/2)%mod)%mod)%mod;
    	else if(x.sc>y.fr){
    		ll len=x.sc-x.fr+1;
    		if(x.fr>y.fr) cnt=(cnt+(len*(x.fr-y.fr)%mod+(len*(len-1)/2)%mod)%mod)%mod;
    		else cnt=(cnt+((x.sc-y.fr+1)*(x.sc-y.fr)/2)%mod)%mod;
    	}
    	return mp(cnt,sum);
    }
    
    int main(){
    	n=input();
    	for(int i=1;i<=n;i++){
    		a[i].fr=input(),a[i].sc=input();
    	}
    	sort(a+1,a+n+1,cmp);
    
    	ll X=0,Y=1;
    
    	for(int i=1;i<=n;i++){
    		for(int j=i+1;j<=n;j++){
    			PII tmp=cal(a[i],a[j]);
    			X=(X*tmp.sc%mod+Y*tmp.fr%mod)%mod;
    			Y=Y*tmp.sc%mod;
    		}
    	}
    	printf("%lld
    ",(X*powmod(Y,mod-2)%mod+mod)%mod);
    }
    

    C


    考虑如何求(g(n,m))

    显然我们可以知道(g(n,m)=C_{n}^{2}-sum_{i=1}^{m}c_i)

    然后为了让(g(n,m))尽可能的大,我们要让(c_i)的值尽可能的平均,所以

    [forall c_i in {lfloorfrac{n}{m} floor,lceilfrac{n}{m} ceil}。 ]

    故一共有(n\%m)(lceilfrac{n}{m} ceil)(m-n\%m)(lfloorfrac{n}{m} floor)。即可得:

    [g(n,m)=C_n^2-(n\%m)C_{lceilfrac{n}{m} ceil}^{2}-(m-n\%m)C_{lfloor frac{n}{m} floor}^2 ]

    又有以下两个等式:

    [egin{cases} n\%m=n-lfloor frac{n}{m} floor m \ lceilfrac{n}{m} ceil=lfloor frac{n-1}{m} floor+1 end{cases} ]

    带入上式化简得:

    [g(n,m)=C_n^2-mC_{lfloor frac{n}{m} floor}^2+(n-lfloor frac{n}{m} floor m)(C_{lfloor frac{n-1}{m} floor+1}^{2}-C_{lfloor frac{n}{m} floor}^2) ]

    由于上式答案跟(lfloor frac{n}{m} floor,lfloor frac{n-1}{m} floor)有关,故可以用数论分块在(O(sqrt{n}))的复杂度内求得(sum_{i=l}^{r}g(n,i))的答案。


    #include <bits/stdc++.h>
     
    using namespace std;
     
    #define ll long long
    ll input(){
        ll x=0,f=0;char ch=getchar();
        while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
        while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
        return f? -x:x;
    }
     
    const ll mod=998244353;
     
    ll sum(ll l,ll r){
        return ((l+r)*(r-l+1)/2)%mod;
    }
     
    ll C2(ll n){
        return ((n)*(n-1)/2)%mod;
    }
     
    ll n;
     
    ll calc(ll l,ll r){
        ll res=0;
        for(ll i=l,j;i<=r;i=j+1){
            j=min(n/(n/i),r);
            res+=sum(i,j)*C2(n/i)%mod;
            res%=mod;
            res+=((n*(j-i+1)%mod-sum(i,j)*(n/i)%mod+mod)%mod*(C2((n-1)/i+1)-C2(n/i)+mod)%mod)%mod;
            res%=mod;
        }
        return  res;
    }
     
    int main(){
        int T=input();
        while(T--){
            n=input();
            ll l=input(),r=input();
            ll Ans=(C2(n)*(r-l+1))%mod;
            Ans=(Ans-calc(l,r)+mod)%mod;
            printf("%lld
    ",Ans);
        }
    }
    

    E


    类似于换根的做法。

    对与当点(i)为根时我们令其答案为(Ans_i),对于其父节点(f_i)的答案(Ans_{f_i})我们有两种情况需要讨论。

    1. 当路径不同时经过(i,f_i)时,答案不会发生变化。

    2. 路径同时经过(i,f_i)时,如果这条路径为(u,v),且长度为(l),设(x=d_i-d_u(d_x表示点x的深度))那么对(i)的贡献为(x(l-x)),那么对(f_{i})的答案为((x+1)(l-x-1))。那么(Ans_i-Ans_{f_i})(2*x+1-l),那么将其拆开就有(2(d_i-d_u)-l+1)

      我们的目标是求出任意点的(2(d_i-d_u)-l+1),我们发现(2(d_i-d_u)-l+1)是一个等差数列,我们可以很方便的用树上前缀和来解决问题。

      最后只要求出(Ans_1),对整个树进行DFS就,就可以获得所有的答案了。


      #include <bits/stdc++.h>
      
      using namespace std;
      
      #define ll long long
      ll input(){
      	ll x=0,f=0;char ch=getchar();
      	while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
      	while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
      	return f? -x:x;
      }
      
      #define PII pair<int,int>
      #define fr first
      #define sc second
      #define mp make_pair
      #define pb push_back
      
      const int N=3e5+7;
      
      int n,m;
      vector <int> G[N];
      
      int dep[N],f[N][20];
      
      void dfs(int u,int fa){
      	dep[u]=dep[fa]+1;
      	f[u][0]=fa;
      	for(auto v:G[u]){
      		if(v==fa) continue;
      		dfs(v,u);
      	}
      }
      
      void work(){
      	for(int i=1;i<20;i++){
      		for(int j=1;j<=n;j++){
      			f[j][i]=f[f[j][i-1]][i-1];
      		}
      	}
      }
      
      int LCA(int u,int v){
      	if(dep[u]<dep[v]) swap(u,v);
      	for(int i=19;i>=0;i--)
      		if(dep[f[u][i]]>=dep[v]) u=f[u][i];
      	if(u==v) return u;
      	for(int i=19;i>=0;i--)
      		if(f[u][i]!=f[v][i]) u=f[u][i],v=f[v][i];
      	return f[u][0];
      }
      
      ll Ans[N],A[N],B[N];
      
      void dfs2(int u,int fa){
      	for(auto v:G[u]){
      		if(v==fa) continue;
      		dfs2(v,u);
      		A[u]+=A[v],B[u]+=B[v];
      	}
      }
      
      void dfs3(int u,int fa){
      	for(auto v:G[u]){
      		if(v==fa) continue;
      		Ans[v]=Ans[u]+A[v]*1ll*dep[v]+B[v];
      		dfs3(v,u);
      	}
      }
      
      
      int main(){
      	n=input(),m=input();
      	for(int i=1;i<n;i++){
      		int u=input(),v=input();
      		G[u].pb(v),G[v].pb(u);
      	}
      	
      	dfs(1,0);
      	work();
      
      	for(int i=1;i<=m;i++){
      		int u=input(),v=input();
      		int lca=LCA(u,v);
      		int l=dep[u]+dep[v]-2*dep[lca],x=dep[u]-dep[lca];
      		A[u]-=2,A[v]-=2,A[lca]+=4;
      		B[u]+=2ll*dep[u]-l+1;
      		B[v]+=2ll*dep[v]-l+1;
      		B[lca]-=2*dep[u]+2*dep[v]-2*l+2;
      		Ans[1]+=1ll*x*(l-x);
      	}
      	dfs2(1,0);
      	dfs3(1,0);
      	for(int i=1;i<=n;i++){
      		printf("%lld
      ",Ans[i]);
      	}
      }
      
  • 相关阅读:
    python深度学习之灾难求生预测(titanic)
    python深度学习之语音识别(speech recognize)
    greenplum集群状态恢复与同步
    python手写图片识别MNIST
    python随机森林房价预测
    机器学习常用模型
    python爬虫优化和错误日志分析
    数据挖掘数学基础
    虚拟机spark集群搭建
    虚拟机zookeeper和hbase集群搭建
  • 原文地址:https://www.cnblogs.com/-aether/p/12285653.html
Copyright © 2011-2022 走看看