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("\n");
}
}
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\)要满足以下两个条件:
- \(y\)必须是\(k\)的倍数,因为我们要区分\(gcd(y,k)\)和\(k\)
- 对于\([1, \lfloor \frac{n}{k} \rfloor]\)中的每一个质数\(p\),\(y\)必须是\(p\)的倍数,因为我们要区分\(p\)和\(pk\)
所以答案就是\([1, \lfloor \frac{n}{k} \rfloor]\)中的每一个质数与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\n",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\n",Value_Tree::query(1,n,x));
}
}
}
A
把所有区间按照中点从小到大排序,我们就可以得到最优的序列。然后问题转化为了求一个序列的逆序对个数的期望。根据期望的可加性,我们可以枚举每一对位置,答案就是它们形成逆序对的概率加起来。对于一对随机变量\([l_1,r_1]\),\([l_2,r_2]\)左边右边大的期望为:
最后\(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\n",(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\)的值尽可能的平均,所以
故一共有\(n\%m\)个\(\lceil\frac{n}{m}\rceil\)和\(m-n\%m\)个\(\lfloor\frac{n}{m}\rfloor\)。即可得:
又有以下两个等式:
带入上式化简得:
由于上式答案跟\(\lfloor \frac{n}{m} \rfloor,\lfloor \frac{n-1}{m} \rfloor\)有关,故可以用数论分块在\(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\n",Ans);
}
}
E
类似于换根的做法。
对与当点\(i\)为根时我们令其答案为\(Ans_i\),对于其父节点\(f_i\)的答案\(Ans_{f_i}\)我们有两种情况需要讨论。
-
当路径不同时经过\(i,f_i\)时,答案不会发生变化。
-
路径同时经过\(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\n",Ans[i]); } }