2020 CCPC Wannafly Winter Camp Day5 解题报告
A
考虑到k最多只有3个可以考虑状压,然后就是简单的贪心了。
#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;
int t[4];
int st[N];
int cnt[8];
int main(){
int n=input(),k=input();
for(int i=0;i<k;i++){
t[i]=input();
for(int j=1;j<=t[i];j++){
int x=input();
st[x]|=(1<<i);
}
}
for(int i=1;i<=n;i++) cnt[st[i]]++;
if(k==1) printf("%d\n",t[0]);
else if(k==2){
printf("%d\n",max(cnt[1],cnt[2])+cnt[3]);
}else{
int Ans=cnt[7];
for(int i=0;i<3;i++){
Ans+=cnt[(7^(1<<i))];
if(cnt[(7^(1<<i))]<cnt[(1<<i)])cnt[(1<<i)]-=cnt[(7^(1<<i))];
else cnt[(1<<i)]=0;
}
Ans+=max(cnt[1],max(cnt[2],cnt[4]));
printf("%d\n",Ans);
}
}
E
暴力枚举前三个数,第四个数就可以直接判断出来了。
#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=1e6+7;
ll inv[N];
ll p,mid;
void Solve(){
stack<PII> tmp;
vector<PII> Ans;
for(ll i=2;i<p;i++){
if(!tmp.empty()){
if(i>=tmp.top().fr){
while(!tmp.empty())
Ans.push_back(tmp.top()),tmp.pop();
break;
}
}
inv[i]=((p-p/i)*inv[p%i])%p;
if(inv[i]<mid){
Ans.push_back(mp(i,inv[i]));
mid=inv[i];
if(tmp.empty()) tmp.push(mp(inv[i],i));
else if(tmp.top().fr>inv[i])
tmp.push(mp(inv[i],i));
}
}
printf("%d\n",Ans.size());
for(auto v:Ans){
printf("%lld %lld\n",v.fr,v.sc);
}
}
int main(){
inv[0]=inv[1]=1;
int T=input();
while(T--){
p=input();
mid=p;
Solve();
}
}
G
可以双向枚举i,枚举到一个上限u后可以发现后面的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;
}
#define PII pair<ll,ll>
#define fr first
#define sc second
#define mp make_pair
const int N=1e6+7;
ll inv[N];
ll p,mid;
void Solve(){
stack<PII> tmp;
vector<PII> Ans;
for(ll i=2;i<p;i++){
if(!tmp.empty()){
if(i>=tmp.top().fr){
while(!tmp.empty())
Ans.push_back(tmp.top()),tmp.pop();
break;
}
}
inv[i]=((p-p/i)*inv[p%i])%p;
if(inv[i]<mid){
Ans.push_back(mp(i,inv[i]));
mid=inv[i];
if(tmp.empty()) tmp.push(mp(inv[i],i));
else if(tmp.top().fr>inv[i])
tmp.push(mp(inv[i],i));
}
}
printf("%d\n",Ans.size());
for(auto v:Ans){
printf("%lld %lld\n",v.fr,v.sc);
}
}
int main(){
inv[0]=inv[1]=1;
int T=input();
while(T--){
p=input();
mid=p;
Solve();
}
}
I
四叉线段树直接搞定。(注意需要剪枝)
note:听说二维线段树跑得飞快🤦
#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=2007;
ll t[(N*N)<<3],a[N][N];
int n,m1,m2;
void build(int rt,int xl,int xr,int yl,int yr){
if(xl>xr||yl>yr) return;
if(xl==xr&&yl==yr){
t[rt]=a[xl][yl];
return;
}
int midx=(xl+xr)>>1,midy=(yl+yr)>>1;
build(rt*4,xl,midx,yl,midy);
build(rt*4+1,midx+1,xr,yl,midy);
build(rt*4+2,xl,midx,midy+1,yr);
build(rt*4+3,midx+1,xr,midy+1,yr);
t[rt]=max(t[rt*4],max(t[rt*4+1],max(t[rt*4+2],t[rt*4+3])));
}
ll Ans;
void query(int rt,int xl,int xr,int yl,int yr,int qxl,int qxr,int qyl,int qyr){
if(xl>xr||yl>yr) return;
if(xr<qxl||yr<qyl) return;
if(xl>qxr||yl>qyr) return;
if(t[rt]<=Ans) return;
if(qxl<=xl&&xr<=qxr&&qyl<=yl&&yr<=qyr){
Ans=max(t[rt],Ans);
return;
}
int midx=(xl+xr)>>1,midy=(yl+yr)>>1;
ll res=0;
query(rt*4,xl,midx,yl,midy,qxl,qxr,qyl,qyr);
query(rt*4+1,midx+1,xr,yl,midy,qxl,qxr,qyl,qyr);
query(rt*4+2,xl,midx,midy+1,yr,qxl,qxr,qyl,qyr);
query(rt*4+3,midx+1,xr,midy+1,yr,qxl,qxr,qyl,qyr);
return;
}
int main(){
n=input(),m1=input(),m2=input();
for(int i=1;i<=m1;i++){
int x1=input(),y1=input(),x2=input(),y2=input(),w=input();
a[x1][y1]+=w,
a[x1][y2+1]-=w,
a[x2+1][y1]-=w,
a[x2+1][y2+1]+=w;
}
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
a[i][j]+=a[i-1][j]+a[i][j-1]-a[i-1][j-1];
build(1,1,n,1,n);
for(int i=1;i<=m2;i++){
int x1=input(),y1=input(),x2=input(),y2=input();
Ans=0;
query(1,1,n,1,n,x1,x2,y1,y2);
printf("%lld\n",Ans);
}
}
J
暴力的预处理出在\(F\)在每个位置的情况,将之映射到一个一维空间中(可以使用bitset实现),因为题意是求的向量可以组合()多少种不同的向量,很自然的考虑到可以把这个一维化的的向量插入到一个线性基中,然后求线性基的秩,就可以算出答案为\(2^k\),或者像官方题解一样直接把所有向量拼成一个矩阵进行高斯消元也是可以的。
#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=1e9+7;
bitset <1024> linear[1024];
void Ins(bitset<1024> k){
for(int i=1023;i>=0;i--){
if((k>>i)[0])
if(linear[i]==0){linear[i]=k;break;}
else k^=linear[i];
}
}
int count(){
int res=0;
for(int i=1023;i>=0;i--){
if(linear[i]!=0) res++;
}
return res;
}
ll powmod(ll a,ll b){
ll res=1;
while(b){
if(b&1) res=(res*a)%mod;
a=a*a%mod;
b>>=1;
}
return res;
}
char s[1024][1024];
int m[1024][1024],t[1024][1024];
int main(){
int n=input();
for(int i=1;i<=(1<<n);i++){
scanf("%s",s[i]+1);
}
for(int i=1;i<=(1<<n);i++)
for(int j=1;j<=(1<<n);j++)
m[i][j]=s[i][j]-'0';
for(int i=1;i<=(1<<n);i++){
for(int j=1;j<=(1<<n);j++){
bitset<1024> tmp;
for(int a=1;a<=(1<<n);a++){
for(int b=1;b<=(1<<n);b++){
t[a][b]=m[(a+i-2)%(1<<n)+1][(b+j-2)%(1<<n)+1];
tmp[a*(1<<n)-a+b-1]=t[a][b];
}
}
Ins(tmp);
}
}
ll Ans=count();
printf("%lld\n",powmod(2,Ans));
}
B
首先对于两个集合的并,我们知道\(S_u \cap S_v = S_u + S_v - S_u \cap S_v\)。对于本题按操作顺序倒着合并集合,就可以得到各个节点在所有的集合中出现的次数了。原理大概是一次合并对答案的影响只对小于当前编号的元素有影响,所以需要倒着合并集合。
#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
const int N=5e5+7;
PII E[N];
int opr[N];
int Ans[N],in[N];
int main(){
int n=input(),m=input();
for(int i=1;i<n;i++){
Ans[i]=1,in[i]=0;
E[i]=mp(input(),input());
}Ans[n]=1;
for(int i=1;i<=m;i++) opr[i]=input();
for(int i=m;i>=1;i--){
int id=opr[i],u=E[id].fr,v=E[id].sc;
int tmp=Ans[u]+Ans[v]-in[id];
Ans[u]=Ans[v]=in[id]=tmp;
}
for(int i=1;i<=n;i++) printf("%d%c",Ans[i],i==n?'\n':' ');
}