给你n个字符串, f ( s , t ) f(s,t) f(s,t)代表最大的 i i i满足s0…i等于tlen-i+1…len。要你求出 ∑ i = 1 n \sum_{i=1}^n ∑i=1n ∑ j = 1 n \sum_{j=1}^n ∑j=1n f ( s i , s j ) f(si,sj) f(si,sj) 2 %(998242353)
先预处理每个字符串的后缀并把它保存到map中,然后,遍历每个字符串,从后往前遍历(因为题目让我们求最大的i),在map中找这个串,更新答案, a n s + = m p [ h a s h S i ] ∗ i ∗ i ans+=mp[hashSi]*i*i ans+=mp[hashSi]∗i∗i然后减去next【i+1】匹配的答案,既 a n s − = m p [ h a s h S i ] ∗ ( n e x t ( I + 1 ) ) ∗ ( n e x t ( I + 1 ) ) ans-=mp[hashSi]*(next(I+1))*(next(I+1)) ans−=mp[hashSi]∗(next(I+1))∗(next(I+1)),为什么呢,假设我们遍历到 i i i,那么 s i si si,在map中又xx个,这xx个字符串与 s i si si完全相等,那么i后面一位失配时,会移动到 n x t [ i + 1 ] nxt[i+1] nxt[i+1]位置继续匹配,但是此时匹配的数是多余的,因为题目要求最长的i。hash可以利用ull自动溢出取模,好像单hash不会冲突。
#include
#define pi pair
#define mk make_pair
#define ull unsigned long long
#define ll long long
using namespace std;
const ull base = 101;
const int maxn = 1e6+10;
string a[maxn];
int id(char x) {
return x-'a'+1;
}
map<ull,int>mp;
ull h[maxn];
int nxt[maxn];
const int mod = 998244353;
void getNext(int tmp)
{
int k = -1,j = 0;
nxt[0] = -1;
while(j < a[tmp].size())
{
if(k == -1 || a[tmp][j] == a[tmp][k])
{
++k,++j;
nxt[j] = k;
}
else k = nxt[k];
}
}
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
cin >> a[i];
ull hash = 0,bs = 1;
for(int j=a[i].size()-1;j>=0;j--)
{
int tmp = id(a[i][j]);
hash = tmp*bs + hash;
bs *= base;
mp[hash]++;
}
}
ll ans = 0;
for(int i=1;i<=n;i++)
{
h[0] = 0;
for(int j=0;j<a[i].size();j++)
{
int tmp = id(a[i][j]);
h[j+1] = h[j]*base + tmp;
}
getNext(i);
for(int j=a[i].size();j>0;j--)
{
int xx = mp[h[j]];
ans += 1ll*xx*j%mod *j %mod;
if(nxt[j] != -1)ans -= 1ll*xx*(nxt[j])%mod*(nxt[j])%mod;
}
}
printf("%lld\n",ans%mod);
}
给你一个长度为1-n的全排列A,根据某种规则变换了k次(k一定是一个大质数),得到了规则p,问这个全排列变换一次的答案是什么。
参考: 首先我们知道Ak(A置换k次)= B,我们就让B作为Ak次的规则,那么,我们根据这个规则可以求出A2k,A3k……。但是题目让我们求A1,我们猜想会不会存在一个x,使得Axk = A1。(A置换 t ∗ x t * x t∗x次等价于A置换1次)。即 k ∗ x = 1 k * x = 1 k∗x=1,这显然不太可能,但是我们知道置换是有循环节的,这个循环节的大小就等于环的大小,就像一个时钟,转了一圈又回到起点一样。我们不妨假设这个循环节(环)的大小为 l e n len len,那么我们可以得到:At*x%len = A1 ,进而可以得到: x ∗ k x*k x∗k%len==1,如果这个等式成立,则说明,我们的猜想是正确的。要想证明这个等式成立就要用到逆元的知识了。我们给这个等式变形一下:
#include
#define pb push_back
using namespace std;
const int maxn = 100005;
int a[maxn],vis[maxn],sum[maxn],n,k;
vector<int>ans;
void dfs(int u)
{
if(vis[u])return ;
vis[u] = 1;
ans.pb(u);
dfs(a[u]);
}
void solve()
{
int tmp;
int len = ans.size();
for(int i=0;i<ans.size();i++)
{
if(1ll*i*k%len == 1)
{
tmp = i;
break;
}
}
for(int i=0;i<ans.size();i++)
{
int v = ans[i];
sum[v] = ans[(i + tmp)%len];
}
}
int main()
{
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)scanf("%d",a+i);
for(int i=1;i<=n;i++)
{
if(!vis[i])
{
ans.clear();
dfs(i);
solve();
}
}
for(int i=1;i<=n;i++)printf("%d ",sum[i]);
return 0;
}
给你一个 n ∗ m n*m n∗m的矩阵,aij = l c m ( i , j ) lcm(i,j) lcm(i,j),让你求着个矩阵中所有大小为 k ∗ k k*k k∗k的矩阵最大值的和
我们可以先 n ∗ n n*n n∗n递推一下每个位置的 g c d gcd gcd,然后再求lcm,先预处理出每一行到第i个元素为止(包括第i个元素在内)往前k个元素的最大值,然后竖着求一遍更新一下答案。时间复杂度是o(n*m).
#include
#define ll long long
using namespace std;
const int maxn = 5005;
int a[maxn][maxn],b[maxn][maxn];
int main()
{
int n,m,k;
scanf("%d%d%d",&n,&m,&k);
for(int i=0;i<=n;i++)
for(int j=0;j<=m;j++)
{
if(i == 0 || j == 0)a[i][j] = max(i,j);
else if(i >= j)a[i][j] = a[i-j][j];
else a[i][j] = a[i][j-i];
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
a[i][j] = i*j/a[i][j];
}
deque<int>q;
for(int i=1;i<=n;i++)
{
while(q.size())q.pop_back();
for(int j=1;j<=m;j++)
{
while(q.size() && j-q.front()+1 > k)q.pop_front();
while(q.size() && a[i][q.back()] < a[i][j])q.pop_back();
q.push_back(j);
if(j >= k)b[i][j] = a[i][q.front()];
}
}
ll ans = 0;
for(int j=k;j<=m;j++)
{
while(q.size())q.pop_back();
for(int i=1;i<=n;i++)
{
while(q.size() && i-q.front()+1 > k)q.pop_front();
while(q.size() && b[q.back()][j] < b[i][j])q.pop_back();
q.push_back(i);
if(i >= k)ans += b[q.front()][j];
}
}
printf("%lld\n",ans);
return 0;
}
给你一个A串,给你一个B串,问最多能在A中找到多少个长度等于B串的子串,使得对应位置上,ai > = >= >=bi
首先考虑暴力 n ∗ m n*m n∗m的做法,显然这会超时。那么我们来考虑优化,利用排序+bitset来优化,怎么搞呢?
对于样例来说:如果Bi小于Aj,f[i][j]就等于0,否则就等于1。那么我们可以发现,当存在一个斜线如下图,答案数加一,这可以通过bitset的位运算操作得出答案。但是,如果直接求出这个矩阵,时间复杂度还是 n ∗ m n*m n∗m。
那么我们就可以通过排序,巧妙的转换一下,我们不用矩阵来保存了,我们数组来保存信息,我们从大到小排序,那么如果Aj > Bi,那么Aj肯定也大于Bi后面那些数。所以我们枚举到Bi时f里面的信息对Bi来说肯定也是合法的(因为这些信息都大于比他大的数了,那么肯定也大于他)。这个本质跟上面那个没有什么不同,只不过通过更换枚举的顺序,降低枚举的次数而已。
时间复杂度:o( n ∗ m n*m n∗m / 32)
#include
using namespace std;
const int maxn = 150005;
struct node{
int val,id;
bool operator < (const node & t)const{
return val > t.val;
}
}a[maxn],b[maxn];
bitset<maxn>ans,f;
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++)
{
scanf("%d",&a[i].val);
a[i].id = i;
}
for(int i=0;i<m;i++)
{
scanf("%d",&b[i].val);
b[i].id = i;
}
sort(a,a+n);
sort(b,b+m);
int j = 0;
ans.set();
for(int i=0;i<m;i++)
{
while(j < n && a[j].val >= b[i].val)
f.set(a[j++].id);
ans &= (f>>b[i].id);
}
cout<<ans.count()<<'\n';
return 0;
}
给你一个multiset,最开始时空的,你有三种操作,操作1:往multiset里面插入一个值x。操作2:删除multiset里面的一个值x(如果有多个,只删除其中一个),操作3:查询multiset中是否存在两个值,使得这两个值与x能构成三角形。
参考 感觉这题即使有思路也不能ac,有太多细节需要处理了。构成三角型的条件有三种:
#include
using namespace std;
const int maxn = 2e5+10,N = 1e9+10;
int tr[maxn * 20],ls[maxn * 20],rs[maxn * 20],cnt,rt;
#define mid (l + r)/2
map<int,int>mp;
void up(int &o,int l,int r,int p,int val)
{
if(!o)o = ++cnt;
if(l == r)
{
tr[o] = val;
return ;
}
if(p <= mid)up(ls[o],l,mid,p,val);
else up(rs[o],mid+1,r,p,val);
int ans = N;
if(ls[o])ans = min(ans , tr[ls[o]]);
if(rs[o])ans = min(ans , tr[rs[o]]);
tr[o] = ans;
}
int qu(int o,int l,int r,int L,int R)
{
if(!o || L > R)return N;
if(l >= L && r <= R)return tr[o];
int ans = N;
if(L <= mid)ans = min(ans , qu(ls[o],l,mid,L,R));
if(R > mid)ans = min(ans , qu(rs[o],mid+1,r,L,R));
return ans;
}
void adds(int x)
{
mp[x]++;
if(mp[x] == 1) //之前没有x
{
auto it = mp.lower_bound(x);
++it;
if(it != mp.end() && it->second == 1)up(rt,1,N,it->first,it->first-x); //更改x的后驱:有后驱,而且只有一个数
--it;
if(it != mp.begin()) //更改x :x有前驱
{
--it;
up(rt,1,N,x,x-(it->first));
}
else //x无前驱
{
up(rt,1,N,x,N);
}
}
else up(rt,1,N,x,0); //之前有x
}
void dels(int x)
{
auto it = mp.lower_bound(x);
mp[x]--;
int l = -N;
if(it != mp.begin()) //计算前驱:如果存在
{
l = (--it)->first;
++it;
}
if(mp[x] == 1)up(rt,1,N,x,x-l);
else if(mp[x] == 0)
{
++it;
if(it != mp.end() && it->second == 1)up(rt,1,N,it->first,it->first - l);
up(rt,1,N,x,N);
mp.erase(x);
}
}
int ask(int x)
{
auto it = mp.lower_bound(x/2+1);
if(it == mp.end())return N;
if(it->second > 1)return it->first;
if(it != mp.begin())
{
auto l = it;
if(it->first + (--l)->first > x)return it->first;
}
if((++it)!=mp.end())return it->first;
return N;
}
int main()
{
int n;
scanf("%d",&n);
while(n--)
{
int op,x;
scanf("%d%d",&op,&x);
if(op == 1)adds(x);
else if(op == 2)dels(x);
else
{
if(qu(rt,1,N,ask(x),N) < x)puts("Yes");
else puts("No");
}
}
return 0;
}