====== A. Distance and Axis ====== $n$ 不足 $k$ 先补齐,否则看 $n+k$ 奇偶性。 #include #define ll long long #define fi first #define se second #define mp make_pair #define pb push_back #define pii pair using namespace std; const int N=5e4+10; int a[N]; int main() { int t; scanf("%d",&t); while(t--) { int n,k; scanf("%d%d",&n,&k); if(k>n)printf("%d\n",k-n); else if((n+k)&1)puts("1"); else puts("0"); } return 0; } \\ ====== B. Ternary Sequence ====== 显然除了 $a1b2,a2b1$ 的组合外其他 $c_i$ 为零,于是使得前者尽可能少,后者尽可能多。 #include #define ll long long #define fi first #define se second #define mp make_pair #define pb push_back #define pii pair using namespace std; const int N=5e4+10; int a[N]; int main() { int t; scanf("%d",&t); while(t--) { int a0,a1,a2,b0,b1,b2,res=0; scanf("%d%d%d%d%d%d",&a0,&a1,&a2,&b0,&b1,&b2); int t1=min(a2,b1); res+=t1*2,b1-=t1; res+=-2*max(0,a1-b0-b1); printf("%d\n",res); } return 0; } \\ ====== C. Mere Array ====== 排序判断不在其位的数字是否能被最小值整除即可。 #include #define ll long long #define fi first #define se second #define mp make_pair #define pb push_back #define pii pair using namespace std; const int N=1e5+10; int a[N],b[N]; int main() { int t; scanf("%d",&t); while(t--) { int n,f=1; scanf("%d",&n); for(int i=1;i<=n;i++)scanf("%d",&a[i]),b[i]=a[i]; sort(b+1,b+1+n); for(int i=1;i<=n;i++) if(a[i]!=b[i]&&a[i]%b[1]!=0){f=0;break;} if(f)puts("YES");else puts("NO"); } return 0; } \\ ====== D. Maximum Distributed Tree ====== 给出一棵树,要给每条边赋边权,使得所有边权的乘积为指定值(且 $1$ 要最少),最大化 $\sum\limits_{i=1}^{n-1} \sum\limits_{j=i+1}^n f(i,j)$ 其中 $f(i,j)$ 为 $i,j$ 简单路径长度。 每条边对答案的贡献次数是固定的(一侧的点数乘另一侧的点数),排序之后贪心地将更大的权值赋给使用次数更多的边(注意质因数多于边数时我们把前若干大的质因数乘起来做最大边权)。 #include #define ll long long #define fi first #define se second #define mp make_pair #define pb push_back #define pii pair using namespace std; const int N=1e5+10; const ll mod=1e9+7; int n,m,head[N],cnt,tot; ll p[N],sz[N],tim[N]; struct Node{int nxt,to;}edges[N*2]; void addedge(int u,int v){edges[++cnt]=Node{head[u],v},head[u]=cnt;} void dfs(int u,int f) { sz[u]=1; for(int i=head[u];~i;i=edges[i].nxt) { int v=edges[i].to; if(v!=f)dfs(v,u),sz[u]+=sz[v],tim[++tot]=sz[v]*(n-sz[v]); } } int main() { int t; scanf("%d",&t); while(t--) { memset(head,-1,sizeof(head)),cnt=0,tot=0; scanf("%d",&n); for(int i=1;iy;}); if(m>n-1) { for(int i=2;i<=m-n+2;i++)p[1]=p[1]*p[i]%mod; for(int i=2;iy;}); ll res=0; for(int i=1;i \\ ====== E. Divide Square ====== 二维平面上有一个大正方形,给出若干线段(线段必有一端在正方形边框上,且同一行不会有两条线段),问正方形被分成多少块。 每有一条横跨正方形的线段块数加一,每有一个交点块数加一。交点这个可以线段树搞一搞,又有点类似扫描线,呃一维排序加入一维查询什么的。(不是一个很优美的写法) #include #define ll long long #define fi first #define se second #define mp make_pair #define pb push_back #define pii pair using namespace std; const int N=1e5+10; const int M=1e6; int n,m,num[N*3],tot,lz[N*12],maxn[N*12]; ll res=1; 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; } struct Node1{int y,l,r;}a[N]; struct Node2{int x,l,r;}; vectorb,c; int id(int x){return lower_bound(num+1,num+1+tot,x)-num;} void pushdown(int idx) { int t=lz[idx]; lz[idx]=0,lz[idx<<1]+=t,lz[idx<<1|1]+=t; maxn[idx<<1]+=t,maxn[idx<<1|1]+=t; } void add(int idx,int l,int r,int a,int b) { if(l>=a&&r<=b){lz[idx]++,maxn[idx]++;return;} pushdown(idx); int mid=(l+r)>>1; if(b<=mid)add(idx<<1,l,mid,a,b); else if(a>mid)add(idx<<1|1,mid+1,r,a,b); else add(idx<<1,l,mid,a,b),add(idx<<1|1,mid+1,r,a,b); maxn[idx]=max(maxn[idx<<1],maxn[idx<<1|1]); } int query(int idx,int l,int r,int x) { if(l==r)return maxn[idx]; pushdown(idx); int mid=(l+r)>>1; if(x<=mid)return query(idx<<1,l,mid,x); else return query(idx<<1|1,mid+1,r,x); } int main() { n=read(),m=read(); for(int i=1;i<=n;i++) { a[i].y=read(),a[i].l=read(),a[i].r=read(); num[++tot]=a[i].l,num[++tot]=a[i].r; res+=(a[i].l==0&&a[i].r==M); } sort(a+1,a+1+n,[&](Node1 o1,Node1 o2){return o1.yo2.l;}); for(int i=0,j=n;i0&&a[j].y>=c[i].l)add(1,1,tot,id(a[j].l),id(a[j].r)),j--; int t=query(1,1,tot,id(c[i].x)); res+=t; } printf("%lld\n",res); return 0; } \\ ====== F. Reverse and Swap ====== 应该是有一个 $O(nq)$ 的异或一个值的神秘做法但我懂得还有点模糊。线段树这个还蛮好理解的,交换和翻转操作其实都可以归为对线段树的一些层交换左右子树,记录每层是否需要交换,之后就单点修改区间查询,根据是否交换选择进入左还是右子树即可。 #include #define ll long long #define fi first #define se second #define mp make_pair #define pb push_back #define pii pair using namespace std; const int N=(1<<18)+10; 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; } ll a[N],rev[20],sum[N<<2]; void build(int idx,int l,int r) { if(l==r){sum[idx]=a[l];return;} int mid=(l+r)>>1; build(idx<<1,l,mid),build(idx<<1|1,mid+1,r); sum[idx]=sum[idx<<1]+sum[idx<<1|1]; } void modify(int idx,int k,int l,int r,int pos,int x) { if(l==r){sum[idx]=x;return;} int mid=(l+r)>>1; if(!rev[k]) { if(pos>mid)modify(idx<<1|1,k-1,mid+1,r,pos,x); else modify(idx<<1,k-1,l,mid,pos,x); } else { if(pos>mid)modify(idx<<1,k-1,mid+1,r,pos,x); else modify(idx<<1|1,k-1,l,mid,pos,x); } sum[idx]=sum[idx<<1]+sum[idx<<1|1]; } ll query(int idx,int k,int l,int r,int a,int b) { if(l>b||r=r)return sum[idx]; int mid=(l+r)>>1; if(!rev[k])return query(idx<<1,k-1,l,mid,a,b)+query(idx<<1|1,k-1,mid+1,r,a,b); else return query(idx<<1,k-1,mid+1,r,a,b)+query(idx<<1|1,k-1,l,mid,a,b); } int main() { int n=read(),q=read(); for(int i=1;i<=(1< \\