#include
using namespace std;
const int N=1e5+5;
signed main()
{
ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
int T;
cin>>T;
while(T--)
{
double a,b,c;
cin>>a>>b>>c;
double x0,x1,y0,y1,y2;
cin>>x0>>x1>>y0>>y1>>y2;
double d=b*b-4*a*(c-y0);
if(d<=0)
{
cout<<"No"<<endl;
continue;
}
double xa=(-b-sqrt(b*b-4*a*(c-y0)))/(2*a), xb=(-b+sqrt(b*b-4*a*(c-y0)))/(2*a);
if(xa>xb) swap(xa,xb);
double yb=a*x1*x1+b*x1+c;
if(xa>=x0) // 不能从下往上
{
cout<<"No"<<endl;
}
else if(xb>x0 && xb<x1) // 第一种情况进球
{
cout<<"Yes"<<endl;
}
else if(xb>x1 && xb-x1<x1-x0 && yb<=y2 && yb>y0) // 第二种情况进球,注意yb=y2时也能反弹!!!
{
cout<<"Yes"<<endl;
}
else cout<<"No"<<endl;
}
return 0;
}
#include
using namespace std;
signed main()
{
int T;
cin>>T;
while(T--)
{
long long n;
cin>>n;
if(n==1) cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
return 0;
}
方法一:多条件排序 + + + 多条件二分
方法二:用 m a p map map 嵌套 v e c t o r vector vector 来实现方法一
将 a [ i ] a[i] a[i] 前缀和得 s [ i ] s[i] s[i],设 d = s [ n ] d=s[n] d=s[n]
即求是否存在 s [ i ] s[i] s[i],使得: x = s [ i ] + k d ( k > = 0 ) x=s[i]+kd\ (k>=0) x=s[i]+kd (k>=0)
根据题目要求, a n s = k n + i ans=kn+i ans=kn+i , 取答案最小值
将上式同余 d d d,即: x ≡ s [ i ] m o d d ( d > 0 ) x\equiv s[i]\bmod d\ (d>0) x≡s[i]modd (d>0)
根据 d d d 的值要分三类情况讨论:
d = 0 d=0 d=0
这种情况最简单,直接判断是否存在 s [ i ] = x s[i]=x s[i]=x 即可
d > 0 d>0 d>0
因为 d > 0 d>0 d>0,故满足条件的 s [ i ] < = x s[i]<=x s[i]<=x
对于多个满足条件的 s [ i ] s[i] s[i],要找使 a n s ans ans 最小的那一个
可以想到是最大的,若有多个最大的,那就是出现在最前面的
d < 0 d<0 d<0
这种情况乍一想很复杂,取模都取不动了
可以通过取反的操作,将这种情况变成 d > 0 d>0 d>0 的情况
#include
#define int long long
using namespace std;
const int N=1e5+5;
struct Node
{
int w,id,mo;
bool operator < (const Node &b)
{
return mo<b.mo || mo==b.mo && w<b.w || mo==b.mo && w==b.w && id>b.id;
//先按模数大小排,若相等再按前缀和大小排,若还相等,再按下标大小排(下标下的在后面)
}
}s[N];
int binary(int l,int r,int k,int mo)
{
while(l<=r)
{
int mid=l+r>>1;
if(s[mid].mo>mo || s[mid].mo==mo && s[mid].w>k) // mo相等,返回小于等于x的最后一个
r=mid-1;
else l=mid+1;
}
return r;
}
signed main()
{
ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
int T;
cin>>T;
while(T--)
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
{
int x;
cin>>x;
s[i].w=s[i-1].w+x;
s[i].id=i;
}
int d=s[n].w, fg=0;
if(d==0)
{
for(int i=1;i<=n;i++) s[i].mo=s[i].w;
}
else
{
if(d<0) //取反,注意是所有的都要取反
{
fg=1; d=-d;
for(int i=1;i<=n;i++) s[i].w=-s[i].w;
}
for(int i=1;i<=n;i++) s[i].mo=(s[i].w%d+d)%d;
}
sort(s+1,s+1+n);
//for(int i=1;i<=n;i++) cout<
while(m--)
{
int x;
cin>>x;
if(x==0) { cout<<"0"<<endl; continue; }
if(d==0)
{
int ans=binary(1,n,x,x);
if(s[ans].mo!=x) cout<<"-1"<<endl; //不存在
else cout<<s[ans].id<<endl;
}
else
{
if(fg) x=-x; //输入的x也要取反,整体取反
int mo=(x%d+d)%d;
int p=binary(1,n,x,mo);
if(p==0) { cout<<"-1"<<endl; continue; }
// 当mo=0的时候,特殊情况p会跑到最前面,如果不特判直接GG,s[0].mo=0
if(s[p].mo!=mo) cout<<"-1"<<endl;
else cout<<(x-s[p].w)/d*n+s[p].id<<endl;
}
}
}
return 0;
}
#include
#define int long long
using namespace std;
const int N=1e5+5;
int s[N];
map <int, vector<pair<int,int> > >mp;
unordered_map <int,int> ma;
signed main()
{
int T;
scanf("%lld",&T);
while(T--)
{
mp.clear(); ma.clear();
int n,m;
scanf("%lld%lld",&n,&m);
s[0]=0;
for(int i=1;i<=n;i++)
{
int x;
scanf("%lld",&x);
s[i]=s[i-1]+x;
}
int d=s[n],fg=0;
if(d==0)
{
for(int i=1;i<=n;i++) if(!ma[s[i]]) ma[s[i]]=i;
while(m--)
{
int x;
scanf("%lld",&x);
if(x==0) printf("0\n");
else if(ma[x]) printf("%lld\n",ma[x]);
else printf("-1\n");
}
}
else
{
if(d<0)
{
d=-d; fg=1;
for(int i=1;i<=n;i++) s[i]=-s[i];
}
for(int i=1;i<=n;i++)
{
int mo=(s[i]%d+d)%d;
mp[mo].push_back({-s[i],i}); // s[i]要从大到小排,故先取反,这scz也是惊到我了
}
for(auto u=mp.begin();u!=mp.end();u++)
{
sort(u->second.begin(),u->second.end()); // id从小到大
}
while(m--)
{
int x;
scanf("%lld",&x);
if(x==0) { printf("0\n"); continue; }
if(fg) x=-x;
int mo=(x%d+d)%d;
if(mp[mo].size()==0) printf("-1\n");
else
{
pair <int,int> t={-x,0}; // 根s[i]同步取反
int p=lower_bound(mp[mo].begin(),mp[mo].end(),t)-mp[mo].begin();
// 返回大于等于-x的第一个
if(p==mp[mo].size()) printf("-1\n"); //没找到
else
{
int ans=mp[mo][p].second;
ans+=(x+mp[mo][p].first)/d*n;
printf("%lld\n",ans);
}
}
}
}
}
return 0;
}
线性 D P + DP + DP+ 后缀和
后缀和维护 a a a 的数量,假设 k k k 个 a a a 的任意选择,即: ∑ i = 1 k C k i = 2 k − 1 \sum_{i=1}^{k}C_k^i=2^k-1 ∑i=1kCki=2k−1
维护一定顺序出现的一串序列,典型的线性 D P DP DP
d p [ i ] [ j ] dp[i][j] dp[i][j] 表示前 j j j 位匹配到第 i i i 个前缀的方案数
若当前位为要匹配的前缀: d p [ i ] [ j ] = d p [ i ] [ j − 1 ] + d p [ i − 1 ] [ j − 1 ] dp[i][j]=dp[i][j-1]+dp[i-1][j-1] dp[i][j]=dp[i][j−1]+dp[i−1][j−1]
#include
#define ll long long
#define mod 998244353
using namespace std;
const int N=1e5+5, mo=998244353;
int f[N];
char s[N],ss[15]=" nunhehheh";
int sum[N];
int n,ans;
int dp[12][N];
signed main()
{
f[0] = 1;
for(int i=1;i<=1e5;i++) f[i]=(f[i-1]*2)%mo;
int T;
for(int i=0;i<=1e5;i++) dp[0][i]=1;
cin>>T;
while(T--)
{
scanf("%s", (s + 1));
n=strlen(s+1);
sum[n+1]=0;
for(int i=n;i;i--)
{
sum[i]=sum[i+1];
if(s[i]=='a') sum[i]++;
}
for(int i=1;i<=9;i++)
{
for(int j=1;j<=n;j++)
{
dp[i][j]=dp[i][j-1]; // 继承
if(s[j]==ss[i]) dp[i][j]=(dp[i][j-1]+dp[i-1][j-1])%mod;
}
}
ans=0;
for(int i=1;i<=n;i++)
{
if(s[i]=='h') ans = (ans+1ll*((dp[9][i]-dp[9][i-1]+mod)%mod)*((f[sum[i+1]]-1+mod)%mod)%mod)%mod;
// dp[9][i]-dp[9][i-1]表示当前位(第i位)的方案数
}
cout<<ans<<endl;
}
return 0;
}
构造一个 2 n 2n 2n 的环
两个集合之间连边,且要求从 A A A 集合任意一点到 B B B 集合任意一点(可能会有多条边),要求最长的边 l e n > n len>n len>n
考虑将这个二分图展开来看,会是一个连通块,且一条边上的两端点分别属于两个集合
要使 l e n > n len>n len>n,那这个连通块必然是由多个环组成的(链上肯定存在 l e n < = n len<=n len<=n 的情况)
又要使添的的边最少,所以构造一个 2 n 2n 2n 的环即可
具体构图方法:并查集 + + + 环的性质(每个点的度都为 2 2 2 )
按字典序的话,就从点 1 1 1 ~ n n n 逐一构造即可
#include
using namespace std;
const int N=1e5+5;
int du[N],f[N];
int fd(int k) { return f[k]==k ? k : f[k]=fd(f[k]); }
vector <pair<int,int> > g;
signed main()
{
ios_base::sync_with_stdio(0);
int T;
cin>>T;
while(T--)
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++) du[i]=du[i+n]=0, f[i]=i, f[i+n]=i+n;
g.clear();
for(int i=1;i<=m;i++)
{
int u,v;
cin>>u>>v;
du[u]++;
du[v+n]++;
f[v+n]=u;
}
int st=1;
for(int i=1;i<=n;i++)
{
for(int j=st;j<=n;j++)
{
if(du[j+n]<2)
{
int x=fd(i), y=fd(j+n);
if(x!=y)
{
du[i]++; du[j+n]++;
f[y]=x;
g.push_back({i,j});
}
}
if(du[i]==2) break;
if(du[st+n]==2) st++;
}
}
for(int i=1;i<=n;i++)
{
if(du[i+n]==1)
{
g.push_back({n,i});
break;
}
}
cout<<g.size()<<endl;
for(auto ans : g) cout<<ans.first<<' '<<ans.second<<endl;
}
return 0;
}
并查集+重构树
可以想到 B F S BFS BFS 去遍历,每次从当前权值最大点出发跑一遍 B F S BFS BFS,经过的点的 d e p + + dep++ dep++
然后,便将权值最大点去掉,继续重复上一个操作
但是,这样显然会 T T T(代码见下文)
考虑如何优化?(从比赛开始到结束,也没想出怎么优化)
n n n 遍 B F S BFS BFS ,过程有很多步是重复的
如果这棵树以权值最大点为根,从上到下的权值是递减的,那答案就是每个节点的深度(这就很 n i c e nice nice )
如何重构这棵树?
考虑普遍情况和上述的特殊情况,区别在哪?
区别就在于:普遍情况并非严格递减的
要有一种重构的方法,将这棵树变成特殊情况的,且还不会影响最终答案
重构方法
按点权从小到大枚举结点 u u u ,并将 u u u 作为所有与 u u u 相连的(已经枚举过的)连通块的根(这里要用并查集维护)
从而建立一棵新树,每个结点的深度(根的深度为 1 1 1 )即是答案。
重构方法的正确性,多画几张图就能理解了~
#include
using namespace std;
const int N=1e5+5;
inline int read()
{
int x=0,f=1; char ch=getchar();
while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar();}
while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+ch-'0'; ch=getchar();}
return f*x;
}
struct Node
{
int next,to;
}e[N<<1];
int head[N],tot;
inline void add(int u,int v)
{
e[++tot].next=head[u];
e[tot].to=v;
head[u]=tot;
}
struct graph
{
int w,id;
bool operator <(const graph &a) const { return w<a.w; }
}b[N];
int f[N];
int fd(int k) { return f[k]==k ? k : f[k]=fd(f[k]); }
vector <int> g[N];
int dep[N];
void dfs(int u)
{
for(auto v : g[u])
{
dep[v]=dep[u]+1;
dfs(v);
}
}
int vis[N];
signed main()
{
int T;
scanf("%d",&T);
while(T--)
{
int n;
n=read();
tot=0;
for(int i=1;i<=n;i++)
{
f[i]=i; g[i].clear(); dep[i]=vis[i]=head[i]=0;
}
for(int i=1;i<n;i++)
{
int u,v;
u=read(); v=read();
add(u,v);
add(v,u);
}
for(int i=1;i<=n;i++)
{
b[i].w=read();
b[i].id=i;
}
sort(b+1,b+1+n);
vis[b[1].id]=1;
for(int i=2;i<=n;i++)
{
int u=b[i].id;
for(int i=head[u]; i ;i=e[i].next)
{
int v=e[i].to;
if(vis[v])
{
int y=fd(v); // 找到连通块的祖宗
f[y]=u;
g[u].push_back(y); // 重构树
}
}
vis[u]=1; //要维护的集合里加入该点
}
dep[b[n].id]=1;
dfs(b[n].id);
for(int i=1;i<=n;i++) printf("%d\n",dep[i]);
}
return 0;
}
#include
using namespace std;
const int N=1e5+5;
inline int read()
{
int x=0,f=1; char ch=getchar();
while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar();}
while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+ch-'0'; ch=getchar();}
return f*x;
}
struct Node
{
int next,to;
}e[N<<1];
int head[N],tot;
inline void add(int u,int v)
{
e[++tot].next=head[u];
e[tot].to=v;
head[u]=tot;
}
struct graph
{
int w,id;
bool operator <(const graph &a) const { return w>a.w; }
}b[N];
int dep[N];
int vis[N],vis1[N];
signed main()
{
int T;
scanf("%d",&T);
while(T--)
{
int n;
n=read();
tot=0;
for(int i=1;i<=n;i++)
{
dep[i]=vis[i]=head[i]=0;
}
for(int i=1;i<n;i++)
{
int u,v;
u=read(); v=read();
add(u,v);
add(v,u);
}
for(int i=1;i<=n;i++)
{
b[i].w=read();
b[i].id=i;
}
sort(b+1,b+1+n);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++) vis1[j]=0;
queue <int> q;
q.push(b[i].id);
dep[b[i].id]+=1;
vis[b[i].id]=1; // 去掉当前点
vis1[b[i].id]=1;
while(!q.empty())
{
int u=q.front();
q.pop();
for(int j=head[u]; j ;j=e[j].next)
{
int v=e[j].to;
if(vis[v] || vis1[v]) continue;
dep[v]+=1;
q.push(v);
vis1[v]=1;
}
}
}
for(int i=1;i<=n;i++) printf("%d\n",dep[i]);
}
return 0;
}