【前言】
比赛的时候I想了一个俩log的方法,结果最后过不了,遂放弃。后来发现其实维护的东西没有那么多…
这场题目有点阴间,感觉剩下的题除了I都不太可做,题解都两三页。
rk47,校3/9,日常被踩
A. Guess and lies
【题意】
Alice想一个 n n n以内的数Bob来猜,每次Bob可以询问是否大于等于某个数,Alice会回答yes或no,但是Alice在一轮里面可以说一次谎。Alice希望猜的次数尽可能多,Bob希望猜的次数尽可能少。同时,Alice第一次一定会回答yes。
现在对于 x ∈ [ 1 , n ] x\in[1,n] x∈[1,n],求出当Bob第一次猜 x x x时,猜几轮Bob能知道这个数。
【思路】
不会。
【参考代码】
#include<bits/stdc++.h>
using namespace std;
const int N=2005;
const int mo=10007;
int f[24][N][N];
int n,L[N];
int main(){
scanf("%d",&n);
// n=2000;
for (int i=1;i<=22;i++) L[i]=min(n,1<<i);
memset(f,-1,sizeof(f));
f[0][0][1]=0;
f[0][0][0]=1;
f[0][1][0]=0;
for (int i=1;i<=22;i++){
for (int j=0;j<=n;j++)
for (int k=0;k+j<=n;k++)
if (f[i-1][j][k]>=0){
int p=min(f[i-1][j][k],n-j-k);
f[i][p][j+k]=max(f[i][p][j+k],f[i-1][k][j]);
}
for (int j=0;j<=n;j++)
for (int k=0;k+j<=n;k++)
if (f[i-1][j][k]>=0) f[i][j][k]=max(f[i][j][k],f[i-1][j][k]+(L[i-1]-k));
for (int j=n;j>=1;j--)
for (int k=0;k+j<=n;k++)
f[i][j-1][k]=max(f[i][j-1][k],f[i][j][k]);
for (int j=0;j<=n;j++)
for (int k=0;k<=n;k++)
if (f[i][j][k]>=0){
int p=min(f[i][j][k],n-k);
f[i][p][k]=max(f[i][p][k],j);
}
for (int j=n;j>=1;j--)
for (int k=0;k+j<=n;k++)
f[i][j-1][k]=max(f[i][j-1][k],f[i][j][k]);
/*for (int j=0;j<=n;j++)
for (int k=0;k+j<=n;k++)
if (f[j][k][i]>=0&&i<=5)
cout<<f[j][k][i]<<' '<<j<<' '<<k<<' '<<i<<endl;*/
}
for (int i=1;i<=n;i++){
int j=22;
for (;j&&f[j-1][i-1][n-i+1]>=0;) --j;
printf("%d ",j);
}
// cerr<<clock()<<endl;
}
B. Black and White
【题意】
给定一个 n × m n\times m n×m的网格,每个位置有一个涂黑的花费,现在要把所有位置涂黑,同时如果任意两行两列的四个交点中有三个已经涂黑,那么剩下的那个可以免费涂,求最小花费。
n ≤ 5000 n\leq 5000 n≤5000
【思路】
对于一个位置 ( i , j ) (i,j) (i,j),如果该格子是黑色,连一条 A i A_i Ai到 B j B_j Bj的边,操作不改变连通性。全部染黑等价于初始联通,那么用Prim求最小生成树就行,复杂度 O ( n 2 ) O(n^2) O(n2)
或者,我们考虑一个暴力的贪心(也是我们比赛时的做法):每次选择花费最小的一个位置涂黑,然后扩展现在所有染黑的位置,这样复杂度是 O ( n 3 ) O(n^3) O(n3)的,瓶颈实际上在扩展的地方。
考虑优化这个过程,可以观察到一个性质:如果某一行的两个位置都已经涂黑,那么这两个位置对应的列的状态一定相同。那么事实上我们只需要用并查集维护这堆状态相同的列,每次并查集合并的时候用bitset或一下就行。
合并次数是 O ( n ) O(n) O(n)的,那么总时间复杂度 O ( n 2 ω + n 2 log n ) O(\frac {n^2}{\omega}+n^2\log n) O(ωn2+n2logn),后面是堆取出每个数的复杂度,其实远远跑不到上界,因为只需要自己涂黑 2 n − 1 2n-1 2n−1个就行。
【参考代码】
/*
* @date:2021-07-24 13:31:52
* @source:
*/
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> pii;
typedef long long ll;
typedef pair<ll, ll> pll;
typedef vector<int> vi;
#define fir first
#define sec second
#define ALL(x) (x).begin(), (x).end()
#define SZ(x) (int)x.size()
#define For(i, x) for (int i = 0; i < (x); ++i)
#define Trav(i, x) for (auto & i : x)
#define pb push_back
template<class T, class G> bool chkMax(T &x, G y) {
return y > x ? x = y, 1 : 0;
}
template<class T, class G> bool chkMin(T &x, G y) {
return y < x ? x = y, 1 : 0;
}
const int MAXN = 5000 + 5;
int N, M;
ll a, b, c, d, p;
int A[MAXN][MAXN];
struct Node {
int val, x, y;
bool operator < (const Node &x) const {
return val > x.val;
}
};
priority_queue<Node> Pq;
bitset<MAXN> C[MAXN];
int Fa[MAXN];
int findFa(int x) {
return Fa[x] == x ? x : Fa[x] = findFa(Fa[x]);
}
int merge(int x, int y) {
x = findFa(x), y = findFa(y);
if (x == y) return x;
Fa[x] = y;
C[y] |= C[x];
return y;
}
int v[MAXN];
int main() {
scanf("%d%d%lld%lld%lld%lld%lld", &N, &M, &a, &b, &c, &d, &p);
for (int i = 1; i <= N; ++i) {
for (int j = 1; j <= M; ++j) {
a = (a * a * b + a * c + d) % p;
A[i][j] = a;
Pq.push({A[i][j], i, j});
}
}
int cnt = 0;
ll val = 0;
for (int i = 1; i <= M; ++i) {
Fa[i] = i;
}
while (!Pq.empty()) {
auto x = Pq.top(); Pq.pop();
int fa = findFa(x.y);
if (C[fa][x.x]) continue;
if (v[x.x]) {
v[x.x] = merge(v[x.x], x.y);
} else v[x.x] = x.y;
C[findFa(x.y)][x.x] = 1;
val += x.val;
if (++cnt == N + M - 1) break;
}
printf("%lld\n", val);
return 0;
}
C. Minimum Grid
【题意】
一个 n × n n\times n n×n的网格,其中 m m m个格子里需要填非负数,现在给出每行每列的最大值,求所有可行方案中,填的数字和最小的任意一种。
n ≤ 2000 , m ≤ 8 × 1 0 5 n\leq 2000,m\leq 8\times 10^5 n≤2000,m≤8×105
【思路】
我们从考虑从大往小填数,不难发现,实际上使答案减小的方式,是在行列最大值相同的地方填上这个最大值,因为这样不仅能使填的数最多,而且能使小的数有更多可行的填放位置(如果没有最大值相同的,则需要在任意一个能放的位置放下)。
那么实际上我们只需要从大到小枚举每个数字,对当前枚举到的数字行列匹配,跑一下二分图匹配,在匹配的位置填上这个数字。其余需要满足最大值的位置再找到可以放的位置即可,具体实现可以见代码。
或者,答案最后的形式实际上是行列限制和减去上面匹配的数,我们可以直接建图跑最大费用流即可,具体来说,还是行列连边,在相同限制的位置连一条流量为 1 1 1,费用为这个限制的边即可。
复杂度 O ( m n ) O(m\sqrt n) O(mn)
【参考代码】
二分图匹配
#include<bits/stdc++.h>
#define pb push_back
#define mkp make_pair
#define fi first
#define se second
using namespace std;
typedef double db;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<int,ll> pil;
const int N=2005,M=1e6+10,inf=0x3f3f3f3f;
int n,m,K,S,T;
int rmx[N],cmx[N],mp[N][N],hv_r[N],hv_c[N];
vector<int>row[M],col[M];
ll ans;
int read()
{
int ret=0,f=1;char c=getchar();
while(!isdigit(c)) {if(c=='-')f=0;c=getchar();}
while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
return f?ret:-ret;
}
int tot,head[N<<1],dis[N<<1];
vector<int>vec;
queue<int>q;
struct Tway
{
int v,w,nex;
}e[M];
//最大流
void add(int u,int v)
{
e[++tot]=(Tway){v,1,head[u]};head[u]=tot;
e[++tot]=(Tway){u,0,head[v]};head[v]=tot;
}
bool bfs()
{
for(auto x:vec) dis[x]=-1;
while(!q.empty()) q.pop();
q.push(S);dis[S]=0;
while(!q.empty())
{
int x=q.front();q.pop();
for(int i=head[x];i;i=e[i].nex)
{
int v=e[i].v;
if(!~dis[v] && e[i].w) dis[v]=dis[x]+1,q.push(v);
}
}
return ~dis[T];
}
int dfs(int x,int flow)
{
if(x==T || !flow) return flow;
int f,used=0;
for(int i=head[x];i;i=e[i].nex)
{
int v=e[i].v;
if(dis[v]!=dis[x]+1 || !(f=dfs(v,min(flow-used,e[i].w)))) continue;
e[i].w-=f;e[i^1].w+=f;used+=f;
if(used==flow) break;
}
return used;
}
void clear_mp(int k)
{
tot=1;
head[S]=head[T]=0;
vec.clear();
for(auto i:row[k]) vec.pb(i);
for(auto i:col[k]) vec.pb(i+n);
vec.pb(S);vec.pb(T);
}
int dinic()
{
int ret=0;
while(bfs()) ret+=dfs(S,inf);
return ret;
}
signed main()
{
n=read();m=read();K=read();
for(int i=1;i<=n;++i)
{
int t=read();
rmx[i]=t;row[t].pb(i);
}
for(int i=1;i<=n;++i)
{
int t=read();
cmx[i]=t;col[t].pb(i);
}
for(int i=1;i<=m;++i)
{
int x=read(),y=read();
mp[x][y]=1;
}
S=0;T=2*n+2;
for(int k=K;k;k--)
{
if(!row[k].size() && !col[k].size()) continue;
if(row[k].size() && col[k].size())
{
clear_mp(k);
for(auto i:row[k]) for(auto j:col[k])
if(mp[i][j]) add(i,j+n);
for(auto i:row[k]) add(S,i);
for(auto j:col[k]) add(j+n,T);
int tmp=dinic();
//printf("%d %d\n",k,tmp);
ans+=1ll*(row[k].size()+col[k].size()-tmp)*k;
for(int i=head[S];i;i=e[i].nex)
{
if(!e[i].w)
{
int x=e[i].v;
for(int j=head[x];j;j=e[j].nex)
{
if(!e[j].w)
{
mp[i][e[j].v-n]=0;
hv_r[x]=hv_c[e[j].v-n]=1;
break;
}
}
}
}
}
else ans+=1ll*(row[k].size()+col[k].size())*k;
for(auto i:row[k])
{
if(hv_r[i]) continue;
for(int j=1;j<=n;++j)
{
if(mp[i][j] && cmx[j]>=k)
{
mp[i][j]=0;
hv_r[i]=1;
break;
}
}
}
for(auto j:col[k])
{
if(hv_c[j]) continue;
for(int i=1;i<=n;++i)
{
if(mp[i][j] && rmx[i]>=k)
{
mp[i][j]=0;
hv_c[j]=1;
break;
}
}
}
}
printf("%lld\n",ans);
return 0;
}
费用流
#include<algorithm>
#include<bitset>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<iostream>
#include<map>
#include<queue>
#include<set>
#include<stack>
#include<string>
#include<vector>
using namespace std;
namespace Solve
{
typedef long long LL;
const int MAXN=4010;
const int MAXM=500005;
const LL INF=1000000005;
inline LL mymin(LL a,LL b){return a<b?a:b;}
inline int oid(int p){return (p&1)?(p+1):(p-1);}
struct edge
{
int to,next,cap,flow;LL cost;
edge(int a=0,int b=0,int c=0,int d=0,LL e=0):
to(a),next(b),cap(c),flow(d),cost(e){}
}E[MAXM<<1];
int first[MAXN],np;
void add_edge(int u,int v,int c,LL w)
{
//printf("%d->%d %d %d\n",u,v,c,w);
E[++np]=edge(v,first[u],c,0,w);
first[u]=np;
E[++np]=edge(u,first[v],0,0,-w);
first[v]=np;
return;
}
int S,T;
int q[MAXN],front,rear;
LL dist[MAXN]; int cur[MAXN];
bool inq[MAXN];
bool SPFA()
{
memset(inq,false,sizeof(inq));
for(int i=0;i<=T;i++) dist[i]=-INF;
front=rear=0;
q[rear++]=S,inq[S]=true,dist[S]=0;
if(rear==MAXN) rear=0;
while(rear-front)
{
int i=q[front++];
if(front==MAXN) front=0;
inq[i]=false;
for(int p=first[i];p;p=E[p].next)
{
int j=E[p].to;
if(E[p].cap>E[p].flow && dist[j]<dist[i]+E[p].cost)
{
dist[j]=dist[i]+E[p].cost;
if(!inq[j])
{
inq[j]=true;
q[rear++]=j;
if(rear==MAXN) rear=0;
}
}
}
}
return dist[T]>-INF;
}
int DFS(int i,int f,LL &mincost)
{
if(i==T || f==0) return f;
int aug=0;inq[i]=true;
for(int p=cur[i];p;p=E[p].next)
{
cur[i]=E[p].next;
int j=E[p].to;
if(inq[j]) continue;
if(E[p].cap>E[p].flow && dist[j]==dist[i]+E[p].cost)
{
int nxaug=DFS(j,mymin(f-aug,E[p].cap-E[p].flow),mincost);
if(!nxaug) continue;
aug+=nxaug;
E[p].flow+=nxaug;
E[oid(p)].flow-=nxaug;
mincost+=nxaug*E[p].cost;
if(aug==f) break;
}
}
inq[i]=false;
if(aug==0) dist[i]=-1;
return aug;
}
LL dinic()
{
int maxflow=0;LL mincost=0;
while(SPFA())
{
for(int i=0;i<=T;i++) cur[i]=first[i];
memset(inq,false,sizeof(inq));
maxflow+=DFS(S,INF,mincost);
}
return mincost;
}
int n,m,k,b[MAXN],c[MAXN];
void work()
{
scanf("%d%d%d",&n,&m,&k);
LL tot = 0;
for(int i=1;i<=n;i++) scanf("%d",&b[i]);
for(int i=1;i<=n;i++) scanf("%d",&c[i]);
for(int i=1;i<=n;i++) tot+=(LL)b[i]+c[i];
np = 0;memset(first,0,sizeof(first));
S = (n<<1)+1, T = (n<<1)+2;
for(int i=1;i<=m;i++)
{
int x,y;scanf("%d%d",&x,&y);
if(b[x] == c[y]) add_edge(x,y+n,1,b[x]);
}
for(int i=1;i<=n;i++) add_edge(S,i,1,0);
for(int i=1;i<=n;i++) add_edge(i+n,T,1,0);
LL temp = dinic();
cout<<tot - temp<<endl;
return;
}
}
int main()
{
#ifdef LOCAL
freopen("00test.in","r",stdin);
freopen("00test.out","w",stdout);
#endif
Solve::work();
return 0;
}
D. Count
【题意】
有一个 n ∗ n n*n n∗n的棋盘, 要求选出 m m m个点的方案数, 使得每一行至少一个, 每一列至少一个, 每一个对角线至少一个,每一个副对角线至少一个, 同时有 K K K个点不能选.
【思路】
由于同时考虑 "必选"的要求和"必不能选"的要求比较困难, 考虑容斥.
答案可以表示成 ∑ i = 0 K ( − 1 ) i ⋅ " 强 行 选 i 个 非 法 点 , 剩 下 的 点 随 意 " 的 答 案 ( 记 为 d p [ S ] ) \sum_{i=0}^K (-1)^i\cdot "强行选i个非法点,剩下的点随意"的答案(记为dp[S]) ∑i=0K(−1)i⋅"强行选i个非法点,剩下的点随意"的答案(记为dp[S])
于是二进制枚举钦定选哪些非法点.
当选择了非法点时, 其对应的行,列,对角线和副对角线(如果有的话)就相当于直接选了, 就解除了行\列的限制.
而对角线的限制比较特殊, 首先, 如果钦定的点里面有在对角线上的, 那相当于对角线的要求已经达成了. 而若没有, 则还需要容斥, 来算出答案.
设
f
0
,
f
1
f0,f1
f0,f1分别表示 “主/副对角线上的点必须不能放的要求”,则
d
p
[
S
]
=
s
o
l
v
e
(
S
,
f
0
=
0
,
f
1
=
0
)
−
s
o
l
v
e
(
S
,
f
0
=
1
,
f
1
=
0
)
−
s
o
l
v
e
(
S
,
f
0
=
0
,
f
1
=
1
)
+
s
o
l
v
e
(
S
,
f
0
=
1
,
f
1
=
1
)
dp[S]=solve(S,f0=0,f1=0)-solve(S,f0=1,f1=0)-solve(S,f0=0,f1=1)+solve(S,f0=1,f1=1)
dp[S]=solve(S,f0=0,f1=0)−solve(S,f0=1,f1=0)−solve(S,f0=0,f1=1)+solve(S,f0=1,f1=1)
若
S
S
S中的点有主对角线上的, 那就去掉第2,4项, 若有副对角线上的, 就去掉第3,4项. 若都有, 就只考虑第一项.
下面考虑如何计算 s o l v e ( s , f 0 , f 1 ) solve(s,f0,f1) solve(s,f0,f1),即: 有的行\列必须选, 有的行可选可不选, 有的对角线必须不选, 的方案数.
首先我们知道, 若有A行可以选, B行不可以选, 同时有X个位置因为对角线而被ban掉, 那么可选的点数为 A B − X AB-X AB−X, 要从中选出m个点,其中有 ∣ s ∣ |s| ∣s∣个是来自非法点, 方案数为 ( A B − X − ∣ s ∣ m − ∣ s ∣ ) \binom{AB-X-\mid s\mid}{m-\mid s\mid} (m−∣s∣AB−X−∣s∣)
然后要求出, 得到A,B,X的方案数, 可以用三维背包的方法, 可以通过枚举i,表示当前执行到了 i , n − i + 1 i,n-i+1 i,n−i+1行,列, 枚举第i行,n-i+1行,i列,n-i+1列选不选, 结合f0,f1的要求来递推, 这个地方也要容斥(不知道为啥).
然后返回 ∑ ( A B − X − ∣ s ∣ m − ∣ s ∣ ) g ( A , B , X ) \sum \binom{AB-X-\mid s\mid}{m-\mid s\mid} g(A,B,X) ∑(m−∣s∣AB−X−∣s∣)g(A,B,X).
【参考代码】
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a),i##ss=(b);i<=i##ss;i++)
#define dwn(i,a,b) for(int i=(a),i##ss=(b);i>=i##ss;i--)
#define deb(x) cerr<<(#x)<<":"<<(x)<<'\n'
#define pb push_back
#define fi first
#define se second
#define hvie '\n'
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
int yh(){
int ret=0;bool f=0;char c=getchar();
while(!isdigit(c)){if(c==EOF)return -1;if(c=='-')f=1;c=getchar();}
while(isdigit(c))ret=(ret<<3)+(ret<<1)+(c^48),c=getchar();
return f?-ret:ret;
}
const int mod=10007;
int g[35][35][70],h[35][35][70];
int n,k,m;
int C[1050][1050];
int bi[1<<12];
int add(int x,int y){return (x+y)%mod;}
void inc(int &x,int y){x=(x+y)%mod;}
int mul(int x,int y){return x*y%mod;}
bool vx[50],vy[50];
int x[105],y[105];
int solve(int f0,int f1,int ban){//ban diag/negdiag , numbers of k chosen
int T=0;
memset(g,0,sizeof(g));
g[0][0][0]=1;
for(int l=1,r=n;l<=r;l++,r--){
memset(h,0,sizeof(h));
// cout<<T<<hv ie;
rep(i,0,T)rep(j,0,T)rep(k,0,(T<<1))if(g[i][j][k]){
if(l<r){
rep(x1,vx[l],1)rep(x2,vx[r],1)
rep(y1,vy[l],1)rep(y2,vy[r],1){
int ni=i+x1+x2,nj=j+y1+y2;
int nk=k+(f0)*((int)(x1&&y1)+(int)(x2&&y2))+
(f1)*((int)(x1&&y2)+(int)(x2&&y1));
if(x1^x2^y1^y2) inc(h[ni][nj][nk],mod-g[i][j][k]);
else inc(h[ni][nj][nk],g[i][j][k]);
}
}
else{
rep(x,vx[l],1)rep(y,vy[l],1){
int ni=i+x,nj=j+y;
int nk=k+ (f0||f1)*(x&&y);
if(x^y) inc(h[ni][nj][nk],mod-g[i][j][k]);
else inc(h[ni][nj][nk],g[i][j][k]);
}
}
}
T+=1+(l<r);
rep(i,0,T)rep(j,0,T)rep(k,0,(T<<1)){
g[i][j][k]=h[i][j][k];
}
}
int ans=0;
rep(i,0,T)rep(j,0,T)rep(k,0,(T<<1)){
if(i*j-k>=ban){
inc(ans,mul(C[i*j-k-ban][m-ban],g[i][j][k]));
}
}
return ans;
}
int F(int S){
memset(vx,0,sizeof(vx));
memset(vy,0,sizeof(vy));
bool f0(0),f1(0);
rep(i,0,k-1)if(S&(1<<i)){
vx[x[i]]=1;
vy[y[i]]=1;
f0|=(x[i]==y[i]);
f1|=(x[i]+y[i]==n+1);
}
int ans=solve(0,0,bi[S]);
if(!f0) inc(ans,mod-solve(1,0,bi[S]));
if(!f1) inc(ans,mod-solve(0,1,bi[S]));
if(!f0&&!f1) inc(ans,solve(1,1,bi[S]));
return (ans%mod+mod)%mod;
}
int main(){
n=yh(),k=yh(),m=yh();
rep(i,0,1024){
C[i][0]=1;
rep(j,1,i) C[i][j]=add(C[i-1][j],C[i-1][j-1]);
bi[i]=bi[i>>1]+(i&1);
}
rep(i,0,k-1) x[i]=yh(),y[i]=yh();
int ans=0;
rep(i,0,(1<<k)-1){
//m-=bi[i];
if(bi[i]&1) ans=(ans-F(i)+mod)%mod;
else ans=(ans+F(i))%mod;
//m+=bi[i];
}
cout<<ans<<hvie;
return 0;
}
E. Math
【题意】
给定 n n n,问有多少对 ( x , y ) (x,y) (x,y),满足 x y + 1 ∣ x 2 + y 2 , 1 ≤ x ≤ y ≤ n xy+1|x^2+y^2,1\leq x\leq y\leq n xy+1∣x2+y2,1≤x≤y≤n
n ≤ 1 0 18 n\leq 10^{18} n≤1018, 1 0 5 10^5 105组数据
【思路】
考虑 x 2 + y 2 = k ( x y + 1 ) x^2+y^2=k(xy+1) x2+y2=k(xy+1)的解 ( x , y ) (x,y) (x,y),由韦达定理可知, ( x , k x − y ) (x,kx-y) (x,kx−y)也是一组解,且 x ( k x − y ) = ( y 2 − 1 ) x(kx-y)=(y^2-1) x(kx−y)=(y2−1)
由于 k x − y kx-y kx−y的绝对值更小且依然非负(代入可知),那么对于该 k k k,绝对值最小的一组正整数解满足 k x − y = 0 kx-y=0 kx−y=0,这组解有 ( a , a 3 ) (a,a^3) (a,a3)的形式,然后其余解可以通过 ( x , y ) → ( x , k x − y ) (x,y)\rightarrow(x,kx-y) (x,y)→(x,kx−y)的递降得到,于是也可以反推回去。
然后又发现取 k = a 2 k=a^2 k=a2可以覆盖所有情况,比如 ( a 3 , a 5 − a ) (a^3,a^5-a) (a3,a5−a)也是一组解,然后 ( a 5 − a , a 7 − 2 a 3 ) (a^5-a,a^7-2a^3) (a5−a,a7−2a3)也是一组解。
然后处理出大概 50 50 50个多项式,询问的时候二分一下就行了。
复杂度 O ( n 1 3 log 2 n + T log 2 n ) O(n^{\frac 1 3}\log ^2n+T\log^2 n) O(n31log2n+Tlog2n)
题解没有处理多项式,而是直接根据反推预处理了答案,复杂度可以降到 O ( n 1 3 + T log n ) O(n^{\frac 1 3}+T\log n) O(n31+Tlogn)
事实上,这个东西是椭圆曲线。
【参考代码】
赛时代码
#include<bits/stdc++.h>
#define int long long
#define rep(i,a,b) for(int i=(a),i##ss=(b);i<=i##ss;i++)
#define dwn(i,a,b) for(int i=(a),i##ss=(b);i>=i##ss;i--)
#define deb(x) cerr<<(#x)<<":"<<(x)<<'\n'
#define pb push_back
#define fi first
#define se second
#define hvie '\n'
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
int yh(){
int ret=0;bool f=0;char c=getchar();
while(!isdigit(c)){if(c==EOF)return -1;if(c=='-')f=1;c=getchar();}
while(isdigit(c))ret=(ret<<3)+(ret<<1)+(c^48),c=getchar();
return f?-ret:ret;
}
const int maxn=3e5+5;
ll ksm(ll x,int p){
ll ans=1;
while(p){
if(p&1) ans=ans*x;
}
return ans;
}
struct poly{
int a[100];
poly(){memset(a,0,sizeof(a));}
void init(int x=0,int y=0,int z=0,int w=0){
a[0]=x;a[1]=y;a[2]=z;a[3]=w;
}
void print(){
rep(i,0,15) cout<<a[i]<<" ";
puts("");
}
__int128 eval(ll x){
__int128 now=1,ret=0;
rep(i,0,69){
ret+=now*a[i];
now=now*x;
}
return ret;
}
};
poly operator<<(poly v,int b){
poly u;
rep(i,0,69-b) u.a[i+b]=v.a[i];
return u;
}
poly operator+(poly a,poly b){
poly c;
rep(i,0,69) c.a[i]=a.a[i]+b.a[i];
return c;
}
poly operator-(poly a,poly b){
poly c;
rep(i,0,69) c.a[i]=a.a[i]-b.a[i];
return c;
}
poly a[100];
vector<ll>s[65];
// ll s1[1000006],s2[1000006],s3[1000006];
int t1,t2,t3;
signed main(){
a[1].init(0,1);
int top=0;
rep(i,2,50){
a[i]=(a[i-1]<<2)-a[i-2];
if(a[i].eval(2)>1e18){ top=i-1;break;}
}
rep(j,2,top){
rep(i,1,1e6){
__int128 x=a[j].eval(i);
if(x<=1e18) s[j].pb(x);
else break;
}
}
dwn(_,yh(),1){
int n=yh();
int ans=1;
rep(i,2,top){
ans+=upper_bound(s[i].begin(),s[i].end(),n)-s[i].begin()-1;
// cout<<i<<" "<<upper_bound(s[i].begin(),s[i].end(),n)-s[i].begin()-1<<hvie;
}
cout<<ans<<hvie;
}
return 0;
}
题解写法
#include<bits/stdc++.h>
using namespace std;
#define int long long
int read(){
int x=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-') f=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
x=x*10+c-'0';
c=getchar();
}
return x*f;
}
//map<pair<int,int>,int> allse; //对于去重可以利用map,也可以利用vector自带的方式去重。 如果去重后想要排序,就利用vector进行记录,利用map来 ,还有set
const int beishu=1e12;
vector<pair<int,int> > q;
int cnt;
bool cmp(pair<int ,int> A,pair<int,int> B){
if(A.first<B.first||A.first==B.first&&A.second<B.second) return 1;
return 0;
}
map<pair<int,int>,int> mp;
void get_allse(){
for(int i=1;i*i<=beishu;i++){
if(i==1){
__int128 x=i,y=i*i*i;
pair<int,int> a=make_pair(y,x);
q.push_back(a);
cnt++;
}
else{
for(__int128 x=i,y=i*i*i;y<=1e18;){
pair<int,int> a=make_pair(y,x);
q.push_back(a);
cnt++;
int temp=x;
x=y;
y=i*i*y-temp;
}
}
}
sort(q.begin(),q.end(),cmp); //如果用vector的话 我们无法找到对应的结果。 其实也可以、
q.erase(unique(q.begin(), q.end()), q.end());
/*for(int i=0;i<=cnt-1;i++){
mp[q[i]]=i;
} */
}
int find_answer(int a){
int l=0,r=q.size()-1;
while(l<r){
int mid=(l+r+1)/2;
if(q[mid].first>a) {
r=mid-1;
}
else{
l=mid;
}
}
return l;
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0),cin.tie(0);
get_allse();
int T;
cin>>T;
for(int i=1;i<=T;i++){
int a;
cin>>a;
cout<<find_answer(a)+1<<endl;
}
//for(int i=0;i<=cnt-1;i++){
// cout<<q[i].first<<" "<<q[i].second<<endl;}
// int T;
// T=read();
/*
for(int i=1;i<=n;i++){
int a;
a=read();
} */
}
F. 24dian
【题意】
输入 n n n和 m m m,给出所有使用 n n n个 1 ∼ 13 1\sim 13 1∼13数字加减乘除得到 m m m的方案
n ≤ 4 , m ≤ 1 0 9 n\leq 4,m\leq 10^9 n≤4,m≤109
【思路】
按照题意模拟即可,可以暴力枚举符号和数字。
【参考代码】
/*
* @date:2021-07-24 12:22:37
* @source:
*/
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> pii;
typedef long long ll;
typedef pair<ll, ll> pll;
typedef vector<int> vi;
#define fir first
#define sec second
#define ALL(x) (x).begin(), (x).end()
#define SZ(x) (int)x.size()
#define For(i, x) for (int i = 0; i < (x); ++i)
#define Trav(i, x) for (auto & i : x)
#define pb push_back
template<class T, class G> bool chkMax(T &x, G y) {
return y > x ? x = y, 1 : 0;
}
template<class T, class G> bool chkMin(T &x, G y) {
return y < x ? x = y, 1 : 0;
}
int N, M;
struct Pair {
int a, b, c, d;
};
vector<Pair> v;
int opt[5], B[5];
double A[5];
char op[] = "+-*/";
bool f1, f2;
double calculate(double x, double y, int op) {
double s;
switch (op) {
case 0:
s = x + y;
break;
case 1:
s = x - y;
break;
case 2:
s = x * y;
break;
case 3:
s = x / y;
break;
}
return s;
}
//对应公式1 ((A#B)#C)#D
void cal1() {
double t1 = calculate(A[0], A[1], opt[0]);
if (opt[0] == 3 && fabs(A[1]) < 1e-8) return ;
double t2 = calculate(t1, A[2], opt[1]);
if (opt[1] == 3 && fabs(A[2]) < 1e-8) return ;
double t3 = calculate(t2, A[3], opt[2]);
if (opt[2] == 3 && fabs(A[3]) < 1e-8) return ;
if(fabs(t3 - M) > 1e-8) return ;
f1 = 1;
if (opt[0] != 3 && opt[1] != 3) f2 = 0;
if (opt[0] == 3) f2 &= fabs(A[0] / A[1] - floor(A[0] / A[1])) > 1e-8;
if (opt[1] == 3) f2 &= fabs(t1 / A[2] - floor(t1 / A[2])) > 1e-8;
}
//对应公式2 (A#(B#C))#D
void cal2() {
double t1 = calculate(A[1], A[2], opt[1]);
if (opt[1] == 3 && fabs(A[2]) < 1e-8) return ;
double t2 = calculate(A[0], t1, opt[0]);
if (opt[0] == 3 && fabs(t1) < 1e-8) return ;
double t3 = calculate(t2, A[3], opt[2]);
if (opt[2] == 3 && fabs(A[3]) < 1e-8) return ;
if(fabs(t3 - M) > 1e-8) return ;
f1 = 1;
if (opt[1] != 3 && opt[0] != 3) f2 = 0;
if (opt[1] == 3) f2 &= fabs(A[1] / A[2] - floor(A[1] / A[2])) > 1e-8;
if (opt[0] == 3) f2 &= fabs(A[0] / t1 - floor(A[0] / t1)) > 1e-8;
}
//对应公式3 A#(B#(C#D))
void cal3() {
double t1 = calculate(A[2], A[3], opt[2]);
if (opt[2] == 3 && fabs(A[3]) < 1e-8) return ;
double t2 = calculate(A[1], t1, opt[1]);
if (opt[1] == 3 && fabs(t1) < 1e-8) return ;
double t3 = calculate(A[0], t2, opt[0]);
if (opt[0] == 3 && fabs(t2) < 1e-8) return ;
if(fabs(t3 - M) > 1e-8) return ;
f1 = 1;
if (opt[2] != 3 && opt[1] != 3) f2 = 0;
if (opt[2] == 3) f2 &= fabs(A[2] / A[3] - floor(A[2] / A[3])) > 1e-8;
if (opt[1] == 3) f2 &= fabs(A[1] / t1 - floor(A[1] / t1)) > 1e-8;
}
//对应公式4 A#((B#C)#D)
void cal4() {
double t1 = calculate(A[1], A[2], opt[1]);
if (opt[1] == 3 && fabs(A[2]) < 1e-8) return ;
double t2 = calculate(t1, A[3], opt[2]);
if (opt[2] == 3 && fabs(A[3]) < 1e-8) return ;
double t3 = calculate(A[0], t2, opt[0]);
if (opt[0] == 3 && fabs(t2) < 1e-8) return ;
if(fabs(t3 - M) > 1e-8) return ;
f1 = 1;
if (opt[1] != 3 && opt[2] != 3) f2 = 0;
if (opt[1] == 3) f2 &= fabs(A[1] / A[2] - floor(A[1] / A[2])) > 1e-8;
if (opt[2] == 3) f2 &= fabs(t1 / A[3] - floor(t1 / A[3])) > 1e-8;
}
//对应公式5 (A#B)#(C#D)
void cal5() {
double t1 = calculate(A[0], A[1], opt[0]);
if (opt[0] == 3 && fabs(A[1]) < 1e-8) return ;
double t2 = calculate(A[2], A[3], opt[2]);
if (opt[2] == 3 && fabs(A[3]) < 1e-8) return ;
double t3 = calculate(t1, t2, opt[1]);
if (opt[1] == 3 && fabs(t2) < 1e-8) return ;
if(fabs(t3 - M) > 1e-8) return ;
f1 = 1;
if (opt[0] != 3 && opt[2] != 3) f2 = 0;
if (opt[0] == 3) f2 &= fabs(A[0] / A[1] - floor(A[0] / A[1])) > 1e-8;
if (opt[2] == 3) f2 &= fabs(A[2] / A[3] - floor(A[2] / A[3])) > 1e-8;
}
void dfs(int x) {
if (f1 && !f2) return ;
if (x == 3) {
cal1(); cal2(); cal3(); cal4(); cal5();
return ;
}
for (int i = 0; i < 4; ++i) {
opt[x] = i;
dfs(x + 1);
}
return ;
}
bool solve() {
for (int i = 0; i < 4; ++i) A[i] = B[i];
f1 = 0, f2 = 1;
do {
dfs(0);
} while (next_permutation(A, A + 4));
return f1 && f2;
}
void dfs1(int x) {
if (x == N) {
if (solve()) v.pb({B[0], B[1], B[2], B[3]});
return ;
}
for (int i = !x ? 1 : B[x - 1]; i <= 13; ++i) {
B[x] = i;
dfs1(x + 1);
}
}
int main() {
scanf("%d%d", &N, &M);
dfs1(0);
printf("%d\n", SZ(v));
Trav(x, v) {
printf("%d %d %d %d\n", x.a, x.b, x.c, x.d);
}
return 0;
}
G. Yu Ling(Ling YueZheng) and Colorful Tree
【题目】
【思路】
有点暴力:
观察到0操作影响的是所有的儿子,而1操作实际上可以转化为所有影响到这个点的最深的点,于是我们先处理出一个dfs序。
那么对0操作,直接枚举约数,修改它子树中所有约数的答案。
对1操作,我们就是查找一个点的答案。
上面的东西可以用树套树维护。
可以先预处理出一个数所有的约数,这样预处理的复杂度是 O ( n log n ) O(n\log n) O(nlogn)(枚举倍数)。单次修改复杂度是 O ( log 3 n ) O(\log^3 n) O(log3n),询问的复杂度是 O ( log 2 n ) O(\log^2n) O(log2n)
复杂度 O ( n log n + q log 3 n ) O(n\log n+q\log^3n) O(nlogn+qlog3n),能过。
上面这个太暴力了,题解一定是更暴力的做法的优化:
【参考代码】
树套树
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a),i##ss=(b);i<=i##ss;i++)
#define dwn(i,a,b) for(int i=(a),i##ss=(b);i>=i##ss;i--)
#define deb(x) cerr<<(#x)<<":"<<(x)<<'\n'
#define pb push_back
#define fi first
#define se second
#define hvie '\n'
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
int yh(){
int ret=0;bool f=0;char c=getchar();
while(!isdigit(c)){if(c==EOF)return -1;if(c=='-')f=1;c=getchar();}
while(isdigit(c))ret=(ret<<3)+(ret<<1)+(c^48),c=getchar();
return f?-ret:ret;
}
const int maxn=150005;
vector<int>P[maxn];
vector<pii>adj[maxn];
ll dep[maxn];
int root[maxn<<2],ls[maxn*262],rs[maxn*262];
ll mx[(maxn<<3)*33];
int cnt=0;
int n,q;
#define mid ((l+r)>>1)
ll ask(int x,int l,int r,int al,int ar){
if(x==0) return 0;
if(al<=l&&ar>=r) return mx[x];
ll ma=0;
if(al<=mid) ma=max(ask(ls[x],l,mid,al,ar),ma);
if(ar>mid) ma=max(ask(rs[x],mid+1,r,al,ar),ma);
return ma;
}
void chg(int &x,int l,int r,int p,ll val){
// cout<<x<<" "<<l<<" "<<r<<" "<<p<<" "<<val<<"*"<<hvie;
if(!x){
x=++cnt;
mx[x]=ls[x]=rs[x]=0;
}
mx[x]=max(mx[x],val);
if(l==r)return;
(p<=mid)?chg(ls[x],l,mid,p,val):chg(rs[x],mid+1,r,p,val);
}
vector<int>vis;
void add(int v,int l,int r,int al,int ar,int x,ll val){
// cout<<v<<" "<<x<<" "<<al<<" "<<ar<<" "<<val<<hvie;
if(al<=l&&ar>=r){
vis.pb(v);
chg(root[v],1,n,x,val);
return;
}
if(al<=mid) add(v<<1,l,mid,al,ar,x,val);
if(ar>mid) add(v<<1|1,mid+1,r,al,ar,x,val);
}
ll query(int v,int l,int r,int p,int al,int ar){
if(l==r) return ask(root[v],1,n,al,ar);
return max(ask(root[v],1,n,al,ar),p<=mid?query(v<<1,l,mid,p,al,ar):query(v<<1|1,mid+1,r,p,al,ar));
}
int L[maxn],R[maxn],dfntot=0;
void dfs(int x,int fa){
L[x]=++dfntot;
for(auto e:adj[x]){
if(e.fi!=fa){
dep[e.fi]=dep[x]+e.se;
dfs(e.fi,x);
}
}
R[x]=dfntot;
}
struct node{
int u,l,r,x,id;
};
vector<node>Q[maxn];
ll ans[maxn],sum[maxn];int totques=0;
int main(){
// freopen("my.in","r",stdin);
n=yh(),q=yh();
rep(i,1,n){
for(int j=i;j<=n;j+=i) P[j].pb(i);
}
rep(i,2,n){
int x=yh(),y=yh(),w=yh();adj[x].pb({y,w});adj[y].pb({x,w});
}
int op,u,l,r,x;
dep[1]=1;
dfs(1,0);
rep(_,1,q){
op=yh();u=yh();l=yh();
if(op==0){
for(int k:P[l]){
Q[k].pb({u,0,0,l,0});
}
}
else{
r=yh();x=yh();
Q[x].pb({u,l,r,x,++totques});
}
}
rep(i,1,n){
cnt=0;
vis.clear();
for(auto q:Q[i]){
// cout<<i<<" : "<<q.u<<" "<<q.l<<" "<<q.r<<" "<<q.x<<" "<<q.id<<hvie;
if(q.id==0){
int u=q.u, x=q.x;
// cout<<u<<" "<<x<<endl;
add(1,1,n,L[u],R[u],x,dep[u]);
}
else{
int u=q.u,l=q.l,r=q.r;
sum[q.id]=dep[u];
ans[q.id]=query(1,1,n,L[u],l,r);
}
}
for(int u:vis) root[u]=0;
}
rep(i,1,totques){
if(!ans[i]) puts("Impossible!");
else{
cout<<sum[i]-ans[i]<<hvie;
}
}
return 0;
}
std
#include<bits/stdc++.h>
using namespace std;
const int N=110005;
int n,Q,dfn[N],ed[N];
vector<pair<int,int> > e[N];
int fa[N][20],dep[N];
long long dis[N];
void dfs(int x){
dfn[x]=++*dfn;
for (auto i:e[x])
if (i.first!=fa[x][0]){
fa[i.first][0]=x;
dep[i.first]=dep[x]+1;
dis[i.first]=dis[x]+i.second;
dfs(i.first);
}
ed[x]=*dfn;
}
vector<int> di[N];
void init(){
for (int i=1;i<N;i++)
for (int j=i;j<N;j+=i)
di[j].push_back(i);
}
struct node{
int x,l,r,id;
};
vector<node> op[N];
long long ans[N];
const int M=20000005;
int ls[M],rs[M],S[M],nd;
int rt[N];
void change(int &k,int l,int r,int x,int v){
if (!k) k=++nd; S[k]+=v;
if (l==r) return;
int mid=(l+r)/2;
if (x<=mid) change(ls[k],l,mid,x,v);
else change(rs[k],mid+1,r,x,v);
}
int ask(int k,int l,int r,int x,int y){
if ((x<=l&&r<=y)||!k) return S[k];
int mid=(l+r)/2;
if (y<=mid) return ask(ls[k],l,mid,x,y);
if (x>mid) return ask(rs[k],mid+1,r,x,y);
return ask(ls[k],l,mid,x,mid)+ask(rs[k],mid+1,r,mid+1,y);
}
void erase(int x,int y){
for (;x<=n;x+=x&(-x)) rt[x]=0;
}
void change(int x,int y,int v){
//cout<<"C"<<x<<' '<<y<<' '<<v<<endl;
for (;x<=n;x+=x&(-x)) change(rt[x],1,n,y,v);
}
int query(int x,int l,int r){
//cout<<"Q"<<x<<' '<<l<<' '<<r<<endl;
int s=0;
for (;x;x-=x&(-x)) s+=ask(rt[x],1,n,l,r);
return s;
}
void solve(int x){
for (auto i:op[x])
if (!i.id){
change(dfn[i.x],i.l,1);
change(ed[i.x]+1,i.l,-1);
}
else{
int p=i.x;
int val=query(dfn[p],i.l,i.r);
//cout<<val<<endl;
if (!val){
ans[i.id]=-1;
continue;
}
for (int j=16;j>=0;j--)
if (fa[p][j])
if (query(dfn[fa[p][j]],i.l,i.r)==val)
p=fa[p][j];
ans[i.id]=dis[i.x]-dis[p];
//cout<<i.x<<' '<<p<<' '<<dis[i.x]<<' '<<dis[p]<<endl;
}
for (;nd;--nd) ls[nd]=rs[nd]=S[nd]=0;
for (auto i:op[x])
if (!i.id){
erase(dfn[i.x],i.l);
erase(ed[i.x]+1,i.l);
}
}
int main(){
init();
scanf("%d%d",&n,&Q);
for (int i=1;i<n;i++){
int x,y,v;
scanf("%d%d%d",&x,&y,&v);
e[x].push_back(pair<int,int>(y,v));
e[y].push_back(pair<int,int>(x,v));
}
dfs(1);
for (int i=1;i<=18;i++)
for (int j=1;j<=n;j++)
fa[j][i]=fa[fa[j][i-1]][i-1];
for (int i=1;i<=Q;i++){
int tp,a,b,c,d;
scanf("%d%d%d",&tp,&a,&b);
if (tp==0){
for (auto j:di[b])
op[j].push_back((node){a,b,0,0});
ans[i]=(-(1ll<<62));
}
else{
scanf("%d%d",&c,&d);
op[d].push_back((node){a,b,c,i});
}
}
for (int i=1;i<=n;i++)
solve(i);
for (int i=1;i<=Q;i++)
if (ans[i]!=-(1ll<<62))
if (ans[i]<0) puts("Impossible!");
else printf("%lld\n",ans[i]);
}
H. Ling Qiu, Luna and Triple Backpack
【题目】
【思路】
题解有三页字很小的PDF呢!
【参考代码】
#include<bits/stdc++.h>
using namespace std;
long long A,D;
int n,a[310],b[310],c[310],d[310],id[310],sum,sum3,sum33,bas,s[310];
unsigned long long f[310][1693][29];
short g[310][51][5010],h[310][5010];
bool cmp(int x,int y) {return b[x]<b[y];}
int main()
{
scanf("%d",&n),A=D=0,sum=0;
for (int i=1; i<=n; i++)
{
scanf("%d%d%d%d",&a[i],&b[i],&c[i],&d[i]);
A=max(1ll*a[i],A),D+=d[i],id[i]=i,sum+=c[i];
}
sum3=sum/3+24,sum33=sum3/64,bas=sum/3-24;
sort(id+1,id+1+n,cmp);
f[0][0][0]=1,s[0]=0;
long long ans=1000000000000000000ll;
for (int i=1; i<n; i++)
{
int C=c[id[i]];
s[i]=s[i-1]+C;
//printf("%d %d\n",id[i],g[1][);
for (int j=0; j<=sum3-C; j++)
for (int k=0; k<=sum33; k++) f[i][j+C][k]|=f[i-1][j][k];
for (int j=0; j<=sum3; j++)
for (int k=0; k<=sum33; k++)
{
f[i][j][k]|=f[i-1][j][k];
f[i][j][k]|=(f[i-1][j][k]&((1ull<<(64-C))-1))<<C;
f[i][j][k+1]|=(f[i-1][j][k]>>(64-C));
}
for (int j=max(bas-C,0); j<=sum3-C; j++)
for (int k=0; k<=sum3; k++)
if (1&(f[i-1][j][k>>6]>>(k&63)))
g[i][j+C-bas][k]=max(g[i][j+C-bas][k],(short)(n+1-i));
for (int j=0; j<=48; j++)
{
for (int k=0; k<=sum3; k++)
g[i][j][k]=max(g[i][j][k],g[i-1][j][k]);
for (int k=C; k<=sum3; k++)
{
g[i][j][k]=max(g[i][j][k],g[i-1][j][k-C]);
if (g[i-1][j][k-C])
{
ans=min(ans,(1ll*b[id[i]]+b[id[n+1-g[i-1][j][k-C]]]+b[id[n]])*(max(bas+j,max(k,sum-k-bas-j))));
//if ((1ll*b[i]+b[n+1-g[i-1][j][k-C]]+b[n])*(max(bas+j,max(k,sum-k-bas-j)))==264) printf("%d %d %d %d %d\n",i,j,k,C,g[i-1][j][k-C]);
}
}
}
}
for (int i=n-1; i>1; i--)
{
int C=c[id[i]];
h[i][C]=n+1-i;
for (int j=0; j<=sum; j++) h[i][j]=max(h[i+1][j],h[i][j]);
for (int j=0; j<=sum-C; j++) h[i][j+C]=max(h[i+1][j],h[i][j+C]);
for (int j=0; j<=sum; j++) if (h[i][j])
{
ans=min(ans,(1ll*b[id[n]]+b[id[n+1-h[i][j]]]+b[id[i-1]])*(max(j,max(sum-s[i-1]-j,s[i-1]))));
}
}
//#define __u128int_t long long
__uint128_t Ans=((__uint128_t)A)*((__uint128_t)ans)*((__uint128_t)D);
//cout<<ans<<endl;
int o[110],oN=0;
while (Ans) o[++oN]=Ans%10,Ans/=10;
for (int i=oN; i; i--) putchar(o[i]+'0');
putchar('\n');
return 0;
}
I. Kuriyama Mirai and Exclusive Or
【题意】
给定一个长度为 n n n的序列 a i a_i ai,有两种操作:
- 给 [ l , r ] [l,r] [l,r]区间每个数异或上 x x x
- 给 ∀ i ∈ [ l , r ] \forall i\in[l,r] ∀i∈[l,r],给 a i a_i ai异或上 ( x + ( i − l ) ) (x+(i-l)) (x+(i−l))
最后求每个位置的值。
n ≤ 6 × 1 0 5 , q ≤ 4 × 1 0 5 , a < 2 30 n\leq 6\times 10^5,q\leq 4\times 10^5,a<2^{30} n≤6×105,q≤4×105,a<230
【思路】
先说说我的两个log做法,然而属实卡不过去。
这题很关键的一个点是最后求权值,就在告诉我们可以离线搞。
首先这种求权值的东西一般都是拆开按位考虑的。第一种操作可以通过简单的差分来做,关键是第二种操作。
我们考虑 x = 0 x=0 x=0时,这个贡献的形式。
对于第 0 0 0位,可以发现从 l l l开始的贡献是01010101;
对于第 1 1 1位,可以发现从 l l l开始的贡献是00110011;
对于第 2 2 2位,可以发现从 l l l开始的贡献是00001111;
这个贡献的规律是由于进位导致的,而实际上加上 x x x后,贡献的形式不变,只是起始位置变了,然后有一个作用的区间。
然后,对于第 i i i位二进制,我们如果把贡献的起始点全部移动到1,实际上可以看成有 2 i + 1 2^{i+1} 2i+1种标记(就是上面的贡献位移),而不难看出,实际上对于这些标记,有贡献的是对应回区间以后1的位置,这些位置组成了一个或两个连续的区间。于是我们可以用树状数组来维护标记个数和,这样最后也是区间求值就行了。
需要注意的是高位二进制的标记类型很多,但实际上只有至多 2 q 2q 2q种标记,那么离散化以后就一样了。
复杂度 O ( q log q log a ) O(q\log q\log a) O(qlogqloga)
这个做法并不能通过。
实际上,我们通过前面的做法可以知道,我们只关心标记中有贡献的1的位置,又可以发现所有标记异或起来的结果,最多只有4个突变的位置,那么我们只需要维护这些突变的位置就可以了。
复杂度 O ( n log a ) O(n\log a) O(nloga)
std给出的做法也是基于这个贡献考虑的,不过它通过二维差分和二维后缀和统计答案。
这题因为std写法特殊,还有点卡空间- -
【参考代码】
2log
#include<bits/stdc++.h>
#define pb push_back
#define mkp make_pair
#define fi first
#define se second
#define rg register
using namespace std;
typedef double db;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<int,ll> pil;
const int N=6e5+10,M=31;
vector<int>vec,q[N];
int n,Q;
int ss[N],fc[M],ans[N],a[N];
int read()
{
int ret=0,f=1;char c=getchar();
while(!isdigit(c)) {if(c=='-')f=0;c=getchar();}
while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
return f?ret:-ret;
}
struct BIT
{
#define lowbit(x) ((x)&(-x))
int c[N],MX;
void clear(int x){for(int i=0;i<=MX;++i)c[i]=0;MX=x;}
void insert(int x)
{
x=lower_bound(vec.begin(),vec.end(),x)-vec.begin()+1;
//printf("insert:%d\n",x);
for(;x<MX;x+=lowbit(x)) c[x]^=1;
}
int query(int x)
{
int ret=0;
for(;x>0;x-=lowbit(x)) ret^=c[x];
return ret;
}
int nowquery(int l,int r)
{
int lp=lower_bound(vec.begin(),vec.end(),l)-vec.begin()+1;
int rp=upper_bound(vec.begin(),vec.end(),r)-vec.begin();
//printf("nowquery:%d [%d-1,%d] query[l-1]:%d query[r]:%d\n",p,l,r,query(lp-1),query(rp));
return query(rp)^query(lp-1);
}
int Query(int l,int r,int add)
{
if(l<0) return nowquery(0,r)^nowquery(l+add,add-1);
else return nowquery(l,r);
}
}bit;
void write(int x)
{
if(x>9) write(x/10);
putchar(x%10^48);
}
void writesp(int x)
{
write(x);putchar(' ');
}
int main()
{
freopen("i.in","r",stdin);
freopen("i.out","w",stdout);
for(int i=0;i<M;++i) fc[i]=1<<i;
n=read();Q=read();
for(int i=1;i<=n;++i) a[i]=read();
while(Q--)
{
int op=read(),l=read(),r=read(),x=read();
if(!op)
{
ss[l]^=x;
ss[r+1]^=x;
}
else
{
q[l].pb(x);
q[r+1].pb(x+(r-l+1));
}
}
for(rg int i=1;i<=n;++i) ss[i]^=ss[i-1];
for(rg int p=0;p<M-1;++p)
{
//cerr<<p<<" "<<clock()<<endl;
vec.clear();
for(rg int i=1;i<=n;++i)
{
for(auto x:q[i])
{
int typ=((-((i-x)-1))%fc[p+1]+fc[p+1])%fc[p+1];
vec.pb(typ);
}
}
vec.pb(fc[p+1]);
sort(vec.begin(),vec.end());
vec.erase(unique(vec.begin(),vec.end()),vec.end());
//cerr<<p<<" "<<clock()<<endl;
bit.clear(vec.size()+1);
//printf("size:%d\n",bit.MX);
for(rg int i=1;i<=n;++i)
{
for(auto x:q[i])
{
int typ=((-((i-x)-1))%fc[p+1]+fc[p+1])%fc[p+1];
bit.insert(typ);
// printf("insert_p: %d, %d,%d\n",p,i,typ);
}
int t=(i-1)%fc[p+1];
//printf("for [%d,%d] query:%d %d\n",i,p,fc[p]-t,fc[p+1]-t-1);
int ans1=bit.Query(fc[p]-t,fc[p+1]-t-1,fc[p+1]);
ans[i]^=ans1*fc[p];
}
}
/*for(int i=1;i<=n;++i)
{
//ans[i]^=ans[i-1];
printf("%d ",ans[i]);
}
puts("");*/
for(int i=1;i<=n;++i) writesp(ans[i]^ss[i]^a[i]);
cerr<<clock()<<endl;
return 0;
}
1log维护突变
#include <stdio.h>
#include <vector>
#define MN 600000
#define MQ 400000
const int B = 21;
using std::vector;
struct Opt{
int o,l,r,x;
}q[MQ+5];
int n,Q,a[MN+5];
vector<int> t[MN+5];
bool ch[1<<(B+1)],*f[B];
int mask[B];
void proc1(int& i,int& lx,int o,int l,int r,int x){
if(o==0){
if(i==l||i==r+1) lx ^= x;
}else{
int cp = 1;
for(int j=0;j<30;j++){
if(i==l) lx ^= x&(1<<j);
if(i==r+1) lx ^= (x+r-l+1)&(1<<j);
if(j>=B&&l+cp<=r+1){
if(i==l+cp) lx ^= 1<<j;
}
if((x&(1<<j))==0) cp += (1<<j);
}
}
}
void proc2(int& i,int& lx,int o,int l,int r,int x){
if(o==1){
int cp = 1;
for(int j=0;j<B;j++){
if(i==l||i==r+1) f[j][(l+cp)&((1<<j)-1)] ^= 1;
if((x&(1<<j))==0) cp += (1<<j);
}
}
}
int main(){
for(int i=0;i<B;i++){
f[i] = &ch[1<<i];
mask[i] = (1<<i)-1;
}
scanf("%d%d",&n,&Q);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int i=1;i<=Q;i++){
scanf("%d%d%d%d",&q[i].o,&q[i].l,&q[i].r,&q[i].x);
t[q[i].l].push_back(i);
t[q[i].r+1].push_back(i);
if(q[i].o==1){
int cp = 1;
for(int j=0;j<B;j++){
if((q[i].x&(1<<j))==0) cp += (1<<j);
}
for(int j=B;j<30;j++){
if(q[i].l+cp<q[i].r+1){
t[q[i].l+cp].push_back(i);
break;
}
if((q[i].x&(1<<j))==0) break;
}
}
}
for(int i=1,x=0;i<=n;i++){
for(int id:t[i]){
proc1(i,x,q[id].o,q[id].l,q[id].r,q[id].x);
}
for(int j=0;j<B;j++)
if(f[j][i&mask[j]])
x ^= 1<<j;
a[i] ^= x;
for(int id:t[i]){
proc2(i,x,q[id].o,q[id].l,q[id].r,q[id].x);
}
}
for(int i=1;i<=n;i++)
printf("%d%c",a[i]," \n"[i==n]);
}
std
#include <bits/stdc++.h>
using namespace std;
const int L = 1 << 9, mask = L - 1;
int main(){
freopen("i.in","r",stdin);
freopen("is.out","w",stdout);
cin.tie(nullptr);
ios::sync_with_stdio(false);
int N, Q;
cin >> N >> Q;
vector<int> A(N);
for(int& a : A) cin >> a;
vector<bitset<L>> lo(N + 1);
vector<int> hi(N + 1);
while(Q--){
int t, l, r, x;
cin >> t >> l >> r >> x;
l--;
if(t == 0){
hi[l] ^= x;
hi[r] ^= x;
}
else{
lo[l][x - l & mask] = !lo[l][x - l & mask];
lo[r][x - l & mask] = !lo[r][x - l & mask];
int X = x & ~mask;
hi[l] ^= X;
for(int i = l - (x & mask) + L; i < r; i += L){
hi[i] ^= X;
hi[i] ^= X += L;
}
hi[r] ^= X;
}
}
for(int i = 0; i < N; i++){
lo[i + 1] ^= lo[i];
hi[i + 1] ^= hi[i];
A[i] ^= hi[i];
for(int j = 0; j < L; j++) if(lo[i][j]) A[i] ^= i + j & mask;
cout << A[i] << " \n"[i + 1 == N];
}
cerr<<clock()<<endl;
}
J. Counting Triangles
【题意】
一幅 n n n个点的完全图,每条边要么是白色要么是黑色,问有多少个同色三角形。
n ≤ 8000 n\leq 8000 n≤8000
【思路】
一个性质是,每个三角形要么三边同色,要么两边同色另一边异色,对于后者,三角形恰有两个异色角,而前者没有异色角。
因此异色角数除以2即为不符合条件的三角形个数,总数减去即可。
【参考代码】
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mac = 8e3 + 10;
bool color[mac][mac];//0为白色,1为黑色
namespace GenHelper {
unsigned z1, z2, z3, z4, b, u;
unsigned get() {
b = ((z1 << 6)^z1) >> 13;
z1 = ((z1 & 4294967294U) << 18)^b;
b = ((z2 << 2)^z2) >> 27;
z2 = ((z2 & 4294967288U) << 2)^b;
b = ((z3 << 13)^z3) >> 21;
z3 = ((z3 & 4294967280U) << 7)^b;
b = ((z4 << 3)^z4) >> 12;
z4 = ((z4 & 4294967168U) << 13)^b;
return (z1 ^ z2 ^ z3 ^ z4);
}
bool read() {
while (!u) u = get();
bool res = u & 1;
u >>= 1; return res;
}
void srand(int x) {
z1 = x;
z2 = (~x) ^ 0x233333333U;
z3 = x ^ 0x1234598766U;
z4 = (~x) + 51;
u = 0;
}
}
using namespace GenHelper;
ll pw(int x) {
return 1LL * x * x;
}
int main() {
int n, seed;
cin >> n >> seed;
srand(seed);
for (int i = 0; i < n; i++)
for (int j = i + 1; j < n; j++)
color[j][i] = color[i][j] = read();
ll ans = 1LL * n * (n - 1) * (n - 2) / 6;
ll res = 0;
for (int i = 0; i < n; i++) {
int black = 0, white = 0;
for (int j = 0; j < n; j++) {
if (i == j) continue;
if (color[i][j]) {
res += white;
black++;
} else {
res += black;
white++;
}
}
}
ans -= res / 2;
printf("%lld\n", ans);
return 0;
}