A.移除石子
模拟题。从左上角开始处理,讨论几种情况即可。
复杂度 O ( T n log n ) O(Tn\log n) O(Tnlogn)。
#include<bits/stdc++.h>
#define ll long long
#define pb push_back
#define fi first
#define se second
using namespace std;
int T,n,lsh[3005],X[3005],Y[3005],cnt;
deque<int>v[3005];
int get(int x){return lower_bound(lsh+1,lsh+1+cnt,x)-lsh;}
void print(pair<int,int>x,pair<int,int>y){
pair<int,int>le=make_pair(max(x.fi,y.fi),max(x.se,y.se));int d=max(abs(x.fi-y.fi),abs(x.se-y.se));
cout<<le.fi-d<<' '<<le.se-d<<' '<<le.fi<<' '<<le.se<<"\n";
}
int main(){
freopen("stone.in","r",stdin);
freopen("stone.out","w",stdout);
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>T;
while(T--){
cin>>n,cnt=0;for(int i=1;i<=n;i++)cin>>X[i]>>Y[i],lsh[++cnt]=X[i];
sort(lsh+1,lsh+1+cnt),cnt=unique(lsh+1,lsh+1+cnt)-lsh-1;
for(int i=1;i<=cnt;i++)v[i].clear();for(int i=1;i<=n;i++)v[get(X[i])].push_back(Y[i]);
cout<<"Yes"<<"\n";
pair<int,int>fuck=make_pair(0x3f3f3f3f,0x3f3f3f3f);
for(int i=1;i<=cnt;i++){
sort(v[i].begin(),v[i].end());
while(v[i].size()>1){
int x=v[i][0];v[i].pop_front();
int y=v[i][0];v[i].pop_front();
if(y<fuck.se){
int d=y-x;
cout<<lsh[i]-d<<' '<<x<<' '<<lsh[i]<<' '<<y<<"\n";
}
else if(y>fuck.se){
print(fuck,make_pair(lsh[i],x)),fuck=make_pair(0x3f3f3f3f,0x3f3f3f3f),v[i].push_front(y);
}
else {
if(lsh[i]-fuck.fi>y-x)print(make_pair(lsh[i],x),make_pair(lsh[i],y));
else if(lsh[i]-fuck.fi<y-x)print(fuck,make_pair(lsh[i],y)),v[i].push_front(x);
else {
cout<<fuck.fi<<".5"<<' '<<x<<' '<<lsh[i]<<".5"<<' '<<y<<"\n";
}
}
}
if(v[i].size()){
if(fuck.fi==0x3f3f3f3f)fuck=make_pair(lsh[i],v[i][0]);
else {
print(fuck,make_pair(lsh[i],v[i][0])),fuck=make_pair(0x3f3f3f3f,0x3f3f3f3f);
}
}
}
}
}
B.抓内鬼
这题没想到抽屉原理纯属脑抽
首先把最短路图建出来,如果 1 → n 1\to n 1→n直接相连的话直接把 { 1 , n } \{1,n\} {1,n}放到同一组即可。
否则我们假设不存在长度为 3 3 3的最短路。设直接与 1 1 1相连的点的集合为 S S S,与 n n n相连的点的集合为 T T T,显然 S ∩ T = ∅ S\cap T=\empty S∩T=∅,大概是这个样子:
显然只需把
S
S
S或者
T
T
T全部分到同一组即可。而根据抽屉原理这是显然成立的。
对于长度为 3 3 3的最短路,由于 1 , n 1,n 1,n分在了不同组,所以中间这个点总会与 1 1 1, n n n中的一个分在同一组。这样从 S S S到 T T T的任何路径都存在一条边满足两个端点被分在了同一组。
时间复杂度 O ( n log n ) O(n\log n) O(nlogn)。
#include<bits/stdc++.h>
#define ll long long
#define pb push_back
#define fi first
#define se second
using namespace std;
int n,m,K,vis[200005],vis2[200005],a[200005],dis[200005],res[200005];
vector<int>g[200005],vec;
priority_queue<pair<int,int>>q;
struct node{
int u,v;
}e[200005];
int main(){
freopen("catch.in","r",stdin);
freopen("catch.out","w",stdout);
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>n>>m>>K,memset(res,-1,sizeof res);
int l=K,r=n-K;
if(K==0){for(int i=1;i<=n;i++)cout<<"P";return 0;}
if(K==n){for(int i=1;i<=n;i++)cout<<"U";return 0;}
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=m;i++){
int u,v;cin>>u>>v,g[u].pb(v),g[v].pb(u),e[i]={u,v};
}
memset(dis,0x3f,sizeof dis),dis[1]=0,q.push(make_pair(0,1));
while(q.size()){
int u=q.top().se;q.pop();
if(vis[u])continue;vis[u]=1;
for(auto v:g[u]){
if(dis[u]+a[v]<dis[v])dis[v]=dis[u]+a[v],q.push(make_pair(-dis[v],v));
}
}for(auto u:g[n]){
if(dis[u]+a[n]==dis[n]){
vis2[u]=1,vec.pb(u);
}
}vec.pb(n);
if(vis2[1]){
if(l>=2)res[1]=res[n]=0,l-=2;
else if(r>=2)res[1]=res[n]=1,r-=2;
else {
cout<<"impossible";
return 0;
}
}
else {
if(l>=vec.size()){
for(auto x:vec)res[x]=0,l--;
res[1]=1,r--;
}
else if(r>=vec.size()){
for(auto x:vec)res[x]=1,r--;
res[1]=0,l--;
}
else if(l>=n-vec.size()){
for(int i=1;i<n;i++){
if(!vis2[i])res[i]=0,l--;
}res[n]=1,r--;
}
else {
for(int i=1;i<n;i++){
if(!vis2[i])res[i]=1,r--;
}res[n]=0,l--;
}
}
for(int i=1;i<=n;i++){
if(res[i]==-1){
if(l)res[i]=0,l--;else res[i]=1,r--;
}cout<<(res[i]?"P":"U");
}
}
C.异或序列
水题,但是我没看出来
考虑最朴素的状态定义 ,设 d p i dp_i dpi表示以 i i i结尾的合法序列数目。那么 d p i = ∑ j < i d p j − ∑ j < i , p ( i ) = p ( j ) d p i ⊕ j dp_{i}=\sum_{j<i}dp_j-\sum_{j<i,p(i)=p(j)}dp_{i\oplus j} dpi=∑j<idpj−∑j<i,p(i)=p(j)dpi⊕j 。其中 p ( i ) p(i) p(i)表示二进制下最高位的 1 1 1。
不难看出后面的式子可以前缀和优化,时间复杂度 O ( n log n ) O(n\log n) O(nlogn)。
#include<bits/stdc++.h>
#define ll long long
#define pb push_back
#define fi first
#define se second
using namespace std;
int n,mod;
ll res,dp[1000005],sum[1000005];
int main(){
freopen("xor.in","r",stdin);
freopen("xor.out","w",stdout);
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>n>>mod,dp[1]=sum[1]=res=1;
for(int i=2;i<=n;i++){
dp[i]=sum[i-1]+1;
int j=29;while(!(i>>j&1))j--;
for(int k=j-1;k>=0;k--){
if(i>>k&1){
dp[i]=(dp[i]-sum[(1<<(k+1))-1]+sum[(1<<k)-1])%mod;
}
}sum[i]=(sum[i-1]+dp[i])%mod;
}cout<<(sum[n]+mod)%mod;
}
D.数圈圈
分治好题。之前没遇到过这个想法。
考虑对矩阵分治,每次选择长的一边割开,然后计算跨过 mid \text{mid} mid的圈的数目。
枚举
u
u
u,
v
v
v,只需将左右方案数相乘。
记 l u l_u lu表示向左延伸的最大长度, d i , u d_{i,u} di,u表示第 i i i列向下延伸的最大长度。
要求 ∑ i = max ( l u , l v ) m i d [ d i , u ≥ v ] \sum_{i=\max(l_u,l_v)}^{mid}[d_{i,u}\ge v] ∑i=max(lu,lv)mid[di,u≥v]。不妨假设 l u > l v l_u>l_v lu>lv(后者再做一遍即可),原式变成了 ∑ i = l u m i d [ d i , u ≥ v ] \sum_{i=l_u}^{mid}[d_{i,u}\ge v] ∑i=lumid[di,u≥v],只有 v v v一个变量,用桶维护即可。
单层复杂度 O ( x y + x 2 ) = O ( x y ) O(xy+x^2)=O(xy) O(xy+x2)=O(xy)( x ≤ y x\le y x≤y),即矩形面积。每一层复杂度 O ( n m ) O(nm) O(nm),总复杂度 O ( n m ( log n + log m ) ) O(nm(\log n+\log m)) O(nm(logn+logm))。
#include<bits/stdc++.h>
#define ll long long
#define pb push_back
#define fi first
#define se second
using namespace std;
int n,m;
int up[2005][2005],down[2005][2005],le[2005][2005],ri[2005][2005],siz[2005][2005],siz2[2005][2005],tong[2005];
char s[2005][2005];
ll solve(int X,int X2,int Y,int Y2){
if(X==X2||Y==Y2)return 0;
ll res=0;
if(X2-X>Y2-Y){
int mid=X+X2>>1;
res+=solve(X,mid,Y,Y2)+solve(mid+1,X2,Y,Y2);
for(int i=Y;i<=Y2;i++)for(int j=Y;j<=Y2;j++)siz[i][j]=siz2[i][j]=0;
for(int i=Y;i<=Y2;i++){
int l=max(X,up[mid][i]),tot=0;
for(int j=i+1;j<=Y2;j++)tong[j]=0;
for(int j=l;j<=mid;j++)tong[min(Y2,ri[j][i])]++;
for(int j=Y2;j>i;j--){
tot+=tong[j];if(max(X,up[mid][j])<=l)siz[i][j]+=tot;
}
}
for(int i=Y2;i>=Y;i--){
int l=max(X,up[mid][i]),tot=0;
for(int j=i-1;j>=Y;j--)tong[j]=0;
for(int j=l;j<=mid;j++)tong[max(Y,le[j][i])]++;
for(int j=Y;j<i;j++){
tot+=tong[j];if(max(X,up[mid][j])<l)siz[j][i]+=tot;
}
}
for(int i=Y;i<=Y2;i++){
int l=min(X2,down[mid+1][i]),tot=0;
for(int j=i+1;j<=Y2;j++)tong[j]=0;
for(int j=mid+1;j<=l;j++)tong[min(Y2,ri[j][i])]++;
for(int j=Y2;j>i;j--){
tot+=tong[j];if(min(X2,down[mid+1][j])>=l)siz2[i][j]+=tot;
}
}
for(int i=Y2;i>=Y;i--){
int l=min(X2,down[mid+1][i]),tot=0;
for(int j=i-1;j>=Y;j--)tong[j]=0;
for(int j=mid+1;j<=l;j++)tong[max(Y,le[j][i])]++;
for(int j=Y;j<i;j++){
tot+=tong[j];if(min(X2,down[mid+1][j])>l)siz2[j][i]+=tot;
}
}
for(int i=Y;i<=Y2;i++)for(int j=i+1;j<=Y2;j++)if(s[mid][i]==s[mid+1][i]&&s[mid][j]==s[mid+1][j])res+=(ll)siz[i][j]*siz2[i][j];
}
else {
int mid=Y+Y2>>1;
res+=solve(X,X2,Y,mid)+solve(X,X2,mid+1,Y2);
for(int i=X;i<=X2;i++)for(int j=X;j<=X2;j++)siz[i][j]=siz2[i][j]=0;
for(int i=X;i<=X2;i++){
int l=max(Y,le[i][mid]),tot=0;
for(int j=i+1;j<=X2;j++)tong[j]=0;
for(int j=l;j<=mid;j++)tong[min(X2,down[i][j])]++;
for(int j=X2;j>i;j--){
tot+=tong[j];if(max(Y,le[j][mid])<=l)siz[i][j]+=tot;
}
}
for(int i=X2;i>=X;i--){
int l=max(Y,le[i][mid]),tot=0;
for(int j=i-1;j>=X;j--)tong[j]=0;
for(int j=l;j<=mid;j++)tong[max(X,up[i][j])]++;
for(int j=X;j<i;j++){
tot+=tong[j];if(max(Y,le[j][mid])<l)siz[j][i]+=tot;
}
}
for(int i=X;i<=X2;i++){
int l=min(Y2,ri[i][mid+1]),tot=0;
for(int j=i+1;j<=X2;j++)tong[j]=0;
for(int j=mid+1;j<=l;j++)tong[min(X2,down[i][j])]++;
for(int j=X2;j>i;j--){
tot+=tong[j];if(min(Y2,ri[j][mid+1])>=l)siz2[i][j]+=tot;
}
}
for(int i=X2;i>=X;i--){
int l=min(Y2,ri[i][mid+1]),tot=0;
for(int j=i-1;j>=X;j--)tong[j]=0;
for(int j=mid+1;j<=l;j++)tong[max(X,up[i][j])]++;
for(int j=X;j<i;j++){
tot+=tong[j];if(min(Y2,ri[j][mid+1])>l)siz2[j][i]+=tot;
}
}
for(int i=X;i<=X2;i++)for(int j=i+1;j<=X2;j++)if(s[i][mid]==s[i][mid+1]&&s[j][mid]==s[j][mid+1])res+=(ll)siz[i][j]*siz2[i][j];
}
return res;
}
int main(){
freopen("circle.in","r",stdin);
freopen("circle.out","w",stdout);
cin>>n>>m;for(int i=1;i<=n;i++)scanf("%s",s[i]+1);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
up[i][j]=(s[i-1][j]==s[i][j])?up[i-1][j]:i;
le[i][j]=(s[i][j-1]==s[i][j])?le[i][j-1]:j;
}
}
for(int i=n;i>=1;i--){
for(int j=m;j>=1;j--){
down[i][j]=(s[i+1][j]==s[i][j])?down[i+1][j]:i;
ri[i][j]=(s[i][j+1]==s[i][j])?ri[i][j+1]:j;
}
}
cout<<solve(1,n,1,m);
}