国庆模拟赛一总结
看到ATZdhjeb在搞我也来整一下,毕竟模拟赛怎么能不总结呢?
T1
题面
首先直接按题模拟能有50pts,然后我就不会了。
赛后别人都写的是差分,考的时候想了但没写/ll
90pts 按行差分
其实能直接拿100pts的,也许是数据水了。
之前不会的技巧:要想给某一个区间加上一个数,珂以在差分数组的这个区间的开头加上 ,在末尾减去 。
这样子维护每一行,时间复杂度减少至 。
code
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll N=2*114514,M=1919810;
ll n,q,a[2005][2005];
int main(){
//freopen("xor.in","r",stdin);
//freopen("xor.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
cin>>n>>q;
for(int i=1;i<=q;++i){
ll r,c,l,s;
cin>>r>>c>>l>>s;
for(int i=1;i<=l;++i){
a[r+i-1][c]+=s;
if(c+i<=n) a[r+i-1][c+i]-=s;
}
}
ll ans=0;
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
a[i][j]+=a[i][j-1],ans^=a[i][j];
cout<<ans;
return 0;
}
正解 二维差分
这个就要高级一些了,直接把修改的复杂度降到了一次 ,还要融合一些容斥的思想。
矩阵前缀和:
这里的三角形前缀和:
如图所示:容斥原理,总=蓝+绿-红
在统计答案的时候答案就是矩形的前缀和+三角形前缀和。
题解code
#include<cstdio>
#define maxn 3039
using namespace std;
int n, m, r, c, l, s;
long long ans,f[maxn][maxn], g[maxn][maxn];
void add1(int x, int y, int s){if(x<=n&&y<=n)f[x][y] += s;} //三角形
void add2(int x, int y, int s){if(x<=n&&y<=n)g[x][y] += s;} //矩形
int main(){
scanf("%d%d", &n, &m);
while(m--){
scanf("%d%d%d%d", &r, &c, &l, &s);
add1(r, c, s); //顶点加s
add1(r+l, c+l, -s);//右下方减s
add2(r+l, c, -s);//维护需要减去的矩阵,注意是减去
add2(r+l, c+l, s);
}
for(int i = 1; i < n+1; i++)
for(int j = 1; j < n+1; j++){
if(i>1)f[i][j] -= f[i-2][j-1];
f[i][j] += f[i-1][j-1] + f[i-1][j]; //三角形的前缀和
g[i][j] += g[i-1][j] + g[i][j-1] - g[i-1][j-1];//矩阵的前缀和
ans ^= f[i][j]+g[i][j];
}
printf("%lld\n", ans);
return 0;
}
T2
诈骗题,但是当你结论之后发现其实非常好推,就是要是一开始方向错了后面就全错了。
首先不能贪心,也没有dp,更没有SG函数,往这方面想就遭。
我们可以先把每个局面的状态通过一棵满二叉树表示出来,大概就是这样的:
这样子的话我们可以直接暴力按树深搜,然后根据深度的奇偶来取 。
code
ll dfs(ll dept,vector <ll> &w){ //按树深搜
//记录下此层的序列,然后直接暴力判断是不是倍数
if(!w.size()) return 0;
if(dept==m+1){
ll sum=0;
for(ll i:w) sum+=i;
return sum;
}
vector <ll> a1,a2;
for(ll i:w)
if(!(i%b[dept])) a1.push_back(i);
else a2.push_back(i);
ll ls=dfs(dept+1,a1);
ll rs=dfs(dept+1,a2);
if(dept&1) return min(ls,rs);
else return max(ls,rs);
}
然而这道题 高达 , 达 ,搜索搜个屁。但是,我们可以发现 的序列画出来的树也最多只有28层,如果再往下画那么节点就会全是。
那么直接大胆特判:当 时直接输出。
然后就做完了?真的非常神奇,这种思维题真的得好好思考。
code
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll N=114514,M=1919810;
ll n,m,a[N],b[N];
vector <ll> w;
ll dfs(ll dept,vector <ll> &w){ //按树深搜
//记录下此层的序列,然后直接暴力判断是不是倍数
if(!w.size()) return 0;
if(dept==m+1){
ll sum=0;
for(ll i:w) sum+=i;
return sum;
}
vector <ll> a1,a2;
for(ll i:w)
if(!(i%b[dept])) a1.push_back(i);
else a2.push_back(i);
ll ls=dfs(dept+1,a1);
ll rs=dfs(dept+1,a2);
if(dept&1) return min(ls,rs);
else return max(ls,rs);
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;++i) cin>>a[i],w.push_back(a[i]);
for(int i=1;i<=m;++i) cin>>b[i];
if(m>28){
cout<<0; //堪比不可以总司令的特判
return 0; //树的高度顶多为log层,之后的局面都必定为0
}
cout<<dfs(1,w);
return 0;
}
T3
首先16pts的暴力,直接枚举删点的点,删掉之后 看最大连通块大小,总复杂度 ,还有在建图的时候也是直接线性筛一遍然后直接 暴力建图。
代码不放了,人人都会。结果我线性筛只筛到 然后挂八分
然后是正解:
其实求答案的过程很简单,先用并查集找到最大的连通块,再用tarjan求出割点,再分别来看这些割点的答案,非常套路。
但是这个 的建边优化成 就很费解。
题解说的:
对于更大的数据规模,上述方法中建边的效率仍然不足,考虑优化建边。
我们先定义一个数是单位合数,当且仅当它能够表示为 个质数的乘积。
考察联通的性质。如果两个点之间存在一条边,那么可以建一个虚拟节点(这个节点应是两个点的单位合数),向两个点连边,这跟原图在联通性上是等价的。
因此我们定义单位合数为两个质数的乘积,数量为 级别的, 其中 是值域。
若任意两个数的 为合数,那么必定有至少一个单位合数同时是两者的因数,于是它们通过这个单位合数联通。
此时边的数量就是 的了,剩下的按 40% 的做法即可。
评价:不会
题解code
#include<bits/stdc++.h>
using namespace std;
int p1=2000000;
char buf[2000005];
int gc(){
if(p1>=2000000)fread(buf,1,2000000,stdin),p1=0;
return buf[p1++];
}
int rd(){
int x=0;
char ch=gc();
while(ch<'0'||ch>'9')ch=gc();
while(ch<='9'&&ch>='0')x=x*10+ch-'0',ch=gc();
return x;
}
int n,a[100005],mnp[10000005],h[2100005],cnt,ans,fa[2100005],sz[2100005],id[10100005],M;
int dfn[2100005],low[2100005],st[2100005],top,sign,sz2[2100005],ALL;
bitset<10100005> vst;
struct E{int to,nxt;}e[7200005];
void Add(int x,int y){e[++cnt]={y,h[x]},h[x]=cnt;}
int gf(int x){while(fa[x]&&fa[fa[x]])x=fa[x]=fa[fa[x]];if(fa[x])x=fa[x];return x;}
void Merge(int x,int y){
x=gf(x),y=gf(y);
if(x^y)fa[x]=y,sz[y]+=sz[x];
}
void Tarjan(int x,int fa){
dfn[x]=low[x]=++sign,st[++top]=x,sz2[x]=0;
int mx=0,cc=0;
for(int i=h[x];i;i=e[i].nxt){
if(i==(fa^1))continue;
int y=e[i].to;
if(!dfn[y]){
Tarjan(y,i),low[x]=min(low[x],low[y]);
if(low[y]>dfn[x])assert(st[top]==y),top--,mx=max(mx,sz2[y]),sz2[x]+=sz2[y],cc++;
else if(low[y]==dfn[x]){
int t,sum=0;
do t=st[top--],sz2[x]+=sz2[t],sum+=sz2[t]; while(t!=y);
cc++,mx=max(mx,sum);
}
}
else low[x]=min(low[x],dfn[y]);
}
if(x<=n){
sz2[x]++;
if((fa&&cc)||(!fa&&cc>1))ans=min(ans,max(mx,ALL-sz2[x]));
else ans=min(ans,ALL-1);
}
}
void Solve(){
cnt=1,sign=top=0,memset(h,0,sizeof(h)),memset(fa,0,sizeof(fa)),memset(sz,0,sizeof(sz)),memset(dfn,0,sizeof(dfn));
n=rd();
for(int i=1;i<=n;i++){
int p[10]={0},C[10]={0},x;
x=rd(),sz[i]=1;
while(x>1){
int u=mnp[x];
p[++p[0]]=u;
while(x%u==0)x/=u,C[p[0]]++;
}
for(int j=1;j<=p[0];j++)
for(int k=j+(C[j]<=1);k<=p[0];k++)
if(1ll*p[j]*p[k]<=10000000)Add(i,id[p[j]*p[k]]+n),Add(id[p[j]*p[k]]+n,i),Merge(i,id[p[j]*p[k]]+n);
}
int mx=0,se=0,mxp=0;
for(int i=1;i<=M+n;i++){
if(fa[i]||!sz[i])continue;
if(sz[i]>mx)se=mx,mx=sz[i],mxp=i;
else if(sz[i]>se)se=sz[i];
}
ans=mx,ALL=mx,Tarjan(mxp,0),cout<<max(ans,se)<<'\n';
}
int main(){
//freopen("connect.in","r",stdin);
//freopen("connect.out","w",stdout);
for(int i=2;i<=10000000;i++){
if(vst[i])continue;
mnp[i]=i;
if(1ll*i*i>10000000)continue;
int I=i*(1+(i!=2));
for(int j=i*i;j<=10000000;j+=I)vst[j]=1,mnp[j]=(!mnp[j]?i:mnp[j]);
}
for(int i=2;i<=10000000;i++)if(vst[i]&&!vst[i/mnp[i]])id[i]=++M;
int t=rd();
while(t--)Solve();
}
T4
虚树上算贡献?dsu on tree?线段树合并?
谢谢,都不会写,这个题能出现在NOIP模拟赛里面真是太核离了……
代码先放着,之后会了再来看/ng
学长程序
#include<bits/stdc++.h>
#define N 100010
#define M 100010
#define LL long long
#define ULL unsigned long long
#define DB double
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
#define tep(u) for(auto v:e[u])
#define INF 0x3f3f3f3f
#define pir pair<int,int>
#define mp(i,j) make_pair(i,j)
#define fi first
#define se second
using namespace std;
template <typename T> inline void read(T &a)
{
a=0;T w=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){a=(a<<3)+(a<<1)+(ch^48);ch=getchar();}
a*=w;
}
template <typename T,typename ...Args> inline
void read(T &x,Args &...args){read(x);read(args...);}
int n,m,k,dfncc,pcc,c[N],dep[N],siz[N],fa[N],son[N],top[N],dfn[N],nid[N],p[N<<1];
int fat[N],s[N],cnt[N],tp;
LL f[N],g[N],A[N],B[N],h[N],tot;
vector<int> e[N],mem[N];
inline void dfs1(int u)
{
dep[u]=dep[fa[u]]+1;siz[u]=1;
tep(u) if(v^fa[u])
{
fa[v]=u;dfs1(v);siz[u]+=siz[v];
if(siz[v]>siz[son[u]]) son[u]=v;
}
}
inline void dfs2(int u,int tp)
{
top[u]=tp;nid[dfn[u]=++dfncc]=u;
if(son[u]) dfs2(son[u],tp);
tep(u) if(v^fa[u]&&v^son[u]) dfs2(v,v);
}
inline int Lca(int u,int v)
{
while(top[u]^top[v]) dep[top[u]]>dep[top[v]]?u=fa[top[u]]:v=fa[top[v]];
return dep[u]<dep[v]?u:v;
}
inline bool cmp(const int &x,const int &y){return dfn[x]<dfn[y];}
inline void build(int c)//虚空建树,名曰虚树
{
pcc=tp=0;for(int x:mem[c]) p[++pcc]=x;p[++pcc]=1;
sort(p+1,p+1+pcc,cmp);pcc=unique(p+1,p+1+pcc)-p-1;
for(int i=2,lim=pcc;i<=lim;i++) p[++pcc]=Lca(p[i-1],p[i]);
sort(p+1,p+1+pcc,cmp);pcc=unique(p+1,p+1+pcc)-p-1;
rep(i,1,pcc)
{
for(;tp&&(dfn[s[tp]]>dfn[p[i]]||dfn[p[i]]>=dfn[s[tp]]+siz[s[tp]]);tp--);
fat[p[i]]=s[tp];s[++tp]=p[i];
}
}
inline LL C2(int n){return n<=1?0:1ll*n*(n-1)/2;}
#define u p[i]
inline void dfs3(int co)
{
rep(i,1,pcc) cnt[p[i]]=0;
per(i,pcc,1)
{
cnt[u]+=(c[u]==co);
f[u]+=C2(cnt[u]);
g[u]+=1ll*cnt[u]*((int)mem[co].size()-cnt[u]);
if(fat[u])
{
cnt[fat[u]]+=cnt[u];
f[fat[u]]-=C2(cnt[u]);
g[fat[u]]-=1ll*cnt[u]*((int)mem[co].size()-cnt[u]);
}
}
}
inline void dfs4(int co)
{
LL sum=0;
rep(i,1,pcc)
{
f[u]=0;cnt[u]=(c[u]==co);
if(c[u]==co) sum+=A[u];
}
per(i,pcc,1) cnt[fat[p[i]]]+=cnt[p[i]];
rep(i,2,pcc) f[u]=f[fat[u]]+B[fat[u]]*(cnt[fat[u]]-cnt[u]);
rep(i,1,pcc) if(c[u]==co) h[u]=f[u]+A[u]*((int)mem[co].size()-2)+sum+B[u]*(cnt[u]-1);
}
#undef u
inline void prework()
{
per(i,n,2) f[fa[nid[i]]]+=f[nid[i]],g[fa[nid[i]]]+=g[nid[i]];
rep(i,1,n)
{
int u=nid[i];A[u]=A[fa[u]]-f[u];
tep(u) if(v^fa[u]) A[u]+=f[v],B[u]+=f[v];
g[u]=tot-f[u]-g[u];
B[u]+=g[u]-A[u]*2;
}
}
signed main()
{
read(n,m,k);
rep(i,1,n) read(c[i]),mem[c[i]].push_back(i);
rep(i,1,k) tot+=C2(mem[i].size());
rep(i,2,n)
{
int u,v;read(u,v);
e[u].push_back(v);
e[v].push_back(u);
}
dfs1(1);dfs2(1,1);
rep(i,1,k) build(i),dfs3(i);
prework();tot=0;
rep(i,1,k) if(mem[i].size()>1) build(i),dfs4(i);
rep(i,1,n) tot+=h[i];
tot/=4;printf("%lld\n",tot);
while(m--)
{
int u;read(u);
printf("%lld\n",tot-h[u]);
}
return 0;
}