我懒得分类讨论,直接枚举。
#include
#define Max(a,b) ((a<b)&&(a=b))
#define Min(a,b) ((a>b)&&(a=b))
using namespace std;
inline int read()
{
int x=0,f=1;static char ch;
while(ch=getchar(),ch<48)if(ch==45)f=0;
do x=(x<<1)+(x<<3)+(ch^48);
while(ch=getchar(),ch>=48);
return f?x:-x;
}
int main()
{
int n=read();
while(n%4!=2)n++;
printf("%d",n);
return 0;
}
暴力存边,枚举即可。
#include
#define Max(a,b) ((a<b)&&(a=b))
#define Min(a,b) ((a>b)&&(a=b))
using namespace std;
const int M=105;
inline int read()
{
int x=0,f=1;static char ch;
while(ch=getchar(),ch<48)if(ch==45)f=0;
do x=(x<<1)+(x<<3)+(ch^48);
while(ch=getchar(),ch>=48);
return f?x:-x;
}
int n,m,ans;
bool s[M][M];
int main()
{
n=read(),m=read();
for(int i=1,u,v;i<=m;i++)
{
u=read(),v=read();
s[u][v]=s[v][u]=true;
}
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
for(int k=j+1;k<=n;k++)
if(s[i][j]&&s[j][k]&&s[i][k])
ans++;
printf("%d",ans);
return 0;
}
枚举 j j j,当 a j = j a_j=j aj=j 时满足条件的 i i i 需有 a i = i a_i=i ai=i,否则只能 i = a j i=a_j i=aj,需有 a a j = j a_{a_j}=j aaj=j。
#include
#define Max(a,b) ((a<b)&&(a=b))
#define Min(a,b) ((a>b)&&(a=b))
using namespace std;
const int M=5e5+5;
inline int read()
{
int x=0,f=1;static char ch;
while(ch=getchar(),ch<48)if(ch==45)f=0;
do x=(x<<1)+(x<<3)+(ch^48);
while(ch=getchar(),ch>=48);
return f?x:-x;
}
int n,a[M],sum[M],sn;
long long ans;
int main()
{
n=read();
for(int i=1;i<=n;i++)a[i]=read();
for(int i=1;i<=n;i++)
{
if(a[i]==i)ans+=sn;
else if(a[i]<i&&a[a[i]]==i)ans++;
if(a[i]==i)sn++;
}
printf("%lld",ans);
return 0;
}
枚举选取的个数,直接跑背包即可。
#include
#define Max(a,b) ((a<b)&&(a=b))
#define Min(a,b) ((a>b)&&(a=b))
using namespace std;
typedef long long ll;
const ll P=998244353;
const int M=105;
inline int read()
{
int x=0,f=1;static char ch;
while(ch=getchar(),ch<48)if(ch==45)f=0;
do x=(x<<1)+(x<<3)+(ch^48);
while(ch=getchar(),ch>=48);
return f?x:-x;
}
int n,a[M];
ll dp[M][M],ans;
void ADD(ll &x,ll y){(x+=y)>=P?x-=P:x;}
int main()
{
n=read();
for(int i=1;i<=n;i++)a[i]=read();
ans+=n;
for(int num=2;num<=n;num++)
{
dp[0][0]=1ll;
for(int i=1;i<=n;i++)
for(int j=num-1;j>=0;j--)
for(int k=0;k<num;k++)
ADD(dp[j+1][(k+a[i])%num],dp[j][k]);
ADD(ans,dp[num][0]);
for(int j=0;j<=num;j++)
for(int k=0;k<=num;k++)
dp[j][k]=0;
}
printf("%lld",ans);
return 0;
}
设红色点权值为 0 0 0,蓝色点权值为 1 1 1,那么边 i i i 的边权 W i = x u W_i=x_{u} Wi=xu xor \text{xor} xor x v x_{v} xv。
题目要求 W i = 1 W_i=1 Wi=1 的边数为偶数,那么所有边权值的异或和就为 0 0 0。如果设 D i D_i Di 表示点 i i i 的度数,那么 x i x_i xi 就会被异或上 D i D_i Di 次。
D i D_i Di 为偶数时或 x i = 0 x_i=0 xi=0 时,贡献为 0 0 0,否则贡献为 1 1 1。那么题目就转化为:在奇点中选取偶数个点染成红色且满足数量不超过 K K K 的方案数。这就等于 ∑ i = 0 min ( ⌊ o d d / 2 ⌋ , ⌊ k / 2 ⌋ ) ( o d d i ) × ( n − o d d k − i ) \sum\limits_{i=0}^{\min(\lfloor odd/2\rfloor,\lfloor k/2\rfloor)}{\binom{odd}{i}\times \binom{n-odd}{k-i}} i=0∑min(⌊odd/2⌋,⌊k/2⌋)(iodd)×(k−in−odd)。
#include
#define Max(a,b) ((a<b)&&(a=b))
#define Min(a,b) ((a>b)&&(a=b))
using namespace std;
typedef long long ll;
const ll P=998244353;
const int M=2e5+5;
inline int read()
{
int x=0,f=1;static char ch;
while(ch=getchar(),ch<48)if(ch==45)f=0;
do x=(x<<1)+(x<<3)+(ch^48);
while(ch=getchar(),ch>=48);
return f?x:-x;
}
int n,m,k,deg[M],odd;
ll Fac[M],Inv[M],ans;
ll ksm(ll a,ll b)
{
ll res=1ll;
while(b)
{
if(b&1ll)res=res*a%P;
a=a*a%P,b>>=1ll;
}
return res;
}
void ADD(ll &x,ll y){(x+=y)>=P?x-=P:x;}
ll C(ll n,ll m)
{
if(n<0||m<0||n<m)return 0;
return Fac[n]*Inv[m]%P*Inv[n-m]%P;
}
int main()
{
n=read(),m=read(),k=read();
for(int i=1,u,v;i<=m;i++)
{
u=read(),v=read();
deg[u]++,deg[v]++;
}
Fac[0]=1ll;for(int i=1;i<=n;i++)Fac[i]=Fac[i-1]*i%P;
Inv[n]=ksm(Fac[n],P-2);for(int i=n-1;i>=0;i--)Inv[i]=Inv[i+1]*(i+1)%P;
for(int i=1;i<=n;i++)if(deg[i]&1)odd++;
for(int i=0;i<=odd&&i<=k;i+=2)
ADD(ans,C(odd,i)*C(n-odd,k-i)%P);
printf("%lld",ans);
return 0;
}
发现所有操作可以变为先移动,后删除,其中删除掉被移动的数字不算入操作次数。
我们先考虑不使用移动操作。按数字从小到大贪心,如果能把当前序列中最小数字前的数字删光,那么就删光,然后对该数字后的序列如法炮制,否则选择次大数字重复上述步骤,依次推进。我们可以用单调队列维护这个序列。
然后考虑使用移动操作。注意到移动操作不会改变被移动的段的先后顺序,因此我们可以选择一段小于等于 k k k 的后缀序列移动到最前面,用和上一种情况相同的策略解决问题。显然这个后缀序列的开头一定是 min i = n − k + 1 n { p i } \min\limits_{i=n-k+1}^n{\{p_i\}} i=n−k+1minn{pi}。
注意到,当操作不满 k k k 次时,我们可以将操作完的序列从后往前删,来使其字典序更小。
#include
#define Max(a,b) ((a<b)&&(a=b))
#define Min(a,b) ((a>b)&&(a=b))
using namespace std;
const int M=2e5+5;
inline int read()
{
int x=0,f=1;static char ch;
while(ch=getchar(),ch<48)if(ch==45)f=0;
do x=(x<<1)+(x<<3)+(ch^48);
while(ch=getchar(),ch>=48);
return f?x:-x;
}
int n,k,mov[M],fir;
bool mark[M];
deque<int> p;
vector<int> ans;
vector<int> solve()
{
vector<int> res;int t=k;
for(int x:p)
{
while(!res.empty()&&x<res.back())
{
if(mark[res.back()])res.pop_back();
else if(t)t--,res.pop_back();
else break;
}
res.push_back(x);
}
while(t)t--,res.pop_back();
return res;
}
int main()
{
n=read(),k=read();
for(int i=1,x;i<=n;i++)
{
x=read(),mov[x]=n-i+1;
p.push_back(x);
}
ans=solve();
if(k)
{
for(int i=1;i<=n;i++)
if(mov[i]<=k){fir=i;break;}
while(p.front()!=fir)
{
mark[p.back()]=true;
p.push_front(p.back());
p.pop_back();k--;
}
ans=min(ans,solve());
}
for(int x:ans)printf("%d ",x);
return 0;
}
最理想的状态,就是该删的都删了,而栈为空或自顶往下单调不降。
设计状态 d p ( i , j , m i , m x ) dp(i,j,mi,mx) dp(i,j,mi,mx) 表示处理完区间 [ i , j ] [i,j] [i,j] 后得到的且元素大小在 [ m i , m x ] [mi,mx] [mi,mx] 且单调不降的最长序列长度。接下来考虑转移:
那么答案即为 d p ( 1 , n , 1 , 50 ) dp(1,n,1,50) dp(1,n,1,50)。
#include
#define Max(a,b) ((a<b)&&(a=b))
#define Min(a,b) ((a>b)&&(a=b))
using namespace std;
const int M=55;
inline int read()
{
int x=0,f=1;static char ch;
while(ch=getchar(),ch<48)if(ch==45)f=0;
do x=(x<<1)+(x<<3)+(ch^48);
while(ch=getchar(),ch>=48);
return f?x:-x;
}
int n,a[M],dp[M][M][M][M];
int main()
{
n=read();
for(int i=1;i<=n;i++)a[i]=read();
for(int i=1;i<=n;i++)
for(int mi=1;mi<=a[i];mi++)
for(int mx=a[i];mx<=50;mx++)
dp[i][i][mi][mx]=1;
for(int len=2;len<=n;len++)
for(int i=1,j=i+len-1;j<=n;i++,j++)
for(int mi=1;mi<=50;mi++)
for(int mx=mi;mx<=50;mx++)
{
Max(dp[i][j][mi][mx],dp[i+1][j][mi][mx]);
if(mi<=a[i]&&a[i]<=mx)
for(int k=i;k<=j;k++)
Max(dp[i][j][mi][mx],dp[i+1][k][mi][a[i]]+dp[k+1][j][a[i]][mx]+1);
}
printf("%d",dp[1][n][1][50]);
return 0;
}
这题即 清华集训2017 某位歌姬的故事 ,直接参照原题题解即可。(说白了就是我又菜又懒)