A,B,C是zz题,直接贴代码
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<iostream>
using namespace std;
int read(){
char c;int x;while(c=getchar(),c<'0'||c>'9');x=c-'0';
while(c=getchar(),c>='0'&&c<='9') x=x*10+c-'0';return x;
}
int n,m,f[1005],ans;
int main()
{
n=read();m=read();ans=2e9;
for(int i=1;i<=m;i++){
int x=read();f[x]++;
}
for(int i=1;i<=n;i++) ans=min(ans,f[i]);
printf("%d",ans);
return 0;
}
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<iostream>
#define ll long long
using namespace std;
ll read(){
char c;ll x;while(c=getchar(),c<'0'||c>'9');x=c-'0';
while(c=getchar(),c>='0'&&c<='9') x=x*10+c-'0';return x;
}
ll n,k,f[100005],vis[100005],ans,sum[100005],res;
int main()
{
n=read();k=read();
for(ll i=1;i<=n;i++) f[i]=read();
for(ll i=1;i<=n;i++){
vis[i]=read();if(vis[i]) ans+=f[i];
}
for(ll i=1;i<=k;i++) if(!vis[i]) sum[1]+=f[i];res=sum[1];
for(ll i=2;i<=n-k+1;i++){
sum[i]=sum[i-1];
if(!vis[i-1]) sum[i]-=f[i-1];
if(!vis[i+k-1]) sum[i]+=f[i+k-1];
res=max(res,sum[i]);
}
printf("%d",res+ans);
return 0;
}
#include<cstdio>
#include<string>
#include<iostream>
#define ll long long
using namespace std;
int n,res=2e9;
char se[5][106][105],k;
char f[205][205];
int read(){
char c;int x;while(c=getchar(),c<'0'||c>'9');x=c-'0';
while(c=getchar(),c>='0'&&c<='9') x=x*10+c-'0';return x;
}
int gos(int a,int b,int c,int d){
for(int i=1;i<=n;i++)for(int j=1;j<=n;j++){f[i][j]=se[a][i][j];}
for(int i=1;i<=n;i++)for(int j=n+1;j<=n*2;j++){f[i][j]=se[b][i][j-n];}
for(int i=n+1;i<=n*2;i++)for(int j=1;j<=n;j++){f[i][j]=se[c][i-n][j];}
for(int i=n+1;i<=n*2;i++)for(int j=n+1;j<=n*2;j++){f[i][j]=se[d][i-n][j-n];}
int t1=0,t2=0;
for(int i=1;i<=n*2;i++)for(int j=1;j<=n*2;j++){if(f[i][j]-'0'!=(((i-1)*n*2+j+i&1)&1))t1++;else t2++;}
return min(t1,t2);
}
int main()
{
scanf("%d\n",&n);
for(int i=1;i<5;i++){
for(int j=1;j<=n;j++)
scanf("%s",se[i][j]+1);getchar();
}
for(int i=1;i<=4;i++)
for(int j=1;j<=4;j++)
for(int k=1;k<=4;k++)
for(int q=1;q<=4;q++){
if(i!=j&&j!=k&&k!=q&&i!=k&&i!=q&&j!=q){res=min(res,gos(i,j,k,q));}
}
printf("%d",res);
return 0;
}
D题我考试的时候打了个random_shuffle操过去了,但是被hack了,其实这个算法还是挺强的,也贴一下。
random_shuffle的思路还是比较简单,我们每次取五个数,如果这五个数不能被两条直线覆盖,则答案直接输出NO,而当我们进行成千上万遍的random_shuffle后,还不输出NO,那么就有极大地概率是YES,我们直接输出YES即可。防超时打个clock。
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<ctime>
using namespace std;
int read(){
char c;int x=0,y=1;while(c=getchar(),(c<'0'||c>'9')&&c!='-');
if(c=='-') y=-1;else x=c-'0';while(c=getchar(),c>='0'&&c<='9')
x=x*10+c-'0';return x*y;
}
int n,vis[10];
struct node{
int x,y;
}F[100005];
double k(int x,int y){
return (double)(F[x].y-F[y].y)/(F[x].x-F[y].x);
}
int check(){
for(int i=2;i<=5;i++){
vis[1]=vis[i]=1;
for(int j=1;j<=5;j++){
if(vis[j]) continue;
if((double)k(1,j)==(double)k(1,i)) vis[j]=1;
}
int re=0,p1=0,p2=0;
for(int j=1;j<=5;j++){
if(vis[j]) continue;
if(!p1){p1=j;continue;}
if(!p2){p2=j;continue;}
if((double)k(p1,p2)!=(double)k(p1,j)){re=1;break;}
}
if(!re) return 1;
memset(vis,0,sizeof(vis));
}
return 0;
}
int main()
{
n=read();
if(n<=4){
puts("YES");return 0;
}
for(int i=1;i<=n;i++) F[i].x=read(),F[i].y=read();
srand(20180404);
for(int k=1;k<=50000;k++){
if(clock()>=1960){
puts("YES");return 0;
}
random_shuffle(F+1,F+1+n);
if(!check()){
puts("NO");return 0;
}
}
puts("YES");
return 0;
}
D题正解的思路也挺简单的,就是我们取三个不在同一直线上的点,这三个点两两连一条线。可以用反证法证明,如果原图可以用两条线覆盖,则其中一条线必为这三条之一。所以我们枚举三条直线,删去在他们上的点,如果剩下的点在同一直线上,就输YES。若枚举过三条线依旧没有,就输NO。
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<ctime>
using namespace std;
int read(){
char c;int x=0,y=1;while(c=getchar(),(c<'0'||c>'9')&&c!='-');
if(c=='-') y=-1;else x=c-'0';while(c=getchar(),c>='0'&&c<='9')
x=x*10+c-'0';return x*y;
}
struct node{
int x,y;
}F[100005];
double k(int x,int y){
if(F[x].x==F[y].x&&F[x].y<F[y].y) swap(x,y);
return (double)(F[x].y-F[y].y)/(F[x].x-F[y].x);
}
int n,vis[100005];
int check(int x,int y,int z){
memset(vis,0,sizeof(vis));vis[1]=vis[2]=vis[3]=1;
double base=k(x,y),now;int pl=0;
for(int i=4;i<=n;i++)
if(k(x,i)==base) vis[i]=1;
for(int i=4;i<=n;i++) if(!vis[i]){pl=i;break;}vis[pl]=1;
if(!pl) return 1;now=k(z,pl);
for(int i=4;i<=n;i++)
if(!vis[i]&&now==k(z,i)) vis[i]=1;
for(int i=1;i<=n;i++) if(!vis[i]) return 0;
return 1;
}
int main()
{
n=read();srand(20180409);
for(int i=1;i<=n;i++) F[i].x=read(),F[i].y=read();
memset(vis,0,sizeof(vis));
for(int i=3;i<=n;i++)
if(k(1,2)!=k(1,i)){swap(F[3],F[i]);break;}
if(check(1,2,3)){puts("YES");return 0;}
if(check(1,3,2)){puts("YES");return 0;}
if(check(2,3,1)){puts("YES");return 0;}
puts("NO");return 0;
return 0;
}
E题:
E题是一道数据结构题。题意大致是有N个数,第i个数为ai,若对于i,j满足:
i< j,ai>=j,aj>=i,则称i,j为错误的数对,求错误的数对的总和。
这道题可以用主席树写,X_O_R大佬的程序。
#include <cstdio>
#include <cctype>
const int maxn=200005,maxp=3800095;
int N,A[maxn];
long long Ans;
inline void read(int &Res){
char ch=getchar(); bool fl=0;
for (Res=0;!isdigit(ch);ch=getchar()) if (ch=='-') fl^=1;
for (;isdigit(ch);ch=getchar()) Res=(Res<<3)+(Res<<1)+ch-48;
if (fl) Res=(~Res)+1;
}
struct PT{
int Root[maxn],x,y,len,sul;
struct Ad{int l,r,cnt;}T[maxp];
void Build(int &p,int L,int R){
T[++len]=T[p],++T[p=len].cnt;
if (L>=R) return; int mid=(L+R)>>1;
if (x>mid) Build(T[p].r,mid+1,R);
else Build(T[p].l,L,mid);
}
void Query(int u,int v,int L,int R){
if (x<=L&&R<=y){sul+=T[v].cnt-T[u].cnt; return;}
if (L>=R) return; int mid=(L+R)>>1;
if (x<=mid) Query(T[u].l,T[v].l,L,mid);
if (y>mid) Query(T[u].r,T[v].r,mid+1,R);
}
inline void Create(int id,int num){
x=num,Build(Root[id]=Root[id-1],1,N);
}
inline int GetAns(int L,int R,int frm,int to){
sul=0; if (L>R||frm>to) return sul;
x=frm,y=to,Query(Root[L-1],Root[R],1,N);
return sul;
}
}T;
int main(){
read(N);
for (int i=1;i<=N;++i){
read(A[i]);
if (A[i]>N) A[i]=N;
T.Create(i,A[i]);
}
for (int i=1;i<=N;++i)
Ans+=T.GetAns(i+1,A[i],i,N);
printf("%I64d",Ans);
return 0;
}
然而我们可以用更为巧妙的思路来解题,我们可以按照ai从小到大sort一遍,然后我们先将所有的点都插入树状数组里。接着我们就可以枚举i了,对于每个i,我们可以在树状数组里删掉aj< i的点。然后我们又可以查询小于ai的j有多少,也就是树状数组里1到a[i]的前缀和。这样就可以求出答案。由于这样会重复,所以最后ans要除以2。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
ll read(){
char c;ll x;while(c=getchar(),c<'0'||c>'9');x=c-'0';
while(c=getchar(),c>='0'&&c<='9') x=x*10+c-'0';return x;
}
ll n,top,ans,sum[400005],a[400005];
struct node{
ll num,pl;
}F[400005];
ll cmp(node a,node b){
return a.num<b.num;
}
ll lowbit(ll x){return x&-x;}
void add(ll x,ll ad){
while(x<=n+1){
sum[x]+=ad;
x+=lowbit(x);
}
}
ll getnum(ll x){
ll res=0;
while(x){
res+=sum[x];
x-=lowbit(x);
}
return res;
}
int main()
{
n=read();top=1;
for(ll i=1;i<=n;i++){
a[i]=read();a[i]=min(a[i],n+1);F[i].num=a[i];F[i].pl=i;add(i,1);
}
sort(F+1,F+1+n,cmp);
for(ll i=1;i<=n;i++){
while(top<=n&&i>F[top].num) add(F[top].pl,-1),top++;
ans+=getnum(a[i]);
if(a[i]>=i) ans--;
}
printf("%lld",ans>>1);
return 0;
}
F题,听JYZ大佬说是回文自动机,不会。等会了再说
G题
这是一道数论题。首先我们看题目n个数取成k个集合,这肯定和第二类斯特林数有关。
第二类斯特林数描述为:将n个不同的球放入m个无差别的盒子中,要求盒子非空,有几种方案?
关于第二类斯特林数,有递推公式:s(i,j)=s(i-1,j-1)+j×s(i-1,j)
同时第二类斯特林数有展开公式如下图
我们考虑每个点对答案的贡献,首先不管它怎么分,它至少会出现在s(n,k)种不同分类的集合里,所以ans先加上sum*s(n,k)。其中sum为∑ai。
我们考虑剩下的点,剩下的点能构成s(n-1,k)种集合,而我们将点i加入不同的集合里,会获得不同的收益,但应为公n-1个数,所以最后收益也就乘上n-1。所以最终的答案为:
(s(n,k)+(n-1)*s(n-1,k))*sum
这道题由于200000的数据范围,需要用到斯特林数展开公式,同时在求组合数时,乘法逆元也非常的重要。
using namespace std;
ll read(){
char c;ll x;while(c=getchar(),c<'0'||c>'9');x=c-'0';
while(c=getchar(),c>='0'&&c<='9') x=x*10+c-'0';return x;
}
ll pows(ll a,ll b){
ll base=1;
while(b){
if(b&1) base=base*a%md;
a=a*a%md;b/=2;
}
return base;
}
ll n,m,sum,f[200005];
ll c(ll x,ll y){
return f[x]*pows(f[y],md-2)%md*pows(f[x-y],md-2)%md;
}
ll s(ll x,ll y){
ll res=0;
for(ll i=0;i<=y;i++)
res=(res+pows(-1,i)%md*c(y,i)%md*pows(y-i,x)+md)%md;
res=res*pows(f[y],md-2)%md;
return res%md;
}
int main()
{
n=read();m=read();
f[0]=f[1]=1;for(ll i=2;i<=n;i++) f[i]=f[i-1]*i%md;
for(ll i=1;i<=n;i++) sum=(sum+read())%md;
printf("%I64d",(s(n,m)%md+(n-1)*s(n-1,m)%md)%md*sum%md);
return 0;
}