签到题
int T;
ll x,y,a,b;
int main() {
qr(T);while(T--) {
qr(x); qr(y); qr(a); qr(b);
if(x>y) swap(x,y);
if(a*2>b) pr2(b*x+(y-x)*a);
else pr2((x+y)*a);
}
return 0;
}
这么简单比赛的时候竟没想出来
简明题意:
给定一个01串 ,求有个子序列正好为 的01串 ,同时满足 的最小正周期最小.
显然,所有数都相同时输出读入的即可.
否则,可以用 个01来输出.(显然一定满足条件)
int T,n;
char s[N];
int main() {
qr(T); while(T--) {
scanf("%s",s+1); n=strlen(s+1);
bool same=1;
for(int i=2;s[i];i++) if(s[i]!=s[1]) {same=0;break;}
if(same) puts(s+1);
else {for(int i=1;i<=2*n;i++) putchar((i&1)+'0'); puts("");}
}
return 0;
}
找循环节即可.
int T,a,b,q,s[N],n;
ll f(ll x) {return 1LL*(x/n)*s[n]+s[x%n];}
int main() {
qr(T); while(T--) {
qr(a); qr(b); qr(q);
n=a*b;
for(int i=1;i<=n;i++) s[i]=((i%a)%b!=(i%b)%a)+s[i-1];
while(q--) {
ll l,r; qr(l); qr(r);
pr1(f(r)-f(l-1));
}
puts("");
}
return 0;
}
贪心.
从后往前选更容易判断是否合法.
我们贪心维护一个|testdata|不严格下降序列,在不得不扩容时扩容即可.
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=2e5+10;
struct edge{int y,next;}e[N<<1]; int len,last[N],cnt[N];
void ins(int x,int y) {e[++len]=(edge){y,last[x]};last[x]=len;cnt[x]++;}
int n,m,a[N],c[N],t,ans;
int main() {
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&t),a[t]++;
for(int i=1;i<=m;i++) scanf("%d",&c[i]);
for(int i=m; i;i--) {//存储大小满足不严格下降
n=0;
for(int j=(1<<17);j>=1;j/=2)
if(cnt[n+j]==c[i]) n+=j;
for(int j=a[i];j--; ins(n,i))
while(cnt[n]==c[i]) n++;
ans=max(ans,n);
}
printf("%d\n",ans+1);
for(int i=0;i<=ans;i++) {
printf("%d",cnt[i]);
for(int k=last[i];k;k=e[k].next)
printf(" %d",e[k].y);
puts("");
}
return 0;
}
容斥原理(或者是第二类斯特林数(还没学,之后要补))
或者二项式反演直接套(本质也是容斥)
题意:
,已知 ,要使得每个格子都能被车攻击且车之间两两能攻击的情况正好为 个.(中间不能有其他的车)求总方案个数.
显然,车要么排满行,要么排满列.
这两种情况显然是可以一一对应的,所以我们仅求一个即可.
考虑每一列都排满,那么有车的行显然有 个.
开始容斥:
设 .
意思: 行任选为 ,显然这样有空行,所以要- …
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=2e5+20,mod=998244353;
ll n,m,q,jc[N],inv[N],ans,f;
ll power(ll a,ll b=mod-2) {
ll c=1;
while(b) {
if(b&1) c=c*a%mod;
a=a*a%mod; b=b>>1;
}
return c;
}
ll C(ll x,ll y){return jc[x]*inv[y]%mod*inv[x-y]%mod;}
int main() {
scanf("%lld%lld",&n,&m);
if(m>=n) puts("0");
else {
jc[0]=1;for(int i=1;i<=n;i++) jc[i]=jc[i-1]*i%mod;
if(!m) return printf("%lld\n",jc[n]),0;
inv[n]=power(jc[n]);for(int i=n;i;i--) inv[i-1]=inv[i]*i%mod;
f=1;q=n-m;
for(int i=q;i>0;i--) {
ans+=f*power(i,n)*C(q,q-i)%mod;
f=-f;
}
ans=(ans%mod+mod)%mod;
printf("%lld\n",ans*2*C(n,m)%mod);
}
return 0;
}
状压DP.
单组数据复杂度 .
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=18,M=1<<15|10;
struct node {
int x,y,tx,ty;
//操作数,末尾数,转移函数
node(int x=M,int y=0,int tx=0,int ty=0):x(x),y(y),tx(tx),ty(ty){};
bool operator <(node b) const {
return x!=b.x?x<b.x:y<b.y;
}
node get(int a,int b,int c,int d) {
return {x+a,b,c,d};
}
}f[N][M],ans[N];
int T,n,m,a[N],sum[M],cnt[M],lg[M],tot;
int main() {
scanf("%d",&T);
for(int i=1;i<M;i++) cnt[i]=cnt[i&(i-1)]+1;
for(int i=0;i<15;i++) lg[1<<i]=i;
while(T--) {
scanf("%d",&n);m=1<<n;
for(int i=0;i<n;i++) scanf("%d",&a[i]);
for(int i=1;i<m;i++) sum[i]=sum[i&(i-1)]+a[lg[i&(-i)]];
for(int i=0;i<=n;i++) for(int j=0;j<m;j++) f[i][j]=node();
f[0][m-1]=node(0);
for(int i=1;i<=n;i++)
for(int j=0;j<m;j++) if(f[i-1][j].x^M) {//上一个状态
f[i][j]=min(f[i][j],f[i-1][j]);
if(j>>(i-1)&1) {
int mask=j^(1<<(i-1));
for(int s=mask; ;s=(s-1)&mask) {
int t=s|(1<<(i-1));//当前选择
if(f[i-1][j].y<sum[t])//上升
f[i][j^t]=min(f[i][j^t],f[i-1][j].get(cnt[s],sum[t],i-1,j));
if(!s) break;
}
}
}
printf("%d\n",f[n][0].x); tot=0;
for(int i=n,j=0;j^(m-1); ) {
int x=f[i][j].tx,y=f[i][j].ty,t=j^y^(1<<x);
for(int k=0;k<n;k++) if(t>>k&1) ans[++tot]=node(k,x);
i=x; j=y;
}
for(int i=1;i<=tot;i++) {
int x=ans[i].x,y=ans[i].y;
printf("%d %d\n",x+1,y+1);
for(int k=i+1;k<=tot;k++)
ans[k].x-=(ans[k].x>x),
ans[k].y-=(ans[k].y>x);
}
}
return 0;
}