Preface
这次CC难度较上两场升高了许多,后面两题都只能借着曲明姐姐和jz姐姐的仙气来做
值得一提的是原来的F大概需要大力分类讨论,结果我写了一大半题目就因为原题被ban了233
最后勉强涨了近200分,下场如果不出意外地话应该可以打到六星。(ORZ七星julao LTL)
A Chef and Maximum Star Value
SB题,可以设一个阈值统计也可以直接根号大暴力,反正都能过
#include<cstdio>
#include<iostream>
#define RI register int
using namespace std;
const int N=100005;
int t,n,x,ct[N*10],ans,mx;
int main()
{
for (scanf("%d",&t);t;--t)
{
RI i,j; for (scanf("%d",&n),ans=mx=0,i=1;i<=n;++i)
{
scanf("%d",&x); ans=max(ans,ct[x]); mx=max(mx,x);
for (j=1;j*j<=x;++j) if (x%j==0)
{
++ct[j]; if (x!=j*j) ++ct[x/j];
}
}
for (printf("%d
",ans),i=1;i<=mx;++i) ct[i]=0;
}
}
B Array Modification
考虑一个数(x),与它对应位置的数记为(y),则((x,y) o (xoperatorname{xor}y,x) o(y,xoperatorname{xor} y) o (x,y))
意味着做(3 imes k)次就会循环出现,然后膜一下再暴力跑即可
WARNING:注意(n)为奇数是最中间一个数做一次就变成(0)了
#include<cstdio>
#define RI register int
using namespace std;
const int N=10005;
int t,n,a[N]; long long k;
int main()
{
for (scanf("%d",&t);t;--t)
{
RI i; scanf("%d%lld",&n,&k);
for (i=1;i<=n;++i) scanf("%d",&a[i]);
if ((n&1)&&k>=(n+1>>1)) a[n+1>>1]=0; k%=3*n;
for (i=1;k;--k,i=i!=n?i+1:1) a[i]^=a[n-i+1];
for (i=1;i<=n;++i) printf("%d%c",a[i],"
"[i==n]);
}
return 0;
}
C Even Edges
分类讨论。首先(m)为偶数显然不需要分,直接输出(1)
如果(m)为奇数但是存在任意一个点度数为奇数那么直接把它单独拿出来即可,答案为(2)
如果没有奇数度数点怎么办,手动构造啊,先随便找一条边拿出其中的一个点,那么剩下的那个点的度数必然是奇数了,重复上面的方法因此答案为3
#include<cstdio>
#define RI register int
using namespace std;
const int N=100005;
int t,n,m,deg[N],x,y;
inline void clear(void)
{
for (RI i=1;i<=n;++i) deg[i]=0;
}
int main()
{
for (scanf("%d",&t);t;--t)
{
RI i; for (scanf("%d%d",&n,&m),i=1;i<=m;++i)
scanf("%d%d",&x,&y),++deg[x],++deg[y];
if (!(m&1))
{
for (puts("1"),i=1;i<=n;++i) printf("%d%c",1,"
"[i==n]);
clear(); continue;
}
bool flag=0; for (i=1;i<=n;++i)
if (deg[i]&1) { flag=1; x=i; break; }
if (flag)
{
for (puts("2"),i=1;i<=n;++i) printf("%d%c",i==x?1:2,"
"[i==n]);
clear(); continue;
}
for (puts("3"),i=1;i<=n;++i) printf("%d%c",i==x?1:(i==y?2:3),"
"[i==n]); clear();
}
return 0;
}
D Bacterial Reproduction
稍微要动下脑子的一题。注意到我们只需要对于节点的性质进行讨论即可:
- 若该点为叶节点,那么从根到它的路径上所有能在规定时间之内到达它的(大于的区间)都可以对他都能造成贡献
- 若该点不是叶节点,那么从根到它的路径上所有能在规定时间之时(单点)到达它的(大于的区间)都可以对他都能造成贡献
那么我们离线处理问题,DFS处理修改,可以用时间减去深度维护下标,用树状数组维护即可
#include<cstdio>
#include<cctype>
#include<vector>
#define int long long
#define RI register int
#define CI const int&
#define pb push_back
#define mp make_pair
using namespace std;
typedef pair <int,int> pi;
const int N=500005;
struct edge
{
int to,nxt;
}e[N<<1]; int n,q,head[N],cnt,x,y,ans[N],deg[N];
vector <pi> ps[N]; vector <int> qs[N];
inline char get(void)
{
char ch; while (isspace(ch=getchar())); return ch;
}
inline void addedge(CI x,CI y)
{
e[++cnt]=(edge){y,head[x]}; head[x]=cnt; ++deg[x];
e[++cnt]=(edge){x,head[y]}; head[y]=cnt; ++deg[y];
}
class Tree_Array
{
private:
int bit[N<<1];
public:
#define lowbit(x) x&-x
inline void add(RI x,CI y)
{
for (x+=n+1;x<=n+q+1;x+=lowbit(x)) bit[x]+=y;
}
inline int get(RI x,int ret=0)
{
for (x+=n+1;x;x-=lowbit(x)) ret+=bit[x]; return ret;
}
#undef lowbit
}BIT;
#define to e[i].to
inline void DFS(CI now=1,CI fa=0,CI dep=0)
{
for (vector <pi>::iterator it=ps[now].begin();it!=ps[now].end();++it)
BIT.add(it->first-dep,it->second);
for (vector <int>::iterator it=qs[now].begin();it!=qs[now].end();++it)
if (deg[now]==1) ans[*it]=BIT.get(*it-dep);
else ans[*it]=BIT.get(*it-dep)-BIT.get(*it-dep-1);
for (RI i=head[now];i;i=e[i].nxt) if (to!=fa) DFS(to,now,dep+1);
for (vector <pi>::iterator it=ps[now].begin();it!=ps[now].end();++it)
BIT.add(it->first-dep,-it->second);
}
#undef to
signed main()
{
RI i; for (scanf("%lld%lld",&n,&q),i=1;i<n;++i)
scanf("%lld%lld",&x,&y),addedge(x,y);
if (n==1) deg[1]=1; else deg[1]=0;
for (i=1;i<=n;++i) scanf("%lld",&x),ps[i].pb(mp(0,x));
for (i=1;i<=q;++i) if (get()=='?') scanf("%lld",&x),qs[x].pb(i);
else scanf("%lld%lld",&x,&y),ps[x].pb(mp(i,y)),ans[i]=-1;
for (DFS(),i=1;i<=q;++i) if (~ans[i]) printf("%lld
",ans[i]);
return 0;
}
F Queries on Matrix
本来只会一个(O(n^3log Q))的暴力方法,主要就是分别考虑行和列的贡献(有多少个是奇数)
然后DP设(f_{i,j})表示做了(i)次,有(j)行奇数的方案数,显然有转移(f_{i,j}=f_{i-1,j-1} imes (n-j+1)+f_{i-1,j+1} imes(j+1)),结合矩阵快速幂转移即可
正解么,生成函数大法好!(让我们一起来膜拜jz姐姐吧)
由于做法比较大力因此可能需要卡常,这里的代码把多项式中的系数为(0)的项省略了
#include<cstdio>
#include<vector>
#include<iostream>
#define RI register int
#define CI const int&
#define pb push_back
using namespace std;
typedef vector <int> VI;
const int N=2005,mod=998244353,inv2=mod+1>>1;
VI A,B,PB[N]; int r[N],c[N];
// A: e^x-e^-x ; B: e^x+e^-x
// use half space and ignore 0
int t,n,m,z,pw[N],fact[N],inv[N],ipw2[N]; long long q;
inline int quick_pow(int x,int p=mod-2,int mul=1)
{
for (;p;p>>=1,x=1LL*x*x%mod) if (p&1) mul=1LL*mul*x%mod; return mul;
}
inline void inc(int& x,CI y)
{
if ((x+=y)>=mod) x-=mod;
}
inline void dec(int& x,CI y)
{
if ((x-=y)<0) x+=mod;
}
inline VI operator * (const VI& A,const VI& B)
{
CI na=A.size(),nb=B.size(); VI C(na+nb-1);
for (RI i=0;i<na;++i) for (RI j=0;j<nb;++j)
inc(C[i+j],1LL*A[i]*B[j]%mod); return C;
}
inline void Div(VI& A) // A/=(e^x+e^-x)
{
CI na=A.size(); VI C(na); RI i;
for (i=na-1;~i;--i) if (A[i])
{
C[i]=A[i]; if (i) dec(A[i-1],A[i]);
}
bool flag=0; for (A.clear(),i=0;i<na;++i)
{
if (C[i]) flag=1; if (flag) A.pb(C[i]);
}
}
inline void DEBUG(const VI& P)
{
int cp=P.size(); for (RI i=0;i<cp;++i) printf("%d ",P[i]); putchar('
');
}
inline int init(CI n=2000)
{
RI i; for (fact[0]=i=1;i<=n;++i) fact[i]=1LL*fact[i-1]*i%mod;
for (inv[n]=quick_pow(fact[n]),i=n-1;~i;--i) inv[i]=1LL*inv[i+1]*(i+1)%mod;
for (ipw2[0]=i=1;i<=n;++i) ipw2[i]=1LL*ipw2[i-1]*inv2%mod;
for (A.pb(mod-1),A.pb(1),B.pb(1),B.pb(1),PB[0].pb(1),i=1;i<=n;++i) PB[i]=PB[i-1]*B;
}
inline int C(CI n,CI m)
{
return 1LL*fact[n]*inv[m]%mod*inv[n-m]%mod;
}
inline int calc(const VI& A,CI c,int ret=0)
{
RI i,j; for (i=j=c;j>0;--i,j-=2) inc(ret,1LL*A[i]*pw[j]%mod);
for (i=0,j=c;j>0;++i,j-=2) if (q&1) dec(ret,1LL*A[i]*pw[j]%mod);
else inc(ret,1LL*A[i]*pw[j]%mod); return ret;
}
inline void solve(int *a,CI n)
{
VI P=PB[n]; RI i; int cp=P.size(); for (i=0;i<cp;++i) P[i]=1LL*P[i]*ipw2[n]%mod;
for (a[0]=calc(P,n),i=1;i<=n;++i) P=P*A,Div(P),a[i]=1LL*calc(P,n)*C(n,i)%mod;
}
int main()
{
for (init(),scanf("%d",&t);t;--t)
{
RI i,j; int ans=0; scanf("%d%d%lld%d",&n,&m,&q,&z);
for (pw[0]=i=1;i<=max(n,m);++i)
pw[i]=quick_pow(i,q%(mod-1));
for (solve(r,n),solve(c,m),i=0;i<=n;++i) for (j=0;j<=m;++j)
if (i*(m-j)+j*(n-i)==z) inc(ans,1LL*r[i]*c[j]%mod);
printf("%d
",ans);
}
return 0;
}
G Faulty System
论文题真有趣。直接看 IOI2018中国国家候选队论文集里的拟阵部分即可
注意这里的拟阵定义就是里面的图拟阵,拟阵交的具体算法实现也都在里面了
#include<cstdio>
#include<cstring>
#define RI register int
#define CI const int&
using namespace std;
const int N=305;
struct edge
{
int to,nxt;
}e[N*N]; int t,n,m,head[N],cnt;
struct Graph_Matroid
{
int x[N],y[N],fa[N];
inline int getfa(CI x)
{
return x!=fa[x]?fa[x]=getfa(fa[x]):x;
}
inline void init(void)
{
for (RI i=1;i<=n;++i) fa[i]=i;
}
inline void Union(CI p)
{
fa[getfa(x[p])]=getfa(y[p]);
}
inline bool identify(CI p)
{
return getfa(x[p])==getfa(y[p]);
}
}A,B; int C[N],q[N],pre[N],tot; bool cs[N],st[N],tar[N],rch[N],vis[N];
inline void addedge(CI x,CI y)
{
e[++cnt]=(edge){y,head[x]}; head[x]=cnt;
}
inline void DEBUG(bool *a)
{
for (RI i=1;i<=m;++i) printf("%d%c",a[i],"
"[i==m]);
}
#define to e[i].to
inline bool BFS(void)
{
RI H=0,T=0,i,now; memset(rch,0,m+1); memset(pre,0,m+1<<2);
for (i=1;i<=m;++i) if (st[i]) q[++T]=i,rch[i]=1;
while (H<T)
{
now=q[++H]; if (tar[now]) break;
for (i=head[now];i;i=e[i].nxt) if (!rch[to])
rch[to]=1,q[++T]=to,pre[to]=now;
}
if (!tar[now]) return 0; for (;now;now=pre[now]) vis[now]=1; return 1;
}
#undef to
inline int Cross(void)
{
for (tot=0,memset(cs,0,m+1);;)
{
RI i,j; for (memset(head,0,m+1<<2),cnt=0,i=1;i<=tot;++i)
{
for (A.init(),B.init(),j=1;j<=tot;++j)
if (i!=j) A.Union(C[j]),B.Union(C[j]);
for (j=1;j<=m;++j) if (!cs[j])
{
if (!A.identify(j)) addedge(C[i],j);
if (!B.identify(j)) addedge(j,C[i]);
}
}
memset(st,0,m+1); memset(tar,0,m+1); memset(vis,0,m+1);
for (A.init(),B.init(),i=1;i<=tot;++i) A.Union(C[i]),B.Union(C[i]);
for (i=1;i<=m;++i) if (!cs[i]) st[i]=!A.identify(i),tar[i]=!B.identify(i);
//DEBUG(st); DEBUG(tar); DEBUG(cs);
if (!BFS()) break; for (tot=0,i=1;i<=m;++i) if (cs[i]^vis[i]) C[++tot]=i;
for (memset(cs,0,m+1),i=1;i<=tot;++i) cs[C[i]]=1;
}
return tot;
}
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
for (scanf("%d",&t);t;--t)
{
RI i; for (scanf("%d%d",&n,&m),i=1;i<=m;++i)
scanf("%d%d",&A.x[i],&A.y[i]); for (i=1;i<=m;++i)
scanf("%d%d",&B.x[i],&B.y[i]); printf("%d
",(n-1<<1)-Cross());
}
return 0;
}
H (Challenge) Maximizing LIS
JB Challenge写了好久的退火TMD没分233
最后还是靠着乱打的点东西拿了(0.004)分的好成绩
Postscript
我实在是太弱了……