感谢敦哥敦哥友好签到题大放送,A题数目最多的一场
∑ i = 1 n ∑ j = 1 n 2 a i a j \sum_{i=1}^n\sum_{j=1}^n2^{a_ia_j} ∑i=1n∑j=1n2aiaj这个式子看上去很像卷积,如果是卷积的话直接NTT就做完了,所以我们想办法康康能不能化成卷积形式:
2 a i a j = 2 ( a i + a j ) 2 − a i 2 − a j 2 = 2 − a i 2 ∗ 2 − a j 2 ∗ 2 ( a i + a j ) 2 2^{a_ia_j}=\sqrt2^{(a_i+a_j)^2-a_i^2-a_j^2}=\sqrt2^{-a_i^2}*\sqrt2^{-a_j^2}*\sqrt2^{(a_i+a_j)^2} 2aiaj=2(ai+aj)2−ai2−aj2=2−ai2∗2−aj2∗2(ai+aj)2
这样把就把 a i a_i ai对应的 2 − a i 2 \sqrt2^{-a_i^2} 2−ai2和 a j a_j aj对应的 2 − a j 2 \sqrt2^{-a_j^2} 2−aj2这两个的乘积与 ( a i + a j ) (a_i+a_j) (ai+aj)对应的 2 ( a i + a j ) 2 \sqrt2^{(a_i+a_j)^2} 2(ai+aj)2对应起来,相加的值相同的归为一类,这样就转换成卷积的形式了
代码如下:
#include
#define ll long long
#define lowbit(x) ((x)&(-(x)))
using namespace std;
#define LL long long int
using namespace std;
const ll hi = 116195171;
const int maxn = 1<<20,maxm = 100005,INF = 1000000000;
const int G = 3,P = (119 << 23) + 1;
const ll mod = (119<<23)+1;
int n,m,L,R[maxn];
int A[maxn],B[maxn];
ll qm(ll a, ll b){ll res = 1; while(b){if(b&1) res = res*a%mod; a = a*a%mod; b >>= 1; } return res; }
ll qpow(ll a,ll b){
ll ans = 1;
for (; b; b >>= 1,a = 1ll * a * a % P)
if (b & 1) ans = 1ll * ans * a % P;
return ans;
}
void NTT(int* a,int f){
for (int i = 0; i < n; i++) if (i < R[i]) swap(a[i],a[R[i]]);
for (int i = 1; i < n; i <<= 1){
int gn = qpow(G,(P - 1) / (i << 1));
for (int j = 0; j < n; j += (i << 1)){
int g = 1;
for (int k = 0; k < i; k++,g = 1ll * g * gn % P){
int x = a[j + k],y = 1ll * g * a[j + k + i] % P;
a[j + k] = (x + y) % P; a[j + k + i] = (x - y + P) % P;
}
}
}
if (f == 1) return;
int nv = qpow(n,P - 2); reverse(a + 1,a + n);
for (int i = 0; i < n; i++) a[i] = 1ll * a[i] * nv % P;
}
int a[maxm];
int main()
{
scanf("%d", &n);
ll invh = qpow(hi, mod-2);
int len = 0;
for(int i = 0; i < n; ++i) {
scanf("%d", &a[i]); len = max(len , a[i]);
ll t = qm(invh, (ll)a[i]*a[i]);
A[a[i]] += t;
A[a[i]] %= P;
}
len = 2*len+1; for (n = 1; n <= len; n <<= 1) L++;
for (int i = 0; i < n; i++) R[i] = (R[i >> 1] >> 1) | ((i & 1) << (L - 1));
NTT(A, 1);
for (int i = 0; i < n; i++) A[i] = 1ll * A[i] * A[i] % P;
NTT(A,-1);
ll ans = 0;
for(int i = 0; i < len; ++i){
ans = (ans + A[i]*qm(hi, (ll)i*i))%mod;
}
cout<<ans<<endl;
}
/*
3
100000 5 1000
*/
模拟就完事了
#include
using namespace std;
char s[1010];
int mxkill(int n, int a, int b, int c, int d){
int res=0;
for (int i=1;i<=n;++i){
if (s[i]=='1'){
if (c){
c--; res++; continue;
}
if (d){
d--; c++; continue;
}
if (a){
a--; res++; continue;
}
if (b){
b--; a++; continue;
}
}else{
if (d){
d--; c++; continue;
}
if (c){
continue;
}
if (b){
b--; a++; continue;
}
}
}
return res;
}
int mikill(int n, int a, int b, int c, int d){
int res=0;
for (int i=1;i<=n;++i){
if (s[i]=='1'){
if (d){
d--; c++; continue;
}
if (c){
c--; res++; continue;
}
if (b){
b--; a++; continue;
}
if (a){
a--; res++; continue;
}
}else{
if (c){
continue;
}
if (d){
d--; c++; continue;
}
if (a){
continue;
}
if (b){
b--; a++; continue;
}
}
}
return res;
}
void sol(){
int n;
scanf("%d",&n);
int a,b,c,d;
scanf("%d%d%d%d",&a,&b,&c,&d);
scanf("%s",s+1);
printf("%d %d\n",mxkill(n,a,b,c,d),mikill(n,a,b,c,d));
return ;
}
int main(){
int T;
scanf("%d",&T);
while(T--){
sol();
}
return 0;
}
枚举每条边,这条边的两端点连的与该边不同色边数量/4(非整除)为对答案的贡献。
#include
using namespace std;
long long A,B,C,P,D,ans;
vector<pair<pair<int,int>,int> > E;
long long n, sum;
long long du[2][5050];
void sol(){
pair<int,int> edge;
ans=0;
int op,u,v;
int lim=E.size();
for (int i=0;i<lim;++i){
edge=E[i].first; op=E[i].second;
u=edge.first; v=edge.second;
ans+=du[op^1][u]; ans+=du[op^1][v];
}
return ;
}
int main(){
scanf("%lld",&n);
sum = n*(n-1)*(n-2); sum/=3; sum/=2;
scanf("%lld%lld%lld%lld%lld",&A,&B,&C,&P,&D);
long long tmp;
for (int i=1;i<=n;++i){
for (int j=i+1;j<=n;++j){
tmp=(A*(i+j)*(i+j)+B*(j-i)*(j-i)+C)%P;
if (tmp>D){
du[1][i]++; du[1][j]++;
E.push_back(make_pair(make_pair(i,j),1));
}else{
du[0][i]++; du[0][j]++;
E.push_back(make_pair(make_pair(i,j),0));
}
}
}
sol();
cout<<sum-ans/4<<"\n";
return 0;
}
贪心,见代码(队友写的)
#include
#define LL long long
#define inf 0x3f3f3f3f
#define test freopen("in","r",stdin);freopen("out","w",stdout);
#define lson rt<<1
#define rson rt<<1|1
#define PII pair
using namespace std;
const int maxn=105;
int n,a[maxn],ans[maxn];
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
memset(ans,-1,sizeof(ans));
scanf("%d",&n);
for(int i=1;i<=n;++i) scanf("%d",a+i);
int nc=0,p=1;
while(nc!=n)
{
for(int i=n;i;--i)
{
if(a[i]==p) ans[i]=++nc;
}
for(int i=1;i<=n;++i)
{
if(a[i]==-1) {a[i]=p,ans[i]=++nc;break;}
else if(a[i]==p) break;
}
++p;
}
printf("%d",ans[1]);
for(int i=2;i<=n;++i) printf(" %d",ans[i]);
puts("");
}
return 0;
}
最后的结果肯定是把序列分成若干个值相等的块。那么我们可以考虑对于一个长度为n的块,它最后的成为值相等的块需要几步:
如果n是奇数,那么它的最大值两边数目要么都是奇数,要么都是偶数,如果都是偶数,那么除了第一次覆盖了3个位置,剩余每次覆盖两个位置,一共n/2次操作。如果都是奇数,那么以最大值所 位置为中心覆盖一次,然后两边剩余就都是偶数了。操作次数也是n/2.
n为偶数也一样分析出操作次数为n/2
所以用 d p ( i , j ) dp(i,j) dp(i,j)表示弄完前i个数字,操作了j次得到的最大值,然后枚举最后一个块的长度去转移就可,复杂度 O ( n 3 ) O(n^3) O(n3)
#include
#define ll long long
#define lowbit(x) ((x)&(-(x)))
#define mid ((l+r)>>1)
#define lson rt<<1, l, mid
#define rson rt<<1|1, mid+1, r
using namespace std;
const int maxn = 55;
int n;
int a[maxn];
int dp[maxn][maxn];
int main()
{
int T; cin>>T;
while(T--){
scanf("%d", &n);
memset(dp, 0, sizeof dp);
for(int i = 1; i <= n; ++i) scanf("%d", &a[i]);
for(int i = 1; i <= n; ++i){
int mx = a[i];
for(int j = i-1; j >= 0; --j){
int c = (i-j)/2;
for(int k = 0; k +c <= i; ++k){
dp[i][k+c] = max(dp[i][k+c], dp[j][k] + mx*(i-j));
}
mx = max(mx, a[j]);
}
}
for(int k = 1; k <= n; ++k){
printf("%d", dp[n][k]);
if(k != n) printf(" ");
else printf("\n");
}
}
}
当k等于1的时候,答案为N!
否则因为每个点都可以回来,所以一定构成若干个环,并且这些环的长度都是k的约数。那么考虑dp加环
d p ( i , j ) dp(i,j) dp(i,j)表示考虑到第i个约数,加入了j个点的方案数,然后转移。
现在要计算从n个点里面选出t个k个点构成t个环的方案数。首先,n个点选t个k个点的无序集合的方案数为 F ( n , k , t ) = ∑ i = 0 t C ( n − i k , k ) t ! = n ! t ! ∗ ( k ! ) t ∗ ( n − t k ) ! F(n,k,t)=\frac{\sum_{i=0}^tC(n-ik,k)}{t!}=\frac{n!}{t!*(k!)^t*(n-tk)!} F(n,k,t)=t!∑i=0tC(n−ik,k)=t!∗(k!)t∗(n−tk)!n!
每个 k k k点集合可以构成 ( k − 1 ) ! (k-1)! (k−1)!个不同圆环。所以再乘上 ( k − 1 ) t (k-1)^t (k−1)t就是从n个点里面选出t个k个点构成t个环的方案数。
然后根据这个dp就可以了
#include
#define ll long long
#define lowbit(x) ((x)&(-(x)))
#define mid ((l+r)>>1)
#define lson rt<<1, l, mid
#define rson rt<<1|1, mid+1, r
using namespace std;
const int maxn = 55;
const ll mod = 998244353;
ll fac[maxn], ifac[maxn], inv[maxn];
ll qm(ll a, ll b){
ll res = 1;
while(b){
if(b&1) res = res*a%mod;
a = a*a%mod;
b >>= 1;
}return res;
}
ll dp[maxn][maxn];
ll cal(int n, int k, int t){
if(k == 0) return 1;
ll res = fac[n]*ifac[t]%mod*qm(inv[k], t)%mod*ifac[n-t*k]%mod;
return res;
}
int main()
{
int n; ll k;
fac[0] = ifac[0] = 1;
for(int i = 1; i < maxn; ++i){
fac[i] = fac[i-1]*i%mod;
ifac[i] = qm(fac[i], mod-2);
inv[i] = qm(i, mod-2);
}
int T; cin>>T;
while(T--){
scanf("%d%lld", &n, &k);
if(n == 1){
printf("1\n");
}else if(k == 1){
printf("%lld\n", fac[n]);
}else{
//k--;
memset(dp, 0, sizeof dp);
dp[0][0] = 1;
int cur = 0;
for(int i = 1; i <= n; ++i){
if(k%i) continue;
for(int j = 0; j <= n; ++j){
if(!dp[cur][j]) continue;
// cout<<"i:"<
for(int u = 0; j + u <= n; u += i){
//cout<<"u/i:"<
dp[cur+1][j+u] += dp[cur][j]*cal(n-j,i, u/i)%mod;
dp[cur+1][j+u] %= mod;
// cout<<"res:"<
}
}
cur++;
}
printf("%lld\n", dp[cur][n]);
}
}
}
4个快乐签到题就不赘述了~