考试总结
DP专题考试#
这几天考了很多场DP啊,属实是考废了,中途因为唐氏错误保龄了一次,其他几次考的也不是很理想,可能跟最近低迷的状态有关吧。
现在开学停课搞竞赛,先把前几天的DP总结一下。
Day1(2024.8.30)#
T1 天平(balance)#
题意
有一个杠杆,有若干个秤砣,重量为
思路
我们考虑
对于当前秤砣
那么就能得到:
再看一眼限制:
直接秒了(为什么时间如此充裕)。
代码
#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define PII pair<int,int>
#define mk(a,b) make_pair(a,b)
using namespace std;
template<typename P>
inline void read(P &x){
P res=0,f=1;
char ch=getchar();
while(ch<'0' || ch>'9'){
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9'){
res=(res<<3)+(res<<1)+(ch^48);
ch=getchar();
}
x=res*f;
}
template<typename PP>
inline void write(PP x){
if(x<0) putchar('-'),x=-x;
if(x>=10) write(x/10);
putchar('0'+x%10);
}
int T=1;
int n,m;
int w[22],s[22],co[22][22];
unordered_map<int,int> dp[22];//前i个力举之和为j
signed main(){
auto solve=[&](){
read(n),read(m);
for(int i=1;i<=n;++i) read(s[i]);
for(int i=1;i<=m;++i) read(w[i]);
for(int i=1;i<=m;++i){
for(int j=1;j<=n;++j){
co[i][j]=s[j]*w[i];
}
}
dp[0][0]=1;
for(int i=1;i<=m;++i){
for(int j=-10000;j<=10000;++j){
for(int k=1;k<=n;++k){
dp[i][j]=dp[i-1][j-co[i][k]]+dp[i][j];
}
}
}
cout<<dp[m][0]<<endl;
};
freopen("balance.in","r",stdin);
freopen("balance.out","w",stdout);
// read(T);
while(T--) solve();
return 0;
}
T2 山峰数(hill)#
题意
山峰数是指数字排列中不存在山谷(先降后升)的数,例如 0,5,13,12321 都是山峰数,101,1110000111 都不是山峰数。
现给出 n 个数,请依次判断它们是否为山峰数,如果不是,输出-1。如果是,求出比它小的数中有多少个山峰数。
思路
简单数位DP,因为要考虑之前是否下降过,所以在转移过程过添加一维
填数字的时候只需要判断一下是否
如果当前是下降的,把
其实数位DP用记忆化搜索好理解多了。
代码
#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define PII pair<int,int>
#define mk(a,b) make_pair(a,b)
using namespace std;
template<typename P>
inline void read(P &x){
P res=0,f=1;
char ch=getchar();
while(ch<'0' || ch>'9'){
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9'){
res=(res<<3)+(res<<1)+(ch^48);
ch=getchar();
}
x=res*f;
}
template<typename PP>
inline void write(PP x){
if(x<0) putchar('-'),x=-x;
if(x>=10) write(x/10);
putchar('0'+x%10);
}
int T=1;
char a[72];
int n;
int num[72];
int f[72][72][2];
int dfs(int now,bool op0,bool lim,int pre,bool flag){
if(!now) return 1;
if(!op0 && !lim && f[now][pre][flag]!=-1) return f[now][pre][flag];
int up=lim?num[now]:9,res=0;
for(int i=0;i<=up;++i){
if(flag && i<=pre) res+=dfs(now-1,(op0 && i==0),(lim && i==up),i,1);
else if(!flag) res+=dfs(now-1,(op0 && i==0),(lim && i==up),i,(i<pre));
}
if(!op0 && !lim) f[now][pre][flag]=res;
return res;
}
signed main(){
memset(f,-1,sizeof(f));
auto solve=[&](){
cin>>(a+1);
n=strlen(a+1);
bool down=0;
for(int i=1;i<=n;++i) num[i]=a[i]-'0';
for(int i=1;i<n;++i){
if(num[i]<num[i+1] && down){
cout<<-1<<endl;
return;
}
if(num[i]>num[i+1]) down=1;
}
reverse(num+1,num+n+1);
cout<<dfs(n,1,1,0,0)-1<<endl;
};
freopen("hill.in","r",stdin);
freopen("hill.out","w",stdout);
read(T);
while(T--) solve();
return 0;
}
T3 粉刷匠 2(draw)#
题意
现在要满足以下条件:
- 有一些指定的
和 ,要求
求方案数,只输出答案后 5 位。
思路
比较有意思的背包。
我们不按照常规思维枚举行列,而是从小到大枚举颜色。若当前枚举到的颜色为
假设有一行现在是 123344556,现在涂 7,显然涂 7 只能涂序列的后缀,比如涂成 123777777,或者 123344577 才能符合条件。所以考虑枚举当前颜色涂到哪个后缀来转移,转移时同样需要使用完全背包的降维思想优化。
对于限制条件,我们需要把不符合限制条件的去掉,什么样的方案符合限制条件呢?对于限制条件
代码
#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define PII pair<int,int>
#define mk(a,b) make_pair(a,b)
using namespace std;
template<typename P>
inline void read(P &x){
P res=0,f=1;
char ch=getchar();
while(ch<'0' || ch>'9'){
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9'){
res=(res<<3)+(res<<1)+(ch^48);
ch=getchar();
}
x=res*f;
}
template<typename PP>
inline void write(PP x){
if(x<0) putchar('-'),x=-x;
if(x>=10) write(x/10);
putchar('0'+x%10);
}
int T=1;
const int mod=100000;
int n,m,tc=1;
int c[4];
int f[16][16][16][16];
bool vis[16][16][16][16];
int r1x[105],r2x[105],r1y[105],r2y[105];
signed main(){
auto solve=[&](){
for(int tt=1;tt<=tc;++tt){
memset(vis,0,sizeof(vis));
read(n),read(m);
for(int i=1;i<=m;++i){
read(r1x[i]),read(r1y[i]),read(r2x[i]),read(r2y[i]);
r1x[i]--,r2x[i]--;
}
for(int i=1;i<=m;++i){
for(c[0]=0;c[0]<=n;++c[0])
for(c[1]=0;c[1]<=n;++c[1])
for(c[2]=0;c[2]<=n;++c[2])
for(c[3]=0;c[3]<=n;++c[3])
if(c[r1x[i]]>=r1y[i]^c[r2x[i]]>=r2y[i])
vis[c[0]][c[1]][c[2]][c[3]]=1;
}
memset(f,0,sizeof(f));
f[0][0][0][0]=1;
for(int col=0;col<=255;++col){
for(int cc=0;cc<=3;++cc)
for(c[0]=0;c[0]<=n;++c[0])
for(c[1]=0;c[1]<=n;++c[1])
for(c[2]=0;c[2]<=n;++c[2])
for(c[3]=0;c[3]<=n;++c[3])
if(c[cc]<n){
int tmp=f[c[0]][c[1]][c[2]][c[3]];
c[cc]++;
f[c[0]][c[1]][c[2]][c[3]]=(f[c[0]][c[1]][c[2]][c[3]]+tmp)%mod;
c[cc]--;
}
for(c[0]=0;c[0]<=n;++c[0])
for(c[1]=0;c[1]<=n;++c[1])
for(c[2]=0;c[2]<=n;++c[2])
for(c[3]=0;c[3]<=n;++c[3])
if(vis[c[0]][c[1]][c[2]][c[3]]) f[c[0]][c[1]][c[2]][c[3]]=0;
}
printf("%05d\n",f[n][n][n][n]);
}
};
// freopen("draw.in","r",stdin);
// freopen("draw.out","w",stdout);
//read(T);
while(T--) solve();
return 0;
}
T4 棋盘(knight)#
题意
有一个
knight 的攻击范围为国际象棋中马的移动范围。
所有 knight 不能互相攻击,请问总共有多少可行的摆法?答案对 1000000007 取模。
思路
状压DP
考虑压当前行,上一行,上两行三个状态。
转移还是正常转移,但是这样你会发现你寄了。
这个时候我们发现可以使用矩阵优化。
咕咕咕
模拟考#
NOIP联测(2024.10.23)#
T1 tree#
题意
思路
构造题,如果注意力十分集中,能够注意到,菊花图带链(蒲公英)是近似于正确的答案,但发现取中间时会错,那可以构造两条链,即两条链的菊花图即可
代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
template<typename P>
void read(P &x){
P res=0,f=1;
char ch=getchar();
while(ch<'0' || ch>'9'){
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9'){
res=(res<<3)+(res<<1)+ch-'0';
ch=getchar();
}
x=res*f;
}
int T=1;
int tp,n,x;
void solve(){
cin>>tp>>n>>x;
if((x<2 || x>n/2+1) && (n!=1 || x!=1)){
cout<<"No"<<'\n';
return;
}
else cout<<"Yes"<<'\n';
if(tp==0 || n==1) return;
int t=n/2+1,tt=t-x+1;
for(int i=1;i<=tt;++i) cout<<i<<' '<<tt+1<<'\n';
for(int i=tt+1;i<n;++i) cout<<i<<' '<<i+1<<'\n';
return;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
cin>>T;
while(T--) solve();
return 0;
}
T2 suffix#
题意
思路
我们发现一个满足条件的区间,最后一个必定与整个字符串最后一个相同,即
这样我们只需要考虑以
图中的每个箭头的终点表示的是初始时
简洁一点:
- 求当前位置的最长后缀能达到的位置
- 对
求 最小值(传递性),这就是以 结尾的最长的能被分成若干个后缀的区间的左端点位置。 - 那么询问就是判断是否
即可。
代码
#include<bits/stdc++.h>
#define endl "\n"
#define int long long
using namespace std;
template<typename P>
void read(P &x){
P res=0,f=1;
char ch=getchar();
while(ch<'0' || ch>'9'){
if(ch=='-') f=-1;
ch=getchar();
}
while(isdigit(ch)){
res=(res<<3)+(res<<1)+ch-'0';
ch=getchar();
}
x=res*f;
}
int T=1;
const int N=1e6+10,mod=1e9+7,base=1e6+7;
const int INF=0x3f3f3f3f3f3f3f3f;
int n,q;
char s[N];
int nxt[N],f[N];
int h[N],be[N];
int query(int l,int r){
return (h[r]-h[l-1]*be[r-l+1]%mod+mod)%mod;
}
struct Stree{
#define lson (rt<<1)
#define rson (rt<<1|1)
int t[N<<2];
void pushup(int rt){
t[rt]=min(t[lson],t[rson]);
}
void build(int rt,int l,int r){
if(l==r) {t[rt]=INF;return;}
int mid=(l+r)>>1;
build(lson,l,mid);
build(rson,mid+1,r);
pushup(rt);
}
void update(int rt,int l,int r,int k,int x){
if(l==r) {t[rt]=x;return;}
int mid=(l+r)>>1;
if(k<=mid) update(lson,l,mid,k,x);
else update(rson,mid+1,r,k,x);
pushup(rt);
}
int query(int rt,int l,int r,int L,int R){
if(L<=l && r<=R) return t[rt];
int mid=(l+r)>>1,res=INF;
if(L<=mid) res=min(res,query(lson,l,mid,L,R));
if(R>mid) res=min(res,query(rson,mid+1,r,L,R));
return res;
}
}Q;
signed main(){
auto solve=[&](){
read(n),read(q);
scanf("%s",s+1);
be[0]=1;
for(int i=1;i<=n;++i){
h[i]=(h[i-1]*base%mod+(s[i]-'a'+1))%mod;
be[i]=be[i-1]*base%mod;
}
Q.build(1,1,n);
char op=s[n];
memset(f,0x3f,sizeof(f));
for(int i=1;i<=n;++i){
if(s[i]==op){
int l=1,r=i,ans=0;
while(l<=r){
int mid=(l+r)>>1;
if(query(i-mid+1,i)==query(n-mid+1,n)) l=mid+1,ans=mid;
else r=mid-1;
}
f[i]=i-ans+1;
Q.update(1,1,n,i,f[i]);
f[i]=Q.query(1,1,n,max(f[i]-1,1ll),i);
Q.update(1,1,n,i,f[i]);
}
}
while(q--){
int l,r;
read(l),read(r);
int id=f[r];
if(id<=l) cout<<"Yes"<<endl;
else cout<<"No"<<endl;
}
return;
};
freopen("suffix.in","r",stdin);
freopen("suffix.out","w",stdout);
// read(T);
while(T--) solve();
return 0;
}
T3 subsequence#
题意
思路
发现对于第二个性质,我们
考虑函数
所以在
其实看不懂没关系,只需要打表观察发现序列
-
序列无
:则顺序对至多为 ,无合法序列。 -
序列有
,不同时存在 :此时记序列长度为 ,则顺序对数为 ,逆序对数为 。联立解得 ,故此时子序列呈 。 -
序列有
,同时存在 ,同理解方程,可知此时子序列呈 。
所以序列的最大长度暂且为
每次碰到一个不是
代码
#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define PII pair<int,int>
#define mk(a,b) make_pair(a,b)
using namespace std;
template<typename P>
inline void read(P &x){
P res=0,f=1;
char ch=getchar();
while(ch<'0' || ch>'9'){
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9'){
res=(res<<3)+(res<<1)+(ch^48);
ch=getchar();
}
x=res*f;
}
template<typename PP>
inline void write(PP x){
if(x<0) putchar('-'),x=-x;
if(x>=10) write(x/10);
putchar('0'+x%10);
}
int T=1;
const int N=1e6+10,mod=998244353;
int n;
int a[N];
struct Stree{
#define lson (rt<<1)
#define rson (rt<<1|1)
int t[N<<2];
void pushup(int rt) {t[rt]=t[lson]+t[rson];t[rt]%=mod;}
void update(int rt,int l,int r,int k,int x){
if(l==r) {t[rt]+=x,t[rt]%=mod;return;}
int mid=(l+r)>>1;
if(k<=mid) update(lson,l,mid,k,x);
else update(rson,mid+1,r,k,x);
pushup(rt);
}
int query(int rt,int l,int r,int L,int R){
if(L<=l && r<=R) return t[rt];
int mid=(l+r)>>1,res=0;
if(L<=mid) res+=query(lson,l,mid,L,R),res%=mod;
if(R>mid) res+=query(rson,mid+1,r,L,R),res%=mod;
return res%mod;
}
}Q1,Q2;
signed main(){
auto solve=[&](){
read(n);
for(int i=1;i<=n;++i) read(a[i]);
int f1=0;
int ans=0,ans2=0;
for(int i=1;i<=n;++i){
if(a[i]==1) {f1++;continue;}
if(f1==0) continue;
Q1.update(1,1,n,a[i],f1);
int tot=Q1.query(1,1,n,a[i]+1,n);
Q2.update(1,1,n,a[i],tot);
if(a[i]==2) ans+=Q2.query(1,1,n,5,n),ans2+=Q2.query(1,1,n,5,n),ans%=mod,ans2%=mod;
else ans+=Q2.query(1,1,n,a[i]+1,n),ans%=mod;
if(a[i]==3) ans+=ans2,ans%=mod;
}
cout<<(ans+n)%mod<<endl;
};
freopen("subsequence.in","r",stdin);
freopen("subsequence.out","w",stdout);
//read(T);
while(T--) solve();
return 0;
}
CSP-S(2024.10.24)#
T1 queue#
题意
思路
太唐了,直接写。
代码
#include<bits/stdc++.h>
#define endl "\n"
#define ll long long
using namespace std;
template<typename P>
void read(P &x){
P res=0,f=1;
char ch=getchar();
while(!isdigit(ch)){
if(ch=='-') f=-1;
ch=getchar();
}
while(isdigit(ch)){
res=(res<<3)+(res<<1)+ch-'0';
ch=getchar();
}
x=f*res;
}
int T=1;
int n;
void solve(){
read(n);
if(n%2==0){
for(int i=n;i>=1;i-=2) printf("%d ",i);
for(int i=1;i<=n;i+=2) printf("%d ",i);
}
else{
for(int i=n;i>=1;i-=2) printf("%d ",i);
for(int i=2;i<=n;i+=2) printf("%d ",i);
}
puts("");
return;
}
signed main(){
freopen("queue.in","r",stdin);
freopen("queue.out","w",stdout);
// read(T);
while(T--) solve();
return 0;
}
T2 cutin#
题意
思路
本来是想做DP的,但是发现一些性质:
- 对于
的队伍,无论怎么走反正都是要走完的,然后我们可以发现,在某个位置停下相当于选了这个位置的 ,如果不停下就是选了 ,那么这一段相当于就是选 和 的较小值即可。 - 对于
的队伍,最多只会停一次(因为已经满足要求了就不用走了),我们考虑在哪儿停下,发现在 停下的花费是 ,后面的求和用前缀和处理即可,求一个最小值就行。
PS:其实DP也能做。
代码
#include<bits/stdc++.h>
#define endl "\n"
#define int long long
using namespace std;
template<typename P>
void read(P &x){
P res=0,f=1;
char ch=getchar();
while(!isdigit(ch)){
if(ch=='-') f=-1;
ch=getchar();
}
while(isdigit(ch)){
res=(res<<3)+(res<<1)+ch-'0';
ch=getchar();
}
x=f*res;
}
int T=1;
const int N=2e5+10;
int n,m;
int a[N],b[N];
int sumb[N],sum[N];
void solve(){
read(n),read(m);
for(int i=1;i<=n;++i) read(a[i]);
for(int i=1;i<=n;++i) read(b[i]),sumb[i]=sumb[i-1]+b[i];
int ans=0;
for(int i=n;i>m;--i){
if(a[i]>b[i]) ans+=b[i];
else ans+=a[i];
}
int sum=0x3f3f3f3f;
for(int i=1;i<=m;++i){
sum=min(sum,a[i]+sumb[m]-sumb[i]);
}
printf("%lld\n",ans+sum);
return;
}
signed main(){
freopen("cutin.in","r",stdin);
freopen("cutin.out","w",stdout);
read(T);
while(T--) solve();
return 0;
}
T3 number#
题意
思路
一眼数位DP,但是写完后发现需要存每个数字,
我们发现一个数取模
那么现在就可以从
代码
#include<bits/stdc++.h>
#define endl "\n"
#define int long long
using namespace std;
template<typename P>
void read(P &x){
P res=0,f=1;
char ch=getchar();
while(!isdigit(ch)){
if(ch=='-') f=-1;
ch=getchar();
}
while(isdigit(ch)){
res=(res<<3)+(res<<1)+ch-'0';
ch=getchar();
}
x=f*res;
}
int T=1;
int num[20];
int Gcd[3005];
int to=0;
int f[20][2601][51];
int gcd(int a,int b){
if(!b) return a;
return gcd(b,a%b);
}
int lc(int x,int y){
return x*y/gcd(x,y);
}
int dfs(int now,int op0,int lim,int sum,int yu){
if(!now) return (yu%sum==0);
if(!lim && !op0 && f[now][yu][Gcd[sum]]!=-1) return f[now][yu][Gcd[sum]];
int up=(lim)?num[now]:9,res=0;
for(int i=0;i<=up;++i){
if(i==0 && op0!=1) continue;
res+=dfs(now-1,(op0 && i==0),(lim && i==up),(i?lc(sum,i):sum),(yu*10+i)%2520);
}
if(!lim && !op0) f[now][yu][Gcd[sum]]=res;
return res;
}
int work(int x){
int len=0;
memset(f,-1,sizeof(f));
while(x){
num[++len]=x%10;
x/=10;
}
return dfs(len,1,1,1,0);
}
void solve(){
int l,r;
read(l),read(r);
for(int i=1;i<=2520;++i){
if(!(2520%i)) Gcd[i]=(++to);
}
cout<<work(r)-work(l-1)<<endl;
return;
}
signed main(){
freopen("number.in","r",stdin);
freopen("number.out","w",stdout);
// read(T);
while(T--) solve();
return 0;
}
NOIP联测(2024.11.20)#
T1 a#
题意
P9827 [ICPC2020 Shanghai R] Sky Garden
思路
大致意思就是求若干个圆心为
发现可以分开讨论走圆弧和走直线的路径。
由于圆弧是等分的,所以一个点要走圆弧的相邻路径可以容易得出,剩下的就是算某个点到其他点走直线所花的的距离。
luogu上的复杂度可以
代码
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
#define ull unsigned long long
#define PII pair<int,int>
#define mk(a,b) make_pair(a,b)
using namespace std;
template<typename P>
inline void read(P &x){
P res=0,f=1;
char ch=getchar();
while(ch<'0' || ch>'9'){
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9'){
res=(res<<3)+(res<<1)+(ch^48);
ch=getchar();
}
x=res*f;
}
template<typename Ty,typename ...Args>
inline void read(Ty &x,Args &...args) {read(x);read(args...);}
int T=1;
const int mod=998244353;
const double pi=acos(-1);
int n,m;
inline void solve(){
read(n,m);
int cnt=0,ans1=0,ans2=0;
for(int i=1;i<=m;++i) if(1.0*pi/m*i>2.0) {cnt=i-1;break;}
int num=(cnt+1)*cnt/2%mod;
for(int i=1;i<=n;++i){
ans1+=i*num%mod*2%mod+(n-i)*2%mod*num%mod*i%mod*2%mod;
ans1%=mod;
}
for(int i=1;i<=n;++i){
ans2=(ans2+(n-i+1)*(n-i)%mod*m%mod+2*m%mod*i%mod+(n-i+1)*(n-i)%mod*cnt%mod*m%mod*2%mod)%mod;
int p=((n+1)*n/2%mod+n*i%mod)%mod;
ans2=(ans2+p*((m-cnt)*2-1)%mod*m%mod)%mod;
}
cout<<(ans1+mod+mod)%mod<<' '<<(ans2+mod+mod)%mod<<endl;
}
signed main(){
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
//read(T);
while(T--) solve();
return 0;
}
剩下的不会。咕咕咕。
NOIP联测(2024.11.25)#
T1 chtholly#
题意
思路
一开始想到缩点,但是发现缩完点后就不能求 LCA,非常复杂。然后因为是长度为 3 的简单环,可以考虑构建圆方树,只有 2 个点的要么不建方点,要么建了方点不标记成有贡献的。
那么现在对于一次询问
,无解,输出 0 ,此时意味着我们需要选择一些更长的路径。发现走一次方点相当于使总路程 ,算出差值 与路径上的方点数 ,那么答案就是 。
代码
#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
#define ull unsigned long long
#define PII pair<int,int>
#define mk(a,b) make_pair(a,b)
using namespace std;
template<typename P>
inline void read(P &x){
P res=0,f=1;
char ch=getchar();
while(ch<'0' || ch>'9'){
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9'){
res=(res<<3)+(res<<1)+(ch^48);
ch=getchar();
}
x=res*f;
}
template<typename Ty,typename ...Args>
inline void read(Ty &x,Args &...args) {read(x);read(args...);}
int T=1;
const int N=2e5+10,mod=998244353,M=4e5+40;
int n,m,q,id,cnt;
vector<int> g[N],ge[M];
int yu[M];
bool flag[M];
ll fac[M];
int st[N],tpp,low[N],dfx[N],tot;
int son[M],siz[M],tp[M],f[M],dep[M];
void add(int u,int v){
ge[u].push_back(v);
ge[v].push_back(u);
}
void tanjan(int u,int fa){
low[u]=dfx[u]=++tot;
st[++tpp]=u;
int son=0;
for(auto to:g[u]){
if(!dfx[to]){
son++;
tanjan(to,u);
low[u]=min(low[u],low[to]);
if(low[to]>=dfx[u]){
++cnt;
int jg=0;
while(st[tpp+1]!=to){
add(st[tpp--],cnt);
jg++;
}
add(u,cnt);
if(jg!=1) flag[cnt]=1;
}
}
else low[u]=min(low[u],dfx[to]);
}
if(fa==0 && son==0) add(u,cnt);
}
void dfs1(int u,int fa){
siz[u]=1;
f[u]=fa;
dep[u]=dep[fa]+1;
yu[u]=yu[fa]+flag[u];
for(auto to:ge[u]){
if(to==fa) continue;
dfs1(to,u);
siz[u]+=siz[to];
if(siz[to]>siz[son[u]]) son[u]=to;
}
}
void dfs2(int u,int t){
tp[u]=t;
if(son[u]) dfs2(son[u],t);
for(auto to:ge[u]){
if(to==f[u] || to==son[u]) continue;
dfs2(to,to);
}
}
int lca(int a,int b){
while(tp[a]!=tp[b]){
if(dep[tp[a]]<dep[tp[b]]) swap(a,b);
a=f[tp[a]];
}
if(dep[a]<dep[b]) swap(a,b);
return b;
}
ll qpow(ll a,ll b){
ll res=1;
while(b){
if(b&1) res=a*res%mod;
a=a*a%mod;
b>>=1;
}
return res;
}
ll inv(int a){
return qpow(a,mod-2)%mod;
}
ll bal(int n,int m){
return fac[n]*inv(fac[m])%mod*inv(fac[n-m])%mod;
}
inline void solve(){
read(id,n,m,q);
fac[0]=1;
for(int i=1;i<=n*2;++i) fac[i]=fac[i-1]*i%mod;
for(int i=1;i<=m;++i){
int u,v;
read(u,v);
g[u].push_back(v);
g[v].push_back(u);
}
cnt=n;
tanjan(1,0);
dfs1(1,1);
dfs2(1,0);
for(int i=1;i<=q;++i){
int u,v,k;
read(u,v,k);
ll lcc=lca(u,v);
ll di,you;
di=(dep[u]+dep[v]-2*dep[lcc])/2,you=yu[u]+yu[v]-2*yu[lcc]+flag[lcc];
if(di>k) cout<<0<<endl;
else cout<<bal(you,k-di)%mod<<endl;
}
}
signed main(){
freopen("chtholly.in","r",stdin);
freopen("chtholly.out","w",stdout);
//read(T);
while(T--) solve();
return 0;
}
T2 hoshino#
题意
思路
一个明显的性质:优先放数量最多的物资。
发现每次移动区间最多会使物质次数
统计答案时从人数最多的二号桶从大到小枚举
容易发现:
细节可以想一下。
代码
#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
#define ull unsigned long long
#define PII pair<int,int>
#define mk(a,b) make_pair(a,b)
using namespace std;
template<typename P>
inline void read(P &x){
P res=0,f=1;
char ch=getchar();
while(ch<'0' || ch>'9'){
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9'){
res=(res<<3)+(res<<1)+(ch^48);
ch=getchar();
}
x=res*f;
}
template<typename Ty,typename ...Args>
inline void read(Ty &x,Args &...args) {read(x);read(args...);}
int T=1;
const int N=3e5+10;
int n,m,idd,siz;
int id[N];
struct node{
int x,id;
bool operator < (const node &a)const{
return x<a.x;
}
}a[N];
int b[N];
ll Ans[N];
struct que{
int l,r,id;
}q[N];
int p[N],tp[N],mx=-0x3f3f3f3f;
bool cmp(que a,que b){
return (id[a.l]==id[b.l])?a.r<b.r:a.l<b.l;
}
void add(int x){
tp[p[x]]--;
p[x]++;
tp[p[x]]++;
mx=max(mx,p[x]);
}
void del(int x){
if(p[x]!=0) tp[p[x]]--;
p[x]--;
tp[p[x]]++;
mx=max(mx,p[x]);
}
inline void solve(){
read(idd,n,m);
siz=sqrt(n);
for(int i=1;i<=n;++i) id[i]=(i-1)/siz+1;
for(int i=1;i<=n;++i) read(a[i].x),a[i].id=i,b[i]=a[i].x;
for(int i=1;i<=m;++i){
int l,r;
read(l,r);
q[i].l=l,q[i].r=r;
q[i].id=i;
}
sort(a+1,a+n+1);
sort(q+1,q+m+1,cmp);
int cnt=0;
for(int i=1;i<=n;i++){
if(a[i].x!=a[i-1].x) cnt++;
b[a[i].id]=cnt;
}
for(int i=1;i<=n;i++) a[i].x=b[i];
int l=1,r=0;
ll ans=0;
for(int i=1;i<=m;++i){
int ql=q[i].l,qr=q[i].r;
while(r<qr) add(a[++r].x);
while(l>ql) add(a[--l].x);
while(l<ql) del(a[l++].x);
while(r>qr) del(a[r--].x);
ll res=0;
ans=0;
for(int j=mx;j>=1;--j){
if(!tp[j]) continue;
ans+=(tp[j]*res+(tp[j]-1)*tp[j]/2)*j;
res+=tp[j];
}
Ans[q[i].id]=ans;
}
for(int i=1;i<=m;++i) cout<<Ans[i]<<endl;
}
signed main(){
freopen("hoshino.in","r",stdin);
freopen("hoshino.out","w",stdout);
//read(T);
while(T--) solve();
return 0;
}
NOIP联测(2024.11.26)#
T1 for ya#
题意
思路
好题!
我们发现
若两个点之间无边,则不可能同时出现在答案中
所以我们考虑每次遍历两个点,若这两个点之间无边,则删掉这两个点。最后一定能找到
考虑证明正确性:假设我们找到的无边的点最坏情况下都是一个在
加之最多存在
代码
#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
#define ull unsigned long long
#define PII pair<int,int>
#define mk(a,b) make_pair(a,b)
using namespace std;
template<typename P>
inline void read(P &x){
P res=0,f=1;
char ch=getchar();
while(ch<'0' || ch>'9'){
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9'){
res=(res<<3)+(res<<1)+(ch^48);
ch=getchar();
}
x=res*f;
}
template<typename Ty,typename ...Args>
inline void read(Ty &x,Args &...args) {read(x);read(args...);}
int T=1;
const int N=3030;
int n,m;
bool a[N][N];
bool vis[N];
inline void solve(){
read(n,m);
for(int i=1;i<=m;++i){
int u,v;
read(u,v);
a[u][v]=a[v][u]=1;
}
for(int i=1;i<=n;++i){
for(int j=i+1;j<=n;++j){
if(!a[i][j] && !vis[i] && !vis[j]) vis[i]=vis[j]=1;
}
}
int sum=0;
for(int i=1;i<=n;++i) if(!vis[i]) sum++;
cout<<sum<<endl;
for(int i=1;i<=n;++i) if(!vis[i]) cout<<i<<' ';
cout<<endl;
}
signed main(){
freopen("ya.in","r",stdin);
freopen("ya.out","w",stdout);
//read(T);
while(T--) solve();
return 0;
}
T2 只因你太美#
咕咕咕
T3 lagtrain#
题意
思路
一个很显然的贪心:对于一个点
那么现在关键问题就是要将一个集合分成两个子集使得两个子集的差最小。
记集合的总和为
考虑用 bitset 优化一下,这样 DP 的复杂度降到了
我们发现我们只需要知道 DP 数组(即 bitset)哪一位是 1 即代表这个位置是能凑出来的,所以只需要枚举 1 的位置。
这里有两个 bitset 的小函数:
a._Find_first()
找到第一个 1 的位置a._Find_next(x)
找到第 x 的位置后的第一个出现的 1 的位置(不含 x 本身)。
那么现在就可以通过这个函数去找 1 的位置。(其实我后面想了一下,好像只需要找到最靠近
for(int i=f._Find_next(ss/2-1);i!=f.size();i=f._Find_next(i)){res=max(res,1ll*i*(ss-i));}
这样就能在 卡过通过此题。
代码
#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
#define ull unsigned long long
#define PII pair<int,int>
#define mk(a,b) make_pair(a,b)
using namespace std;
template<typename P>
inline void read(P &x){
P res=0,f=1;
char ch=getchar();
while(ch<'0' || ch>'9'){
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9'){
res=(res<<3)+(res<<1)+(ch^48);
ch=getchar();
}
x=res*f;
}
template<typename Ty,typename ...Args>
inline void read(Ty &x,Args &...args) {read(x);read(args...);}
int T=1;
const int N=2e5+10,M=5e3+10;
int n;
ll ans=0;
vector<int> g[N];
bitset<N> f;
static bitset<N> ff;
int siz[N];
int son[N];
void dfs(int u){
vector<int> s;
siz[u]=1;
ll ss=0,res=0;
for(auto to:g[u]){
dfs(to);
ss+=siz[to];
siz[u]+=siz[to];
s.push_back(siz[to]);
if(siz[son[u]]<siz[to]) son[u]=to;
}
if(siz[son[u]]>=siz[u]/2){ans+=siz[son[u]]*(ss-siz[son[u]]);return;}
f.reset();
f[0]=1;
for(int x:s) ff=f<<x,f|=ff;
for(int i=f._Find_next(ss/2-1);i!=f.size();i=f._Find_next(i)){res=max(res,1ll*i*(ss-i));}
ans+=res;
return;
}
inline void solve(){
read(n);
for(int i=2;i<=n;++i){
int x;
read(x);
g[x].push_back(i);
}
dfs(1);
cout<<ans<<endl;
}
signed main(){
freopen("train.in","r",stdin);
freopen("train.out","w",stdout);
//read(T);
while(T--) solve();
return 0;
}
NOIP测试(2024.11.28)#
T1 sub#
题意
思路
考场上5min就搞定了,容易发现当
然后做一次差值的答案时
所以最后的答案就是
代码
#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
#define ull unsigned long long
#define PII pair<int,int>
#define mk(a,b) make_pair(a,b)
using namespace std;
template<typename P>
inline void read(P &x){
P res=0,f=1;
char ch=getchar();
while(ch<'0' || ch>'9'){
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9'){
res=(res<<3)+(res<<1)+(ch^48);
ch=getchar();
}
x=res*f;
}
template<typename Ty,typename ...Args>
inline void read(Ty &x,Args &...args) {read(x);read(args...);}
int T=1;
inline void solve(){
int a,b;
read(a,b);
if((a>=0 && b>=0) || (a<=0 && b<=0)) cout<<0<<endl;
else cout<<min(abs(a+b),min(abs(a),abs(b)))<<endl;
}
signed main(){
freopen("sub.in","r",stdin);
freopen("sub.out","w",stdout);
read(T);
while(T--) solve();
return 0;
}
T2 paint#
题意
思路
考场上写的普通DP,理论上能卡过 85pts 但是只有 40pts。
现在我们考虑其特殊性质(只有2种颜色),那么一次操作可以看成反转一个位置的颜色,则有:
- 最左边和最右边的格子是无法反转的。
- 相邻两个格子是无法同时反转的。
嗯大致就是这样,这个
然后考虑多个颜色相同的格子可以缩成一个块是不影响正确性的。
现在考虑多个颜色。
一个性质:
- 当一个节点被染成不同的颜色后,一定存在最优解,该节点所在连通块不会去染其他颜色。
要是染一次又染还不如一次染完。
因此染色区间不交,考虑设置 dp 状态
使用
代码咕咕咕。
北京冬季联训#
Day 1(2024.12.16)#
T1 背包问题(knapsack)#
题意
思路
其实可以先考虑全部放在一号背包里,再挪动一些去其他背包。
转移到 2 号背包的贡献是
后问题转换为在
考虑什么时候交换他们的选择收益不会变少,也就是
移项得
就有对于任意
也就是说存在一个
那么只需要通过优先队列预处理出每个前缀前
话说居然还是道紫题。
代码
#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
#define mk(a,b) make_pair(a,b)
#define PII pair<int,int>
using namespace std;
template<typename P>
void read(P &x){
P res=0,f=1;
char ch=getchar();
while(!isdigit(ch)){
if(ch=='-') f=-1;
ch=getchar();
}
while(isdigit(ch)){
res=(res<<3)+(res<<1)+(ch^48);
ch=getchar();
}
x=res*f;
}
template<typename PP,typename ...Arc>
void read(PP &x,Arc &...y) {read(x),read(y...);}
const int N=1e5+10;
struct node{
int a,b;
};
node p[N];
ll lsum[N],rsum[N];
signed main(){
int x,y,z;
read(x,y,z);
int n=x+y+z;
ll ans=0;
for(int i=1;i<=n;++i){
int a,b,c;
read(a,b,c);
ans+=a;
p[i].a=b-a,p[i].b=c-a;
}
sort(p+1,p+n+1,[](node x,node y){
return x.a-x.b>y.a-y.b;
});
priority_queue<int,vector<int>,greater<int> > q;
for(int i=1;i<=n;++i){
lsum[i]=lsum[i-1]+p[i].a;
q.push(p[i].a);
if(q.size()>y){
lsum[i]-=q.top();
q.pop();
}
}
while(!q.empty()) q.pop();
for(int i=n;i>=1;--i){
rsum[i]=rsum[i+1]+p[i].b;
q.push(p[i].b);
if(q.size()>z){
rsum[i]-=q.top();
q.pop();
}
}
ll maxx=-0x3f3f3f3f3f3f3f3f;
for(int i=y;i<=n-z;++i) maxx=max(maxx,lsum[i]+rsum[i+1]);
cout<<ans+maxx<<endl;
return 0;
}
剩下两道不会了咕咕咕。
Day 2(2024-12-17)#
T1 石子游戏(stone)#
题意
思路
博弈论,考场上写出来了,考虑首先大于 3 的数等价于 3。至于为什么不是 2,因为 B 可以连拿两次,所以如果是 2 的话意味着必定拿完,而 3 就可以剩 1 个。
然后分讨一下,如果只有 1,那么输赢只和 1 的个数与 3 的模数有关。
如果有 1 有 2,则:
-
如果有 1 个 2,则必胜。
-
如果有 2 个 2 且 1 的个数不是 3 的倍数,则必胜
-
其余必输。
如果都有,容易发现当 3 的个数大于 1 之后必输。所以只考虑等于 1 的情况。
同样根据 1 和 2 的个数分讨一下,这里不再赘述。
代码
#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
#define mk(a,b) make_pair(a,b)
#define PII pair<int,int>
using namespace std;
template<typename P>
void read(P &x){
P res=0,f=1;
char ch=getchar();
while(!isdigit(ch)){
if(ch=='-') f=-1;
ch=getchar();
}
while(isdigit(ch)){
res=(res<<3)+(res<<1)+(ch^48);
ch=getchar();
}
x=res*f;
}
template<typename PP,typename ...Arc>
void read(PP &x,Arc &...y) {read(x),read(y...);}
int T=1;
const int N=120;
int n;
int a[N];
void solve(){
read(n);
for(int i=1;i<=n;++i) read(a[i]);
int cnt1,cnt2,cnt3;
cnt1=cnt2=cnt3=0;
for(int i=1;i<=n;++i){
if(a[i]==1) cnt1++;
else if(a[i]==2) cnt2++;
else cnt3++;
}
if(n==1) cout<<"Win"<<endl;
else if(cnt1==0 && n<=2) cout<<"Lose"<<endl;
else if(cnt1 && !cnt2 && !cnt3){
if(cnt1%3==0) cout<<"Lose"<<endl;
else cout<<"Win"<<endl;
}
else if(cnt3==0){
if(((cnt1%3==1 || cnt1%3==2) && cnt2==2) || (cnt2==1)) cout<<"Win"<<endl;
else cout<<"Lose"<<endl;
}
else if(cnt3==1){
if(cnt2==1 && cnt1!=0){
if(cnt1%3==2 || cnt1%3==1)cout<<"Win"<<endl;
else cout<<"Lose"<<endl;
}
else if(cnt2==0 && cnt1!=0) cout<<"Win"<<endl;
else cout<<"Lose"<<endl;
}
else cout<<"Lose"<<endl;
}
signed main(){
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
read(T);
while(T--) solve();
return 0;
}
作者:God_Max_Me
出处:https://www.cnblogs.com/lizihan00787/p/18404773
版权:本作品采用「God_Max_Me-非商业性使用」许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!